Skip to main content

Field

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

In Multi-brand

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Multi-brand: Lays out every form the same way in every brand.

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

verticalorientationUsed in Multi-brand
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 Multi-brand

Sign-up and checkout forms: the label above, in every brand.

horizontalorientationNot used in Multi-brand
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 Multi-brand

Customers sign up on phones: labels stay above.

responsiveorientationUsed in Multi-brand
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 Multi-brand

Account settings: stacked on phones, label beside on laptops.

How Multi-brand uses it​

Field label or column header
Use type.label
Not type.caption
Labels stay readable in every brand.
An error message
Use color.status.danger and a sentence
Not The brand color
Status colors are shared, never branded.

Why it works this way​

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

AA in every brand.

2So Field
  • ✓uses vertical: Sign-up and checkout forms: the label above, in every brand.
It also shapes Button: focus Button: disabled Input group: block-start Input group: block-end
3If it changed
1
Field decision would reopen

and 1 answer that follow from it: AA.

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

Customers sign up on phones and manage plans on laptops.

2So Field
  • ✓uses vertical: Sign-up and checkout forms: the label above, in every brand.
  • ✓uses responsive: Account settings: stacked on phones, label beside on laptops.
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

Customers sign up on phones.

2So Field
  • –does not use horizontal: Customers sign up on phones: labels stay above.
It also shapes Button: default Button: compact Button: icon 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​

  • Shared structure: object, state, next step.
  • Brand tone lives in headlines and marketing copy, not in errors.
  • Errors and confirmations read the same in every brand.
  • Use: Get started, Plan, Order. Avoid: Brand-specific words in errors, Jargon.

The labels components use, from the voice tokens:

  • content.action.commit: Save
  • content.action.cancel: Cancel
  • content.action.destroy: Remove
  • content.action.retry: Try again
  • content.action.empty-action: Get started

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

Multi-brand decides what changes when you switch theme. Here a switch changes colors, type and shape, so the marked rows differ between its brands and 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.600:600
Text sizecomponent.field.legend.font-sizetype.body.font-sizevar(--field-legend-font-size)font.size.16:1rem
Group
Gapcomponent.field.group.gapcomponent-spacing-map.x8var(--field-group-gap)space.48:3rem
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.24:1.5rem
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)Aster Lightred.light.600:#C42B2BAster Darkred.dark.600:#F28B8BBirch Lightred.light.600:#C42B2BBirch Darkred.dark.600:#F28B8BCoral Lightred.light.600:#C42B2BCoral Darkred.dark.600:#F28B8B
Border checked
Changes with theme
when on
component.field.border-checkedcolor.action.primaryvar(--field-border-checked)Aster Lightteal.light.600:#0F766EAster Darkteal.dark.600:#7FCFC6Birch Lightpurple.light.600:#6D28D9Birch Darkpurple.dark.600:#B79DF7Coral Lightorange.light.600:#C2410CCoral Darkorange.dark.600:#F7A67F
Background checked
Changes with theme
when on
component.field.background-checkedcolor.action.primaryvar(--field-background-checked)Aster Lightteal.light.600:#0F766EAster Darkteal.dark.600:#7FCFC6Birch Lightpurple.light.600:#6D28D9Birch Darkpurple.dark.600:#B79DF7Coral Lightorange.light.600:#C2410CCoral Darkorange.dark.600:#F7A67F
Fill
Changes with theme
component.field.backgroundcolor.surface.canvasvar(--field-background)Aster Lightslate-deep.light.50:#F8FAFCAster Darkslate-deep.dark.0:#020617Birch Lightslate-deep.light.50:#F8FAFCBirch Darkslate-deep.dark.0:#020617Coral Lightslate-deep.light.50:#F8FAFCCoral Darkslate-deep.dark.0:#020617
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.12:0.75rem
Disabled opacity disabled
when disabled
component.field.label.disabled-opacity-disabledopacity.disabledvar(--field-label-disabled-opacity-disabled)opacity.40:0.4
Corner
Changes with theme
component.field.label.radiusradius.controlvar(--field-label-radius)Aster Lightradius.4:0.25remAster Darkradius.4:0.25remBirch Lightradius.12:0.75remBirch Darkradius.12:0.75remCoral Lightradius.999:9999pxCoral Darkradius.999:9999px
Paddingcomponent.field.label.paddingcomponent-spacing-map.x4var(--field-label-padding)space.24:1.5rem
Text sizecomponent.field.label.font-sizetype.body.font-sizevar(--field-label-font-size)font.size.16:1rem
Weightcomponent.field.label.font-weighttype.label.font-weightvar(--field-label-font-weight)font.weight.600:600
Color
Changes with theme
component.field.labelcolor.text.mutedvar(--field-label)Aster Lightslate-deep.light.600:#4B5A6FAster Darkslate-deep.dark.400:#96A3B5Birch Lightslate-deep.light.600:#4B5A6FBirch Darkslate-deep.dark.400:#96A3B5Coral Lightslate-deep.light.600:#4B5A6FCoral Darkslate-deep.dark.400:#96A3B5
Description
Text sizecomponent.field.description.font-sizetype.body.font-sizevar(--field-description-font-size)font.size.16:1rem
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)Aster Lightslate-deep.light.600:#4B5A6FAster Darkslate-deep.dark.400:#96A3B5Birch Lightslate-deep.light.600:#4B5A6FBirch Darkslate-deep.dark.400:#96A3B5Coral Lightslate-deep.light.600:#4B5A6FCoral Darkslate-deep.dark.400:#96A3B5
Label 2
Changes with theme
component.field.description.label-2color.action.primaryvar(--field-description-label-2)Aster Lightteal.light.600:#0F766EAster Darkteal.dark.600:#7FCFC6Birch Lightpurple.light.600:#6D28D9Birch Darkpurple.dark.600:#B79DF7Coral Lightorange.light.600:#C2410CCoral Darkorange.dark.600:#F7A67F
Separator
Text sizecomponent.field.separator.font-sizetype.body.font-sizevar(--field-separator-font-size)font.size.16:1rem
Error
Text sizecomponent.field.error.font-sizetype.body.font-sizevar(--field-error-font-size)font.size.16:1rem
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)Aster Lightred.light.600:#C42B2BAster Darkred.dark.600:#F28B8BBirch Lightred.light.600:#C42B2BBirch Darkred.dark.600:#F28B8BCoral Lightred.light.600:#C42B2BCoral Darkred.dark.600:#F28B8B
From the system
Edge
Changes with theme
color.border.default
straight from the system, through --border
Aster Lightslate-deep.light.200 · #E2E8F0Aster Darkslate-deep.dark.100 · #1E293BBirch Lightslate-deep.light.200 · #E2E8F0Birch Darkslate-deep.dark.100 · #1E293BCoral Lightslate-deep.light.200 · #E2E8F0Coral Darkslate-deep.dark.100 · #1E293B