Field

Puts a label, a hint and any error around a control.

Examples

Checked when you leave

Lowercase letters, digits and dashes.

An error in place of the hint

Use lowercase letters and dashes.

A short form

Orientation

  • Lowercase and dashes.

    verticaldefault

  • Reply to customers and issue refunds.

    horizontal

  • responsive

States

  • Lowercase and dashes.

    rest

  • Lowercase and dashes.

    focus-visible

  • Use lowercase letters.

    invalid

  • Locked while live.

    disabled

Usage

When to use it
Use
Any control the person fills: Input, Textarea, Combobox, Date picker, Select.
Use
A Switch or Checkbox that needs a sentence of explanation beside it.
Use
A set of fields that submit together, wrapped in Form.
Not for
A search box in a toolbar names itself with aria-label. Use Input.
Not for
A row of filters, not a form. Use Filter bar.
Not for
A message about the whole page or account. Use Alert.

Rules

8 rules
Must
Leave validationMode unset so a field checks on blur, then on change while invalid. Because onChange flags the value as wrong on the first keystroke, before the person has finished typing it.
Must
Wrap fields that submit together in Form with a type="submit" Button, and pass pending to both. Because without Form, Enter in a field submits nothing and a second press sends the request twice.
Must
Pass server rejections to Form errors keyed by field name, not to a toast. Because a toast disappears and is tied to no control, while Form errors mark the field invalid under its own label.
Must
Name a control with FieldLabel inside a Field, or Label htmlFor outside one, never a styled p. Because a p is not a label, so the control has no accessible name and a click on the text does not focus it.
Should
Write an error as what to do, in a sentence with a period.
Should
Mark the few optional fields with optional and mark nothing else.
Never
Style an error as raw red text; use FieldError. Because a loose p is not in the control's aria-describedby, so a screen reader never reads it, and red-600 is not the fail text step.
Should
Keep a FieldDescription to one sentence under 120 characters.

Accessibility

5 checks
FieldLabel is a native label bound to the control; a click on it focuses the control.
FieldDescription and FieldError link by aria-describedby; the control gets aria-invalid.
Tab leaves the control and validates it; Enter in a Form submits, and does nothing while pending.
Form sets aria-busy while pending; FormStatus is a polite status region.
The error is text, so it survives forced colors.

Edge cases

10 edge cases
Long label and error
Both wrap; nothing has a fixed height.
Right to left
Label, hint and error align to the inline start; FieldSeparator centres logically.
Inside a disabled FieldSet
Every Field and the buttons in its controls take the disabled state.
120px wide
Label, control and error stack; responsive stays vertical.
No FieldLabel
The control is unnamed; add the label or aria-label.
Disabled and invalid
Disabled wins; validation is skipped and the error hides.
Server and client error
FieldError dedupes and lists both.
Enter three times while pending
One request is sent; Form drops the rest.
Failed submit
Values stay and the first invalid field takes focus.
Zoom 200%
Label, hint and error wrap and grow.

API

7 props
Field
name, invalid, validate, validationMode, disabled, orientation
FieldLabel
optional
FieldError
errors, match, children
FieldSet, FieldLegend
disabled; variant
Form
pending, errors, onFormSubmit
FormStatus
status: 'ok' | 'fail'
Label, LabelHint
optional, htmlFor

Tokens

11 tokens
--type-label
400 14px/16px var(--font-sans)
--text-1
var(--ink)
--text-disabled
oklch(var(--ink-c) / 0.40)
--type-body-sm
380 13px/18px var(--font-sans)
--text-2
oklch(var(--ink-c) / 0.80)
--fail-text
oklch(51% 0.180 26)
--line-control
oklch(var(--ink-c) / 0.16)
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--accent-wash
var(--surface-selected)
--line-focus
2px
--border-focus
var(--ink)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.