Skip to main content

Field

A label, control, hint and error laid out together, the same way every time.

In Accessible first

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Accessible first: Label, hint and error in the same place on every question.

  • Use it when: Every form control that needs a label, a hint or an error.
  • Not when: A control that is not part of a form, such as a search box in a toolbar.

Instead:

  • Label: The label on its own.

Variants​

What each variant is for in any design system, then what Accessible first decided and why. Each sample is the live story with that variant set.

verticalorientationUsed in Accessible first
Start Storybook to see it live

What it is for

The label above the control.

✓Most forms: easy to scan from top to bottom.
✕Short settings where the label sits better beside.

In Accessible first

Every question: the label above the field, always visible.

horizontalorientationNot used in Accessible first
Start Storybook to see it live

What it is for

The label beside the control.

✓Settings and short controls such as switches.
✕Narrow screens, or long labels.

In Accessible first

Labels stay above the field, visible while typing and in the same place on any screen.

responsiveorientationNot used in Accessible first
Start Storybook to see it live

What it is for

The label above on narrow screens and beside on wide ones.

✓Forms people use on phones and laptops alike.
✕When one layout is simpler for everyone.

In Accessible first

One layout on every device, so people meet the same form every time.

How Accessible first uses it​

Field label or column header
Use type.label, above the field, always visible
Not A placeholder as the label
Labels must stay visible while typing.
An error message
Use color.status.danger, an icon and a sentence next to the field
Not Color alone
People must know what went wrong and where.

Why it works this way​

Field looks the way it does because of 4 answers about this product. Each card is one answer, in three steps: the answer, what it decided for Field, and what would move if it changed.

What accessibility level do we commit to?people.q2
1The answer
AAA

AAA where it applies; nobody is left out.

2So Field
  • ✓uses vertical: Every question: the label above the field, always visible.
  • –does not use horizontal: Labels stay above the field, visible while typing and in the same place on any screen.
It also shapes Button: danger Button: focus Button: disabled Input group: inline-start Input group: inline-end Input group: block-start Input group: block-end
3If it changed
2
Field decisions would reopen

and 15 answers that follow from it: A ratio, line heights in proportion, Neutrals and one brand ramp, Nothing, and 12 more.

What is the main task?tasks.q1
1The answer
Complete a form

People must finish the form.

2So Field
  • ✓uses vertical: Every question: the label above the field, always visible.
It also shapes Input group: inline-start
3If it changed
1
Field decision would reopen

and 6 answers that follow from it: Completion without errors, A task that fails, One sitting, and 3 more.

How do people recall the interface?human.q2
1The answer
They see it fresh each time

Nobody remembers the interface from last time.

2So Field
  • –does not use responsive: One layout on every device, so people meet the same form every time.
It also shapes Button: secondary Button: ghost Button: disabled Toggle: default
3If it changed
1
Field decision would reopen

and 1 answer that follow from it: Actions and status.

Where do people use it?scenarios.q1
1The answer
Anywhere, on any device

Public services are used on any device, with any assistive technology.

2So Field
  • –does not use responsive: One layout on every device, so people meet the same form every time.
3If it changed
1
Field decision would reopen

No other answer follows from it.

Words​

  • Use common words. Explain any term you must use.
  • One idea per sentence. Put the action first.
  • Never blame the person.
  • Use: Continue, Change, Start, You. Avoid: Invalid, Utilise, Submit, Error code.

The labels components use, from the voice tokens:

  • content.action.commit: Continue
  • content.action.cancel: Go back
  • content.action.destroy: Delete
  • content.action.retry: Try again
  • content.action.empty-action: Start a new application

See Voice and tone for the whole voice.

Accessibility and keyboard​

Test before you ship:

  • The label, hint and error are linked to the control, so they are read with it. undefined

Use it in code​

npx open-ds add field

The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.

Tokens it reads​

Field reads 30 tokens of its own, each an alias of a semantic token. Each row is one part: its own token, the semantic it aliases, and the value it ends at. Change a semantic and every component that reads it follows; override one of Field's tokens to change Field alone.

Changes with theme

Accessible first decides what changes when you switch theme. Here a switch changes colors only, so the marked rows differ between light and dark; everything else holds steady.

