Text fields

mandatory

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`).

Spec — Text fields

Facet *isual*do Koder Design. Material parity: https://m3.material.io/components/text-fields.

2 variants

Variant Visual Use
*illed* Surface-variant bg + bottom border (accent on focus) Default — denser, more visual weight
*utlined* 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]
  • *ontainer* 56 px height (default), 48 px (compact)
  • *abel* body-large floating (above when focused/filled)
  • *nput text* body-large
  • *elper / error* body-small below
  • *ounter* body-small right-aligned below (when maxLength)
  • *cons* 24 px, leading + trailing optional

R1 — Label behavior

Two label modes:

  1. *loating*(default) — label sits inside container at rest;

    floats up when focused or has value

  2. *lways-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 — Singleline vs multiline

  • 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
Email 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

  • *eading icon* signals input type (mail icon for email, search

    icon for search) — decorative, no interaction

  • *railing icon* actionable (clear X, show/hide password, voice

    input mic, calendar picker open) — <button> element with aria-label

Tap target on trailing icon: 24 px visible, 40 px hit zone.

R8 — Accessibility

  • <label for="id"> connection MANDATORY (or aria-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
  • interaction/states.kmd — hoverfocuserror visuals
  • themes/color-roles.kmd — accent / error / text-muted bindings
  • errors/user-facing-messages.kmd — error message format
  • foundations/ux-writing.kmd — label + helper text style
  • foundations/elements.kmd — Control family

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/text-fields.kmd