Skip to main content

Toggle

A button that stays pressed or not.

In Multi-brand

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Multi-brand: One mode turned on or off in place. Not for account settings, which use switch, or one-off actions, which use button.

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

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

Toggles that sit together, like monthly or yearly prices, use toggle-group.

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

A single toggle, with a border so it reads as a control in every brand.

defaultsizeUsed in Multi-brand
Start Storybook to see it live

What it is for

The normal size.

✓Most toolbars.
✕Very dense toolbars.

In Multi-brand

Every toggle, at touch size: customers are often on phones.

smsizeNot used in Multi-brand
Start Storybook to see it live

What it is for

A smaller toggle.

✓Dense toolbars and compact panels.
✕Touch screens.

In Multi-brand

Customers are often on phones: no compact controls.

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

One density across brands.

Why it works this way​

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

How does a component earn its place?components.definition
1The answer
Its family is in the roles, so product teams find it ready

Product teams find a whole family ready instead of asking one by one.

2So Toggle
  • –does not use default: Toggles that sit together, like monthly or yearly prices, use toggle-group.
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

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

Customers see it fresh each time.

2So Toggle
  • ✓uses outline: A single toggle, with a border so it reads as a control in every brand.
It also shapes Button: ghost
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

How are brands handled?theming.definition
1The answer
Brands as modes

Brands are theme modes over shared semantics.

2So Toggle
  • ✓uses outline: A single toggle, with a border so it reads as a control in every brand.
It also shapes Sidebar: default Sidebar: outline Empty: default Item: muted
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

Customers sign up on phones.

2So Toggle
  • ✓uses default: Every toggle, at touch size: customers are often on phones.
  • –does not use sm: Customers are often on phones: no compact controls.
It also shapes Button: default Button: compact Button: icon Field: horizontal Item: default Item: sm
3If it changed
2
Toggle decisions would reopen

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

How many densities exist?spacing.count
1The answer
One

One density across brands.

2So Toggle
  • –does not use lg: One density across brands.
It also shapes Sidebar: default Sidebar: sm Sidebar: lg Item: sm
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

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​

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

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
Toggle
Gapcomponent.toggle.gapspace.gap.defaultvar(--toggle-gap)space.12:0.75rem
Corner
Changes with theme
component.toggle.radiusradius.controlvar(--toggle-radius)Aster Lightradius.4:0.25remAster Darkradius.4:0.25remBirch Lightradius.12:0.75remBirch Darkradius.12:0.75remCoral Lightradius.999:9999pxCoral Darkradius.999:9999px
Text sizecomponent.toggle.font-sizetype.body.font-sizevar(--toggle-font-size)font.size.16:1rem
Weightcomponent.toggle.font-weighttype.label.font-weightvar(--toggle-font-weight)font.weight.600:600
Background hover
Changes with theme
on hover
component.toggle.background-hovercolor.surface.sunkenvar(--toggle-background-hover)Aster Lightslate-deep.light.100:#F1F5F9Aster Darkslate-deep.dark.100:#1E293BBirch Lightslate-deep.light.100:#F1F5F9Birch Darkslate-deep.dark.100:#1E293BCoral Lightslate-deep.light.100:#F1F5F9Coral Darkslate-deep.dark.100:#1E293B
Label hover
Changes with theme
on hover
component.toggle.label-hovercolor.text.mutedvar(--toggle-label-hover)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
Border focus
Changes with theme
on focus
component.toggle.border-focuscolor.border.focusvar(--toggle-border-focus)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
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)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
Focus ring invalid
Changes with theme
when invalid
component.toggle.focus-ring-invalidcolor.status.dangervar(--toggle-focus-ring-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
Background on
Changes with theme
when on
component.toggle.background-oncolor.surface.sunkenvar(--toggle-background-on)Aster Lightslate-deep.light.100:#F1F5F9Aster Darkslate-deep.dark.100:#1E293BBirch Lightslate-deep.light.100:#F1F5F9Birch Darkslate-deep.dark.100:#1E293BCoral Lightslate-deep.light.100:#F1F5F9Coral Darkslate-deep.dark.100:#1E293B
Label on
Changes with theme
when on
component.toggle.label-oncolor.text.primaryvar(--toggle-label-on)Aster Lightslate-deep.light.900:#0F172AAster Darkslate-deep.dark.900:#F8FAFCBirch Lightslate-deep.light.900:#0F172ABirch Darkslate-deep.dark.900:#F8FAFCCoral Lightslate-deep.light.900:#0F172ACoral Darkslate-deep.dark.900:#F8FAFC
Icon sizecomponent.toggle.icon-sizesize.iconvar(--toggle-icon-size)size.16:1rem
Focus
on focus
component.toggle.focusborder-width.focusvar(--toggle-focus)border-width.2:2px
Focus ring focus
Changes with theme
on focus
component.toggle.focus-ring-focuscolor.border.focusvar(--toggle-focus-ring-focus)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
Focus ring offset focus
Changes with theme
on focus
component.toggle.focus-ring-offset-focuscolor.surface.canvasvar(--toggle-focus-ring-offset-focus)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
Outline
Border
Changes with theme
component.toggle.outline.bordercolor.border.strongvar(--toggle-outline-border)Aster Lightslate-deep.light.500:#64748BAster Darkslate-deep.dark.400:#96A3B5Birch Lightslate-deep.light.500:#64748BBirch Darkslate-deep.dark.400:#96A3B5Coral Lightslate-deep.light.500:#64748BCoral Darkslate-deep.dark.400:#96A3B5
Shadow
Changes with theme
component.toggle.outline.shadowelevation.cardvar(--toggle-outline-shadow)Aster Lightshadow.cardAster Darkshadow.card-darkBirch Lightshadow.cardBirch Darkshadow.card-darkCoral Lightshadow.cardCoral Darkshadow.card-dark
Background hover
Changes with theme
on hover
component.toggle.outline.background-hovercolor.surface.sunkenvar(--toggle-outline-background-hover)Aster Lightslate-deep.light.100:#F1F5F9Aster Darkslate-deep.dark.100:#1E293BBirch Lightslate-deep.light.100:#F1F5F9Birch Darkslate-deep.dark.100:#1E293BCoral Lightslate-deep.light.100:#F1F5F9Coral Darkslate-deep.dark.100:#1E293B
Label hover
Changes with theme
on hover
component.toggle.outline.label-hovercolor.text.primaryvar(--toggle-outline-label-hover)Aster Lightslate-deep.light.900:#0F172AAster Darkslate-deep.dark.900:#F8FAFCBirch Lightslate-deep.light.900:#0F172ABirch Darkslate-deep.dark.900:#F8FAFCCoral Lightslate-deep.light.900:#0F172ACoral Darkslate-deep.dark.900:#F8FAFC
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.44:2.75rem
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.20:1.25rem