Skip to main content

Button

A control that commits an action on this screen.

In Content first

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Content first: Subscribe, share and save.

  • Use it when: The press does something here: save, submit, delete, open a dialog, go to the next step.
  • Not when: The press only takes people to another page, or flips a setting that should persist.

Instead:

  • Toggle: An on or off setting that should persist as state.
  • Link: Navigation to another page or section.

Variants​

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

primaryvariantUsed in Content first
Start Storybook to see it live

What it is for

The one action this view is for.

✓One commit: Submit, Save, Continue, Start, Next.
✕A second equally loud action on the same view.

In Content first

One clear action per section.

secondaryvariantUsed in Content first
Start Storybook to see it live

What it is for

A real action, quieter than the commit.

✓Cancel, Back or Reset next to a primary, or an action with no commit beside it.
✕It is the only action; then it is primary.

In Content first

The first step of a delete, with the full verb, before the confirm.

dangervariantUsed in Content first
Start Storybook to see it live

What it is for

A destructive commit, after a confirm.

✓The confirm step of a delete or remove, with the verb: Delete 40 alerts.
✕The first step of a delete; that is secondary. Anything that does not destroy data.

In Content first

The confirm of a delete, with the full verb. Deleting a draft is rare and final.

ghostvariantUsed in Content first
Start Storybook to see it live

What it is for

The lowest emphasis; it never competes with the commit.

✓A third option, a card or row action, a toast action.
✕The page's main task; that is primary.

In Content first

Alternatives as a text link, and row actions with a word.

defaultsizeUsed in Content first
Start Storybook to see it live

What it is for

The normal size, for ordinary actions in forms, dialogs and page chrome.

In Content first

Every button, at touch size: many readers are on phones.

compactsizeNot used in Content first
Start Storybook to see it live

What it is for

Denser, for toolbars and table rows that sit beside content.

✕The page's main commit, or a control too small to hit.

In Content first

Many readers are on phones: no compact controls.

iconsizeNot used in Content first
Start Storybook to see it live

What it is for

Icon only: the name is not on screen.

✓A repeated, known control in chrome: close, settings, more.
✕A one-off or destructive action with no accessible name.

In Content first

Occasional visitors do not know icons: always add a word.

largesizeNot used in Content first
Start Storybook to see it live

What it is for

The largest size: the one action that must stand out most.

✓The single main call to action of a section or page.
✕Ordinary actions, or more than one per section.

In Content first

Attention belongs to the text: one clear action per section, at the normal size.

hoverstateUsed in Content first

What it is for

The pointer is over the control: an extra cue, never the only cue.

✓A mouse or trackpad can reach the control.
✕Hover is the only way to tell it is a button; that fails on touch.

In Content first

Moderate feedback that feels calm.

focusstateUsed in Content first

What it is for

Keyboard or assistive tech is on this control. It must stay visible.

✓Tab, Enter, Space, or focus restored after a dialog.
✕The focus outline removed with no replacement.

In Content first

Always visible.

disabledstateNot used in Content first

What it is for

The action is not available. It must stay perceivable and say why.

✓A control that must not run now, with a visible reason.
✕A form's submit that could explain the problem on press instead.

In Content first

Keep it enabled and explain on press: visitors see the interface fresh each time.

How Content first uses it​

An icon with no label
Use Not used: always add a word
Not An icon button
Occasional visitors do not know icons.
Keyboard focus
Use button/focus-ring at border-width.focus
Not Removing the outline
Keyboard users must always see where they are.
The action that completes the task
Use Button primary, one per section
Not Two primaries side by side
One clear action per section. Two primaries make the reader choose before they have read.
An alternative or cancel
Use Button ghost as a text link
Not A second boxed button
A link feels lighter and does not compete.
Delete or remove
Use Button secondary with the full verb, then confirm with Button danger
Not Button primary
Naming the verb and confirming prevents a mistake.
An action on one row or item
Use Button ghost with a word
Not An icon on its own
Occasional visitors need the word.

Why it works this way​

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

How often do people repeat actions?tasks.q2
1The answer
Rarely

Actions are rare: subscribe, share, save.

2So Button
  • ✓uses primary: One clear action per section.
  • –does not use large: Attention belongs to the text: one clear action per section, at the normal size.
It also shapes Field: horizontal Input group: block-start Input group: block-end
3If it changed
2
Button decisions would reopen

and 2 answers that follow from it: Three, Only what today's screens use.

What is a costly mistake?outcomes.q2
1The answer
Losing your place

Losing your place in an article breaks the read.

2So Button
  • ✓uses secondary: The first step of a delete, with the full verb, before the confirm.
  • ✓uses danger: The confirm of a delete, with the full verb. Deleting a draft is rare and final.
