Skip to main content

Toggle

A button that stays pressed or not.

In Brand expressive

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Brand expressive: A mode turned on and off in place. Not as a one-off action; button does that.

  • Use it when: Turning a mode or format on and off in a toolbar, such as bold.
  • Not when: A setting saved at once (switch), or a one-off action (button).

Instead:

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.

defaultvariantNot used in Brand expressive
Start Storybook to see it live

What it is for

No border; the pressed state shows as a fill.

✓Toolbars where toggles sit together.
✕A toggle alone on a busy background.

In Brand expressive

Sections lead with media and color blocks, not toolbars; a toggle with no border is lost on vivid color.

outlinevariantUsed in Brand expressive
Start Storybook to see it live

What it is for

A border, so the toggle reads as a control at rest.

✓A toggle that stands alone.
✕A dense toolbar of many toggles.

In Brand expressive

A pill with a border, so the toggle reads as a control on vivid color.

defaultsizeUsed in Brand expressive
Start Storybook to see it live

What it is for

The normal size.

✓Most toolbars.
✕Very dense toolbars.

In Brand expressive

Every toggle, at touch size: phones first.

smsizeNot used in Brand expressive
Start Storybook to see it live

What it is for

A smaller toggle.

✓Dense toolbars and compact panels.
✕Touch screens.

In Brand expressive

Phones first: no compact controls.

lgsizeNot used in Brand expressive
Start Storybook to see it live

What it is for

A larger toggle.

✓A few toggles that need to be easy to hit.
✕Dense toolbars.

In Brand expressive

Large is for the one call to action per section, and a toggle is not that action.

Why it works this way​

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

Which component families does it need?components.roles
1The answer
Core, plus media

Campaigns lead with media and big sections.

2So Toggle
  • –does not use default: Sections lead with media and color blocks, not toolbars; a toggle with no border is lost on vivid color.
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

How saturated is the palette?color.values
1The answer
Vivid

Vivid color is the brand.

2So Toggle
  • –does not use default: Sections lead with media and color blocks, not toolbars; a toggle with no border is lost on vivid color.
  • ✓uses outline: A pill with a border, so the toggle reads as a control on vivid color.
It also shapes Item: outline
3If it changed
2
Toggle decisions would reopen

and 2 answers that follow from it: Two-tone, Brand color.

What should control corners say?shape.values
1The answer
Pill

Pill controls feel playful.

2So Toggle
  • ✓uses outline: A pill with a border, so the toggle reads as a control on vivid color.
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

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

Visitors come from social posts on phones.

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

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

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

One action: sign up.

2So Toggle
  • –does not use lg: Large is for the one call to action per section, and a toggle is not that action.
It also shapes Alert: default Input group: block-start Input group: block-end
3If it changed
1
Toggle decision would reopen

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

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

Engagement and sign-ups.

2So Toggle
  • –does not use lg: Large is for the one call to action per section, and a toggle is not that action.
It also shapes Button: primary Button: large Badge: warning Toast: success
3If it changed
1
Toggle decision would reopen

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

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
undefinedSpace or Enter presses it.undefined

Test before you ship:

  • It says whether it is pressed. undefined

Use it in code​

npx open-ds add toggle

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

Tokens it reads​

Toggle reads 24 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 Toggle's tokens to change Toggle 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
Toggle
Gapcomponent.toggle.gapspace.gap.defaultvar(--toggle-gap)space.16:1rem
Cornercomponent.toggle.radiusradius.controlvar(--toggle-radius)radius.999:9999px
Text sizecomponent.toggle.font-sizetype.body.font-sizevar(--toggle-font-size)font.size.17:1.0625rem
Weightcomponent.toggle.font-weighttype.label.font-weightvar(--toggle-font-weight)font.weight.700:700
Background hover
Changes with theme
on hover
component.toggle.background-hovercolor.surface.sunkenvar(--toggle-background-hover)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Label hover
Changes with theme
on hover
component.toggle.label-hovercolor.text.mutedvar(--toggle-label-hover)Lighttaupe.light.600:#6B544ADarktaupe.dark.400:#B5988A
Border focus
Changes with theme
on focus
component.toggle.border-focuscolor.border.focusvar(--toggle-border-focus)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Disabled opacity disabled
when disabled
component.toggle.disabled-opacity-disabledopacity.disabledvar(--toggle-disabled-opacity-disabled)opacity.40:0.4
Border invalid
Changes with theme
when invalid
component.toggle.border-invalidcolor.status.dangervar(--toggle-border-invalid)Lightred.light.600:#C42B2BDarkred.dark.600:#F28B8B
Focus ring invalid
Changes with theme
when invalid
component.toggle.focus-ring-invalidcolor.status.dangervar(--toggle-focus-ring-invalid)Lightred.light.600:#C42B2BDarkred.dark.600:#F28B8B
Background on
Changes with theme
when on
component.toggle.background-oncolor.surface.sunkenvar(--toggle-background-on)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Label on
Changes with theme
when on
component.toggle.label-oncolor.text.primaryvar(--toggle-label-on)Lighttaupe.light.900:#1F1411Darktaupe.dark.900:#FFF8F3
Icon sizecomponent.toggle.icon-sizesize.iconvar(--toggle-icon-size)size.20:1.25rem
Focus
on focus
component.toggle.focusborder-width.focusvar(--toggle-focus)border-width.3:3px
Focus ring focus
Changes with theme
on focus
component.toggle.focus-ring-focuscolor.border.focusvar(--toggle-focus-ring-focus)Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA
Focus ring offset focus
Changes with theme
on focus
component.toggle.focus-ring-offset-focuscolor.surface.canvasvar(--toggle-focus-ring-offset-focus)Lighttaupe.light.50:#FFF8F3Darktaupe.dark.0:#120B09
Outline
Border
Changes with theme
component.toggle.outline.bordercolor.border.strongvar(--toggle-outline-border)Lighttaupe.light.500:#8A6F63Darktaupe.dark.400:#B5988A
Shadow
Changes with theme
component.toggle.outline.shadowelevation.cardvar(--toggle-outline-shadow)Lightshadow.cardDarkshadow.card-dark
Background hover
Changes with theme
on hover
component.toggle.outline.background-hovercolor.surface.sunkenvar(--toggle-outline-background-hover)Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F
Label hover
Changes with theme
on hover
component.toggle.outline.label-hovercolor.text.primaryvar(--toggle-outline-label-hover)Lighttaupe.light.900:#1F1411Darktaupe.dark.900:#FFF8F3
Default
Padding, left and rightcomponent.toggle.default.padding-xcomponent-spacing-map.x2var(--toggle-default-padding-x)space.8:0.5rem
Sm
Heightcomponent.toggle.sm.heightsize.control.defaultvar(--toggle-sm-height)size.52:3.25rem
Padding, left and rightcomponent.toggle.sm.padding-xcomponent-spacing-map.x2var(--toggle-sm-padding-x)space.8:0.5rem
Lg
Padding, left and rightcomponent.toggle.lg.padding-xcomponent-spacing-map.x3var(--toggle-lg-padding-x)space.24:1.5rem