Text fields
components specs/components/text-fields.kmd
Text input control — 2 variants (filled, outlined), single-line and multi-line, with label, helper text, error state, leading/trailing icons, character counter. Material parity (`/components/text-fields`).
When this spec applies
Primary triggers
- Implement text input anywhere
All triggers
- Build any text input in a Koder UI
- Pick between filled and outlined variant
- Add validation, helper text, error state to an input
Specification body
Spec — Text fields
Facet Visual do Koder Design. Material parity: https://m3.material.io/components/text-fields.
2 variants
| Variant | Visual | Use |
|---|---|---|
| Filled | Surface-variant bg + bottom border (accent on focus) | Default — denser, more visual weight |
| Outlined | Border on all sides + transparent bg | Forms with grouped fields; cleaner on light bg |
Pick one per surface — don't mix in the same form.
Anatomy
[leading icon?] Label (floating) [trailing icon?]
[counter?]
┌──────────────────────────────────────────────────────┐
│ [icon?] Input value here [icon?] │
└──────────────────────────────────────────────────────┘
Helper text or error message [chars/max]
- Container: 56 px height (default), 48 px (compact)
- Label:
body-largefloating (above when focused/filled) - Input text:
body-large - Helper / error:
body-smallbelow - Counter:
body-smallright-aligned below (whenmaxLength) - Icons: 24 px, leading + trailing optional
R1 — Label behavior
Two label modes:
- Floating (default) — label sits inside container at rest; floats up when focused or has value
- Always-above — label permanently above the container
Floating is Material standard; always-above is gnome/carbon_ibm
preset convention.
R2 — States
Per interaction/states.kmd:
| State | Filled | Outlined |
|---|---|---|
| Empty (rest) | Label inside container | Label inside container |
| Focused | Label floats up + bottom border accent (2 px) | Label floats up + border accent (2 px) |
| Filled (has value) | Label stays floated | Label stays floated |
| Hovered | Bottom border darker | Border text-muted darker |
| Error | Border error + helper text becomes error text | Same |
| Disabled | Opacity 0.38; no interaction | Same |
R3 — Single-line vs multi-line
- Single-line: 56 px height default, scrolls horizontally
- Multi-line: starts at 56 px, grows to a max (configurable; default max 5 lines), then scrolls vertically inside
<input type="text">vs<textarea>semantics in HTML
R4 — Specialized input types
| Type | Modifier |
|---|---|
inputmode="email", autocomplete=email, validation regex | |
| Number | inputmode="numeric", no spinners (CSS hide), pattern validation |
| Phone | inputmode="tel", mask per locale |
| Password | type="password" + show/hide eye icon (trailing) |
| Search | type="search", ⌘K shortcut hint, clear button (trailing X) |
| Date | Pair with date picker (per future date-pickers.kmd) |
R5 — Helper text + error
Below the input container, single line:
Filled state: "Used for sign-in and password reset" (text-muted)
Error state: "Email must include @" (error color)
Filled + counter:"123/200" (text-muted, right)
When error: helper text replaced by error message. Both styled with
body-small role. Error must be associated via aria-describedby
linking input → error element.
Error format per errors/user-facing-messages.kmd: humanized,
specific, no "Invalid" / "Wrong".
R6 — Validation
| Validation type | When |
|---|---|
required | On submit, not while typing |
| Format regex | On blur (after the user finishes typing) |
| Server-side | On submit; debounced check on blur for sensitive fields (email exists) |
| Real-time (length, char restrictions) | While typing — give immediate feedback |
NEVER validate on first keystroke for format-based rules (annoying; "a" → "Invalid email" before user can type more).
R7 — Leading + trailing icons
- Leading icon: signals input type (mail icon for email, search icon for search) — decorative, no interaction
- Trailing icon: actionable (clear X, show/hide password, voice
input mic, calendar picker open) —
<button>element witharia-label
Tap target on trailing icon: 24 px visible, 40 px hit zone.
R8 — Accessibility
<label for="id">connection MANDATORY (oraria-label/aria-labelledby)- Helper text linked via
aria-describedby - Error linked via
aria-describedby+aria-invalid="true"on the input - Required fields:
aria-required="true"(don't rely on*alone) - Autocomplete attribute set for personal data (email, name, address)
- Screen readers announce label → value → state on focus
R9 — Forbidden patterns
- ❌ Input without visible label (placeholder is NOT a label)
- ❌ Placeholder containing format example ("user@example.com") shown only until user types — use helper text instead (or both)
- ❌ Custom validation messages that contradict spec
(
errors/user-facing-messages.kmd) - ❌ Inputs that look like text but aren't focusable (use a real
<button>if action) - ❌ Disabling submit until valid (use clear error reveal on submit instead)
R10 — Per-preset variation
| Preset | Variant default |
|---|---|
material3 | Filled |
gnome | Outlined |
windows_11 | Filled |
windows_95 | 3D bevel border (Outlined visually) |
ios_cupertino | Outlined |
carbon_ibm | Underline only (lighter than Filled bottom-border) |
shadcn | Outlined |
brutalist | Outlined + 3 px thick border |
Cross-link
interaction/states.kmd— hover/focus/error visualsthemes/color-roles.kmd— accent / error / text-muted bindingserrors/user-facing-messages.kmd— error message formatfoundations/ux-writing.kmd— label + helper text stylefoundations/elements.kmd— Control family
References
specs/foundations/elements.kmdspecs/interaction/states.kmdspecs/themes/color-roles.kmdspecs/errors/user-facing-messages.kmdspecs/foundations/ux-writing.kmd