3If it changed
2
Button decisions would reopen

No other answer follows from it.

What should the brand feel like?brand.q1
1The answer
Warm

Warm and considered, like a good magazine.

2So Button
  • ✓uses ghost: Alternatives as a text link, and row actions with a word.
3If it changed
2
Button decisions would reopen

and 11 answers that follow from it: Present, Neutrals and one brand ramp, Balanced, and 8 more.

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

Visitors see the interface fresh each time.

2So Button
  • ✓uses ghost: Alternatives as a text link, and row actions with a word.
  • –does not use icon: Occasional visitors do not know icons: always add a word.
  • –does not use disabled: Keep it enabled and explain on press: visitors see the interface fresh each time.
It also shapes Input group: inline-start Input group: inline-end
3If it changed
3
Button decisions would reopen

No other answer follows from it.

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

Readers are often on phones.

2So Button
  • ✓uses default: Every button, at touch size: many readers are on phones.
  • –does not use compact: Many readers are on phones: no compact controls.
It also shapes Item: default Item: sm
3If it changed
2
Button decisions would reopen

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

What do icons do?iconography.roles
1The answer
Actions, status and navigation

Occasional readers need icons for save, share and navigation.

2So Button
  • –does not use icon: Occasional visitors do not know icons: always add a word.
It also shapes Input group: inline-start
3If it changed
1
Button decision would reopen

No other answer follows from it.

What must the interface protect?human.q1
1The answer
Attention on the content

Attention belongs to the text.

2So Button
  • –does not use large: Attention belongs to the text: one clear action per section, at the normal size.
It also shapes Field: horizontal Empty: default Item: muted
3If it changed
1
Button decision would reopen

and 8 answers that follow from it: Surface, text, action and status, Only what floats, One, and 5 more.

How fast does feedback feel?motion.values
1The answer
Moderate

Moderate speed feels calm.

2So Button
  • ✓uses hover: Moderate feedback that feels calm.
3If it changed
1
Button decision would reopen

No other answer follows from it.

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

AA is the commitment for a public site.

2So Button
  • ✓uses focus: Always visible.
  • –does not use disabled: Keep it enabled and explain on press: visitors see the interface fresh each time.
It also shapes Field: vertical
3If it changed
2
Button decisions would reopen

and 3 answers that follow from it: A ratio, line heights in proportion, Two: color and word, AA.

Words​

  • Write to one person: "you", not "users".
  • Short sentences with room to breathe.
  • Explain why before asking for anything.
  • Use: Read, Save for later, You, Next issue. Avoid: Click here, Submit, Users, Content.

The labels components use, from the voice tokens:

  • content.action.commit: Save
  • content.action.cancel: Not now
  • content.action.destroy: Remove
  • content.action.retry: Try again
  • content.action.empty-action: Browse guides

See Voice and tone for the whole voice.

Accessibility and keyboard​

KeyboardWhat happensNot
Moving through the page or a dialog.Tab lands on the button in a logical order.The control is skipped because it used HTML disabled.
The button is focused and the person confirms.Enter runs the same action as a click.Enter only works on submit buttons.
The button is focused.Space runs the same action as a click.Space scrolls the page instead.
The button opened a dialog, menu or popover that just closed.After the action closes a surface, focus returns to the button.Focus dropped on the page body.

Test before you ship:

  • The control has a name assistive tech can speak. Any button, especially an icon-only one.
  • The focused control stays visible; color alone is not the cue. Keyboard or assistive-tech focus is on the button.
  • Unavailable stays in the tab order and explains why. The action must not run, but focus must not jump away.
  • A button that stays down exposes its pressed state. Pin, follow, or another sticky on or off on a button.
  • A button that opens something exposes open or closed. The press reveals a menu, panel or more content.
  • The hit area stays usable, including at a small size. Touch, pointer, or a compact toolbar button.

Use it in code​

npx open-ds add button

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

Tokens it reads​

Button reads 32 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 Button's tokens to change Button alone.

Changes with theme

