Skip to main content

Learning Track

Filter content by your role to see the most relevant sections

Input & Forms Patterns

Design forms that respect the user: compound field anatomy with named slots, labels over placeholders, validation that announces through the accessibility tree, and errors legible without color — all mapped to the system’s Field, TextField, and Label contracts.

Darian Rosebrook
Staff Design Technologist, Design Systems Architect
Expertise:Design Systems, Forms, Accessibility

Why This Matters

Forms are where users do the hardest work: they type, they remember, they make mistakes under mild anxiety. Every form is a small promise about competence—label clearly, forgive errors, never make the user guess your rules. Break the promise and the cost is direct: abandoned signups, support tickets, wrong data in the database.

Forms are also the pattern where accessibility failures concentrate, because a form is a live conversation with the accessibility tree: labels associate, errors announce, focus moves with intent. The system's answer is structural—the compound Field component owns the conversation once, so every form inherits correct behavior instead of re-attempting it.

Core Concepts: Anatomy, Labels, Validation, Errors

The Compound Field Anatomy

The system's field is not an input; it is a compound of named parts, declared in the contract:

// ui/components/Field/Field.contract.json — anatomy
["root", "error", "help", "label", "header", "control"]

// The parts compose:
//   root — the association boundary (what belongs together)
//   header — optional affordance row (actions, requirements)
//   label — the persistent question being asked
//   control — the input proper (Input, Select, Checkbox…)
//   help — standing guidance, always available
//   error — the corrective response, announced when present

The anatomy is the pattern: each slot has a job, a binding, and an a11y behavior. Composition (rather than a mega-props input) keeps the jobs inspectable—reviewers see the parts; the tree announces them correctly; designers restyle one part without renegotiating the others.

Labels Over Placeholders

A label is a permanent question; a placeholder is hint-text that vanishes exactly when the user needs it— at input—and that screen readers may skip. The rules the system encodes: labels are always visible, always associated (Label htmlFor to the control's id), placeholders demonstrate format (dd/mm), never name the field, and the label survives in error state—"Email is required" reads; a red outline alone does not.

Validation: When and How to Speak

  • Validate on blur, not on keystroke — screaming at a half-typed email teaches nothing except dread. Re-validate on input only after the first error, to signal recovery.
  • Announce through the tree — the error slot renders role="alert" (or wires aria-describedby to the control) so the correction reaches screen readers with the same timing sighted users get.
  • Preserve the user's work — never clear a field on failed validation; the error is in the system's expectation or the message, not necessarily the typing.

Errors Without Color Alone

Error state is feedback.border.error plus icon plus message—the border pairs with the error token from the feedback ramp, and the message text is the actual carrier. Color is the fastest channel for sighted users and an absent one for others: the pattern requires at least two channels (color + text always; icon as the common third). The pair ledger guarantees the error color clears 3:1 in both modes; nothing about the redundancy is decorative.

System Roles: Owning the Conversation

Design Impact

Designers own the question quality: labels that ask plainly, help text that prevents errors, error messages that name the fix ("add an @", not "invalid format"). The anatomy keeps each artifact in its slot.

Engineering Impact

Engineers own the wiring: association (label→control, error→describedby), timing (blur-then-live), and the contract's declared behavior staying true in every state.

Accessibility Impact

The a11y function owns the announcement audit: error timing, recovery signaling, and the two-channel rule— verified in the axe layer and the manual pass.

Design & Code Interplay

The design side of forms is the state matrix: each field drawn in default, focus, filled, error, and disabled, with the error's two channels visible. The comp is the contract's preview—the slots named in the file are the slots named in the JSON.

Applied Example: A Recoverable Signup Form

Ship one form end to end—a three-field signup:

  1. Anatomy first: three Field compounds, each with label, control, help; no placeholders doing a label's job.
  2. Flow order: DOM order equals tab order equals visual order; the submit button last and reachable; Enter submits the form (native form semantics, free).
  3. Validation: blur-validated email format with a recovery message; on submit, focus the first invalid field—the user lands at the problem, not below it.
  4. Errors: border token + message text + icon; per-field messages, never a banner alone; the corrected field announces recovery by dropping the alert.
  5. Verify: axe on the errored state (not just default), a keyboard walk of the submit failure, and one VoiceOver pass of the error announcement.

Constraints & Trade-offs

  • Blur validation vs submit validation: blur catches early, submit is calmer; the system default is blur-then-live because recovery feedback outweighs the interruption, but quiet forms (search) validate on submit only.
  • Compound anatomy vs single-component convenience: composition costs a few lines per field and buys per-slot styling, association, and review; the mega-input is shorter to type and forever to maintain.
  • Inline errors vs error summary: long forms (10+ fields) add a submit-time summary with links; short forms keep it inline. The pattern, not the habit, decides.

Common Pitfalls & Failure Modes

1. Placeholder as label

// Bad: The question disappears on focus
<input placeholder="Email address" />
// Good
<Label htmlFor="e">Email address</Label>
<input id="e" placeholder="[email protected]" />

2. Color-only errors

/* Bad: A border nothing announces */
.inputError { border-color: var(--semantic-color-feedback-border-error); }
/* Good: Border + role=alert message + icon — two channels minimum */

3. Validating every keystroke

Errors at input-time for half-typed values punish the middle of thinking. Blur first; live-validate only the recovery.

4. Clearing on error

Emptying a field to "help" retries destroys work and trust in one move. Never.

5. Disabled submit as validation theater

A permanently disabled submit with no explanation hides the rule; enable it and explain on failure, or show the requirement beside it.

Verification Checklist

Additional Resources

  • Selection patterns — the choice controls that live inside forms (/blueprints/ux-patterns/selection)
  • Feedback patterns — the announcement family errors belong to (/blueprints/ux-patterns/feedback)
  • Component anatomy — the contract slots this pattern composes (/blueprints/component-standards/anatomy)

Related Concepts

Reflection Questions