Skip to main content

Button

A control that commits an action on this screen.

In Brand expressive

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Brand expressive: The one action: sign up.

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

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

One loud call to action per section.

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

The first step of a delete, before the confirm.

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

The confirm of a delete. It stays red, apart from the brand's pink.

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

The alternative next to the primary, and row actions with a word.

defaultsizeUsed in Brand expressive
Start Storybook to see it live

What it is for

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

In Brand expressive

Every button, at touch size: phones first.

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

Phones first: no compact controls.

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

Phones first: hover hints never show, so every button has a word.

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

The one loud call to action per section.

hoverstateUsed in Brand expressive

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

Relaxed, springy feedback: motion is part of the brand.

focusstateUsed in Brand expressive

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

Always shows, even on vivid color.

disabledstateUsed in Brand expressive

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

Only with a visible reason next to it.

How Brand expressive uses it​

An icon with no label
Use Not used: always add a word
Not An icon button with a hover hint
On phones, hover hints never show; a word says what will happen.
Keyboard focus
Use button/focus-ring at border-width.focus
Not Removing the outline
Focus must show on vivid color.
The action that completes the task
Use Button primary, large, one per section
Not Two primaries
One loud call to action per section.
An alternative or cancel
Use Button ghost
Not A second primary
The alternative must not compete.
Delete or remove
Use Button secondary, then confirm with Button danger
Not Button primary
Confirm before destroying data.
An action on one row or item
Use Button ghost with a word
Not An icon alone
Visitors come rarely, so they never learn an icon. A word says what will happen.

Why it works this way​

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

Which outcome matters most?outcomes.q1
1The answer
Engagement

Engagement and sign-ups.

2So Button
  • ✓uses primary: One loud call to action per section.
  • ✓uses large: The one loud call to action per section.
It also shapes Badge: warning Toast: success Toggle: lg
3If it changed
2
Button decisions would reopen

and 6 answers that follow from it: A few minutes, Brand moments, Hero and launch moments, and 3 more.

What is a costly mistake?outcomes.q2
1The answer
Looking off-brand

A bland page is the costly mistake.

2So Button
  • ✓uses secondary: The first step of a delete, before the confirm.
  • ✓uses danger: The confirm of a delete. It stays red, apart from the brand's pink.
It also shapes Alert: destructive Item: muted
3If it changed
2
Button decisions would reopen

and 1 answer that follow from it: One per moment.

How many emphasis levels do actions need?color.count
1The answer
Three

Primary, secondary, ghost.

2So Button
  • ✓uses ghost: The alternative next to the primary, and row actions with a word.
3If it changed
1
Button decision would reopen

No other answer follows from it.

What do icons do?iconography.roles
1The answer
Those and decoration

Icons also decorate campaign pages.

2So Button
  • ✓uses ghost: The alternative next to the primary, and row actions with a word.
  • –does not use icon: Phones first: hover hints never show, so every button has a word.
It also shapes Empty: icon Input group: inline-start
3If it changed
2
Button decisions would reopen

and 1 answer that follow from it: Two.

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

Visitors come from social posts on phones.

2So Button
  • ✓uses default: Every button, at touch size: phones first.
  • –does not use compact: Phones first: no compact controls.
  • –does not use icon: Phones first: hover hints never show, so every button has a word.
It also shapes Field: horizontal Input group: inline-end Item: default Item: sm Toggle: default Toggle: sm
3If it changed
3
Button decisions would reopen

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

What is motion for?motion.roles
1The answer
Feedback and expression

Motion carries personality.

2So Button
  • ✓uses hover: Relaxed, springy feedback: motion is part of the brand.
3If it changed
1
Button decision would reopen

and 1 answer that follow from it: Three.

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

Feedback stays fast; the personality shows in panels and hero moments, which move slower on a spring.

2So Button
  • ✓uses hover: Relaxed, springy feedback: motion is part of the brand.
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, even when loud.

2So Button
  • ✓uses focus: Always shows, even on vivid color.
  • ✓uses disabled: Only with a visible reason next to it.
3If it changed
2
Button decisions would reopen