Content 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
Primary
Fill
Changes with theme
component.button.primary.backgroundcolor.action.primaryvar(--button-primary-background)Lightnavy.light.600:#1E3A5FDarknavy.dark.600:#8FA6C4
Background hover
Changes with theme
on hover
component.button.primary.background.hovercolor.action.primary.hovervar(--button-primary-background-hover)Lightnavy.light.700:#122948Darknavy.dark.700:#ACBDD5
Text
Changes with theme
component.button.primary.labelcolor.text.on-actionvar(--button-primary-label)Lightstone.light.0:#FFFFFFDarkstone.light.900:#1F1B16
Secondary
Fill
Changes with theme
component.button.secondary.backgroundcolor.action.secondaryvar(--button-secondary-background)Lightstone.light.0:#FFFFFFDarkstone.dark.50:#1F1B16
Background hover
Changes with theme
on hover
component.button.secondary.background.hovercolor.action.secondary.hovervar(--button-secondary-background-hover)Lightstone.light.100:#F6F2EADarkstone.dark.100:#2E2A24
Text
Changes with theme
component.button.secondary.labelcolor.text.primaryvar(--button-secondary-label)Lightstone.light.900:#1F1B16Darkstone.dark.900:#FFFDF8
Border
Changes with theme
component.button.secondary.bordercolor.border.strongvar(--button-secondary-border)Lightstone.light.500:#7A7062Darkstone.dark.400:#A39A8B
Border widthcomponent.button.secondary.border-widthborder-width.defaultvar(--button-secondary-border-width)border-width.1:1px
Danger
Fill
Changes with theme
component.button.danger.backgroundcolor.action.dangervar(--button-danger-background)Lightred.light.600:#C42B2BDarkred.dark.600:#F28B8B
Background hover
Changes with theme
on hover
component.button.danger.background.hovercolor.action.danger.hovervar(--button-danger-background-hover)Lightred.light.700:#981519Darkred.dark.700:#FAA9A8
Text
Changes with theme
component.button.danger.labelcolor.text.on-dangervar(--button-danger-label)Lightstone.light.0:#FFFFFFDarkstone.light.900:#1F1B16
Ghost
Background hover
Changes with theme
on hover
component.button.ghost.background-hovercolor.surface.sunkenvar(--button-ghost-background-hover)Lightstone.light.100:#F6F2EADarkstone.dark.100:#2E2A24
Text
Changes with theme
component.button.ghost.labelcolor.text.linkvar(--button-ghost-label)Lightnavy.light.700:#122948Darknavy.dark.700:#ACBDD5
Button
Heightcomponent.button.heightsize.control.defaultvar(--button-height)size.48:3rem
Padding, left and rightcomponent.button.padding-xspace.inset.control-xvar(--button-padding-x)space.20:1.25rem
Cornercomponent.button.radiusradius.controlvar(--button-radius)radius.8:0.5rem
Text sizecomponent.button.font-sizetype.label.font-sizevar(--button-font-size)font.size.16:1rem
Weightcomponent.button.font-weighttype.label.font-weightvar(--button-font-weight)font.weight.600:600
Focus ring
Changes with theme
on focus
component.button.focus-ringcolor.border.focusvar(--button-focus-ring)Lightnavy.light.600:#1E3A5FDarknavy.dark.600:#8FA6C4
Icon sizecomponent.button.icon-sizesize.iconvar(--button-icon-size)size.16:1rem
Gapcomponent.button.gapspace.gap.defaultvar(--button-gap)space.16:1rem
Font familycomponent.button.font-familytype.label.font-familyvar(--button-font-family)font.family.inter:Inter, system-ui, sans-serif
Focus
on focus
component.button.focusborder-width.focusvar(--button-focus)border-width.2:2px
Canvas
Changes with theme
on focus
component.button.canvascolor.surface.canvasvar(--button-canvas)Lightstone.light.50:#FFFDF8Darkstone.dark.0:#14110D
Disabled
Fill
Changes with theme
when disabled
component.button.disabled.backgroundcolor.disabled.surfacevar(--button-disabled-background)Lightstone.light.100:#F6F2EADarkstone.dark.100:#2E2A24
Text
Changes with theme
when disabled
component.button.disabled.labelcolor.disabled.textvar(--button-disabled-label)Lightstone.light.500:#7A7062Darkstone.dark.500:#CFC6B6
Border
Changes with theme
when disabled
component.button.disabled.bordercolor.disabled.bordervar(--button-disabled-border)Lightstone.light.200:#E4DDD0Darkstone.dark.200:#4A4339
Compact
Heightcomponent.button.compact.heightsize.control.compactvar(--button-compact-height)size.40:2.5rem
Padding, left and rightcomponent.button.compact.padding-xspace.inset.cell-xvar(--button-compact-padding-x)space.16:1rem
Large
Heightcomponent.button.large.heightsize.control.largevar(--button-large-height)size.48:3rem
Shared
Transition durationcomponent.transition.durationmotion.duration.feedbackvar(--transition-duration)duration.moderate:200ms
Transition easingcomponent.transition.easingmotion.easing.standardvar(--transition-easing)easing.standard:cubic-bezier(0.2,0,0,1)