Skip to main content

Field

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

In Brand expressive

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Brand expressive: Lays out the sign-up form.

  • 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 Brand expressive decided and why. Each sample is the live story with that variant set.

verticalorientationUsed in Brand expressive
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 Brand expressive

The sign-up form: the label above the field.

horizontalorientationNot used in Brand expressive
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 Brand expressive

Phones first: there is no room beside the field.

responsiveorientationNot used in Brand expressive
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 Brand expressive

Phones first: one stacked layout.

How Brand expressive uses it​

Field label or column header
Use type.label
Not type.caption
Labels stay readable on vivid color.
An error message
Use color.status.danger and a short sentence
Not The brand magenta
Errors must not look like the brand.

Why it works this way​

Field looks the way it does because of 2 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.

Where do people use it?scenarios.q1
1The answer
On the move

Visitors come from social posts on their phones.

2So Field
  • ✓uses vertical: The sign-up form: the label above the field.
  • –does not use horizontal: Phones first: there is no room beside the field.
  • –does not use responsive: Phones first: one stacked layout.
3If it changed
3
Field decisions would reopen

and 2 answers that follow from it: Touch size, Those and pressed.

What is the smallest target?accessibility.values
1The answer
Touch size

Visitors come from social posts on phones.

2So Field
  • –does not use horizontal: Phones first: there is no room beside the field.
It also shapes Button: default Button: compact Button: icon Input group: inline-end Item: default Item: sm Toggle: default Toggle: sm
3If it changed
1
Field decision would reopen

and 1 answer that follow from it: Those and pressed.

Words​

  • Headlines are short and bold. Three to six words.
  • Verbs that move: Join, Try, Go.
  • Playful in headlines, plain in errors.
  • Use: Join, Drop, Go, Love. Avoid: Synergy, Leverage, Kindly.

The labels components use, from the voice tokens:

  • content.action.commit: Go
  • content.action.cancel: Not now
  • content.action.destroy: Remove
  • content.action.retry: Try again
  • content.action.empty-action: Explore the drop

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

Brand expressive 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.64:4rem
Gap 2component.field.set.gap-2component-spacing-map.x3var(--field-set-gap-2)space.24:1.5rem
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.17:1.0625rem
Group
Gapcomponent.field.group.gapcomponent-spacing-map.x8var(--field-group-gap)space.64:4rem
Gap 2component.field.group.gap-2component-spacing-map.x3var(--field-group-gap-2)space.24:1.5rem
Gap 3component.field.group.gap-3space.gap.groupvar(--field-group-gap-3)space.40:2.5rem
Field
Gapcomponent.field.gapcomponent-spacing-map.x3var(--field-gap)space.24:1.5rem
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)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Background checked
Changes with theme
when on
component.field.background-checkedcolor.action.primaryvar(--field-background-checked)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Fill
Changes with theme
component.field.backgroundcolor.surface.canvasvar(--field-background)Lighttaupe.light.50:#FFF8F3Darktaupe.dark.0:#120B09
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.8:0.5rem
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.40:0.4
Cornercomponent.field.label.radiusradius.controlvar(--field-label-radius)radius.999:9999px
Paddingcomponent.field.label.paddingcomponent-spacing-map.x4var(--field-label-padding)space.32:2rem
Text sizecomponent.field.label.font-sizetype.body.font-sizevar(--field-label-font-size)font.size.17:1.0625rem
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)Lighttaupe.light.600:#6B544ADarktaupe.dark.400:#B5988A
Description
Text sizecomponent.field.description.font-sizetype.body.font-sizevar(--field-description-font-size)font.size.17:1.0625rem
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)Lighttaupe.light.600:#6B544ADarktaupe.dark.400:#B5988A
Label 2
Changes with theme
component.field.description.label-2color.action.primaryvar(--field-description-label-2)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Separator
Text sizecomponent.field.separator.font-sizetype.body.font-sizevar(--field-separator-font-size)font.size.17:1.0625rem
Error
Text sizecomponent.field.error.font-sizetype.body.font-sizevar(--field-error-font-size)font.size.17:1.0625rem
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
Lighttaupe.light.200 · #F2DCCFDarktaupe.dark.100 · #33241F