TextField

ui forminput

Formularfeld-Wrapper: TextInput, TextArea und SelectInput mit optionalem Label (via SectionLabel) und Hinweistext. Einheitliche Höhe/Padding über die Field-Tokens; SelectInput hat ein eigenes Chevron statt des System-Pfeils.

Barrierefreiheit

  • Feld ist von einem <label> umschlossen — Klick auf das Label fokussiert das Control.
  • Eigenes Select-Chevron; appearance:none entfernt den System-Pfeil.

Beispiele

Eingaben
<TextInput label="Name" placeholder="Gemeinde" hint="Pflichtfeld" />
<TextArea label="Notiz" />
<SelectInput label="Plan">
  <option>free</option>
  <option>pro</option>
</SelectInput>

Genutzte Tokens · 21

--accent--bg--border--control-height--ease-standard--fg--field-chevron--field-chevron-offset--field-chevron-pad--field-font-size--field-line-height--field-pad-x--field-pad-y--field-radius--focus-ring--font-stage--motion-fast--muted--surface--text-base--text-xs

Quelldateien

components/ui/TextField.tsx tsx
import type {
  InputHTMLAttributes,
  ReactNode,
  SelectHTMLAttributes,
  TextareaHTMLAttributes,
} from 'react';
import { SectionLabel } from './SectionLabel';
import styles from './TextField.module.css';

interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  hint?: string;
}

export function TextInput({ label, hint, className, id, ...rest }: InputProps) {
  return (
    <label className={styles.field}>
      {label && <SectionLabel as="span">{label}</SectionLabel>}
      <input id={id} className={`${styles.input} ${className ?? ''}`} {...rest} />
      {hint && <span className={styles.hint}>{hint}</span>}
    </label>
  );
}

interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
  label?: string;
  hint?: string;
}

export function TextArea({ label, hint, className, ...rest }: TextareaProps) {
  return (
    <label className={styles.field}>
      {label && <SectionLabel as="span">{label}</SectionLabel>}
      <textarea className={`${styles.textarea} ${className ?? ''}`} {...rest} />
      {hint && <span className={styles.hint}>{hint}</span>}
    </label>
  );
}

interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
  label?: string;
  hint?: string;
  children?: ReactNode;
}

export function SelectInput({ label, hint, className, children, ...rest }: SelectProps) {
  return (
    <label className={styles.field}>
      {label && <SectionLabel as="span">{label}</SectionLabel>}
      <select className={`${styles.select} ${className ?? ''}`} {...rest}>
        {children}
      </select>
      {hint && <span className={styles.hint}>{hint}</span>}
    </label>
  );
}
components/ui/TextField.module.css css
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.input,
.textarea,
.select {
  width: 100%;
  border-radius: var(--field-radius);
  border: 1px solid var(--border);
  background: var(--bg);
  font-size: var(--field-font-size);
  line-height: var(--field-line-height);
  font-family: inherit;
  color: var(--fg);
  outline: none;
  box-sizing: border-box;
  transition:
    border-color var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard);
}
.input,
.textarea {
  padding: var(--field-pad-y) var(--field-pad-x);
}
.input {
  height: var(--control-height);
}
.textarea {
  font-family: var(--font-stage);
  font-size: var(--text-base);
  min-height: 80px;
  resize: vertical;
}
/* Native `<select>` ignoriert vertikales Padding nicht so verlässlich wie
 * `<input>` — daher height + line-height:1 ohne Y-Padding, plus eigenes
 * Chevron-Background (appearance:none entfernt das System-Pfeil-Icon). */
.select {
  height: var(--control-height);
  padding: 0 var(--field-chevron-pad) 0 var(--field-pad-x);
  line-height: 1;
  cursor: pointer;
  appearance: none;
  background-image: var(--field-chevron);
  background-repeat: no-repeat;
  background-position: right var(--field-chevron-offset) center;
  background-size: 12px 12px;
}
.select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.input:focus-visible,
.textarea:focus-visible,
.select:focus-visible {
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
  background-color: var(--surface);
}
.hint {
  font-size: var(--text-xs);
  color: var(--muted);
}

Holen

# MCP (Claude Code / Cursor)
get_component({ name: "text-field" })

# Registry direkt
https://www.ekklesi.tools/design/registry/components/text-field.json