PartIts token and the semantic it aliasesValue
Set
Gapcomponent.field.set.gapspace.gap.sectionvar(--field-set-gap)space.48:3rem
Gap 2component.field.set.gap-2component-spacing-map.x3var(--field-set-gap-2)space.20:1.25rem
Legend
Weightcomponent.field.legend.font-weighttype.label.font-weightvar(--field-legend-font-weight)font.weight.700:700
Text sizecomponent.field.legend.font-sizetype.body.font-sizevar(--field-legend-font-size)font.size.19:1.1875rem
Group
Gapcomponent.field.group.gapcomponent-spacing-map.x8var(--field-group-gap)space.56:3.5rem
Gap 2component.field.group.gap-2component-spacing-map.x3var(--field-group-gap-2)space.20:1.25rem
Gap 3component.field.group.gap-3space.gap.groupvar(--field-group-gap-3)space.32:2rem
Field
Gapcomponent.field.gapcomponent-spacing-map.x3var(--field-gap)space.20:1.25rem
Label invalid
Changes with theme
when invalid
component.field.label-invalidcolor.status.dangervar(--field-label-invalid)Lightred.light.600:#C42B2BDarkred.dark.600:#F28B8B
Border checked
Changes with theme
when on
component.field.border-checkedcolor.action.primaryvar(--field-border-checked)Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8
Background checked
Changes with theme
when on
component.field.background-checkedcolor.action.primaryvar(--field-background-checked)Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8
Fill
Changes with theme
component.field.backgroundcolor.surface.canvasvar(--field-background)Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A
Padding, left and rightcomponent.field.padding-xcomponent-spacing-map.x2var(--field-padding-x)space.8:0.5rem
Gap 2component.field.gap-2space.gap.tightvar(--field-gap-2)space.12:0.75rem
Content
Gapcomponent.field.content.gapcomponent-spacing-map.x2var(--field-content-gap)space.8:0.5rem
Text
Gapcomponent.field.label.gapspace.gap.defaultvar(--field-label-gap)space.16:1rem
Disabled opacity disabled
when disabled
component.field.label.disabled-opacity-disabledopacity.disabledvar(--field-label-disabled-opacity-disabled)opacity.50:0.5
Cornercomponent.field.label.radiusradius.controlvar(--field-label-radius)radius.6:0.375rem
Paddingcomponent.field.label.paddingcomponent-spacing-map.x4var(--field-label-padding)space.28:1.75rem
Text sizecomponent.field.label.font-sizetype.body.font-sizevar(--field-label-font-size)font.size.19:1.1875rem
Weightcomponent.field.label.font-weighttype.label.font-weightvar(--field-label-font-weight)font.weight.700:700
Color
Changes with theme
component.field.labelcolor.text.mutedvar(--field-label)Lightgray.light.500:#595959Darkgray.dark.500:#949494
Description
Text sizecomponent.field.description.font-sizetype.body.font-sizevar(--field-description-font-size)font.size.19:1.1875rem
Weightcomponent.field.description.font-weighttype.body.font-weightvar(--field-description-font-weight)font.weight.400:400
Text
Changes with theme
component.field.description.labelcolor.text.mutedvar(--field-description-label)Lightgray.light.500:#595959Darkgray.dark.500:#949494
Label 2
Changes with theme
component.field.description.label-2color.action.primaryvar(--field-description-label-2)Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8
Separator
Text sizecomponent.field.separator.font-sizetype.body.font-sizevar(--field-separator-font-size)font.size.19:1.1875rem
Error
Text sizecomponent.field.error.font-sizetype.body.font-sizevar(--field-error-font-size)font.size.19:1.1875rem
Weightcomponent.field.error.font-weighttype.body.font-weightvar(--field-error-font-weight)font.weight.400:400
Text
Changes with theme
component.field.error.labelcolor.status.dangervar(--field-error-label)Lightred.light.600:#C42B2BDarkred.dark.600:#F28B8B
From the system
Edge
Changes with theme
color.border.default
straight from the system, through --border
Lightgray.light.200 · #D6D6D6Darkgray.dark.100 · #222222