and 2 answers that follow from it: Two: color and word, AA.

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​

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

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
Primary
Fill
Changes with theme
component.button.primary.backgroundcolor.action.primaryvar(--button-primary-background)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Background hover
Changes with theme
on hover
component.button.primary.background.hovercolor.action.primary.hovervar(--button-primary-background-hover)Lightpink.light.700:#A6025BDarkpink.dark.700:#FBB4D8
Text
Changes with theme
component.button.primary.labelcolor.text.on-actionvar(--button-primary-label)Lighttaupe.light.0:#FFFFFFDarktaupe.light.900:#1F1411
Secondary
Fill
Changes with theme
component.button.secondary.backgroundcolor.action.secondaryvar(--button-secondary-background)Lighttaupe.light.0:#FFFFFFDarktaupe.dark.50:#1F1411
Background hover
Changes with theme
on hover
component.button.secondary.background.hovercolor.action.secondary.hovervar(--button-secondary-background-hover)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Text
Changes with theme
component.button.secondary.labelcolor.text.primaryvar(--button-secondary-label)Lighttaupe.light.900:#1F1411Darktaupe.dark.900:#FFF8F3
Border
Changes with theme
component.button.secondary.bordercolor.border.strongvar(--button-secondary-border)Lighttaupe.light.500:#8A6F63Darktaupe.dark.400:#B5988A
Border widthcomponent.button.secondary.border-widthborder-width.defaultvar(--button-secondary-border-width)border-width.2:2px
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)Lighttaupe.light.0:#FFFFFFDarktaupe.light.900:#1F1411
Ghost
Background hover
Changes with theme
on hover
component.button.ghost.background-hovercolor.surface.sunkenvar(--button-ghost-background-hover)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Text
Changes with theme
component.button.ghost.labelcolor.text.linkvar(--button-ghost-label)Lightpink.light.700:#A6025BDarkpink.dark.700:#FBB4D8
Button
Heightcomponent.button.heightsize.control.defaultvar(--button-height)size.52:3.25rem
Padding, left and rightcomponent.button.padding-xspace.inset.control-xvar(--button-padding-x)space.28:1.75rem
Cornercomponent.button.radiusradius.controlvar(--button-radius)radius.999:9999px
Text sizecomponent.button.font-sizetype.label.font-sizevar(--button-font-size)font.size.15:0.9375rem
Weightcomponent.button.font-weighttype.label.font-weightvar(--button-font-weight)font.weight.700:700
Focus ring
Changes with theme
on focus
component.button.focus-ringcolor.border.focusvar(--button-focus-ring)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Icon sizecomponent.button.icon-sizesize.iconvar(--button-icon-size)size.20:1.25rem
Gapcomponent.button.gapspace.gap.defaultvar(--button-gap)space.16:1rem
Font familycomponent.button.font-familytype.label.font-familyvar(--button-font-family)font.family.dm-sans:"DM Sans", system-ui, sans-serif
Focus
on focus
component.button.focusborder-width.focusvar(--button-focus)border-width.3:3px
Canvas
Changes with theme
on focus
component.button.canvascolor.surface.canvasvar(--button-canvas)Lighttaupe.light.50:#FFF8F3Darktaupe.dark.0:#120B09
Disabled
Fill
Changes with theme
when disabled
component.button.disabled.backgroundcolor.disabled.surfacevar(--button-disabled-background)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Text
Changes with theme
when disabled
component.button.disabled.labelcolor.disabled.textvar(--button-disabled-label)Lighttaupe.light.500:#8A6F63Darktaupe.dark.500:#E2C4B3
Border
Changes with theme
when disabled
component.button.disabled.bordercolor.disabled.bordervar(--button-disabled-border)Lighttaupe.light.200:#F2DCCFDarktaupe.dark.200:#4D3A33
Compact
Heightcomponent.button.compact.heightsize.control.compactvar(--button-compact-height)size.44:2.75rem
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.60:3.75rem
Shared
Transition durationcomponent.transition.durationmotion.duration.feedbackvar(--transition-duration)duration.fast:120ms
Transition easingcomponent.transition.easingmotion.easing.standardvar(--transition-easing)easing.spring:cubic-bezier(0.34,1.56,0.64,1)