Skip to main content

Toggle

A button that stays pressed or not.

In Dense data

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Dense data: One mode in the table toolbar that turns on and off. Not for settings saved at once, which use a switch.

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

defaultvariantUsed in Dense data
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 Dense data

In the table toolbar, with no border; the pressed state shows as a fill, quiet beside the data.

outlinevariantNot used in Dense data
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 Dense data

Borders on every toggle would compete with the data.

defaultsizeNot used in Dense data
Start Storybook to see it live

What it is for

The normal size.

✓Most toolbars.
✕Very dense toolbars.

In Dense data

Toggles sit in the table toolbar, at the smaller size like compact buttons.

smsizeUsed in Dense data
Start Storybook to see it live

What it is for

A smaller toggle.

✓Dense toolbars and compact panels.
✕Touch screens.

In Dense data

The table toolbar, matching compact buttons. It still fits a mouse.

lgsizeNot used in Dense data
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 Dense data

Controls stay small so more records fit on one screen.

Why it works this way​

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

What is the main content?content.q1
1The answer
Numbers and records

Most of the screen is numbers and records.

2So Toggle
  • ✓uses default: In the table toolbar, with no border; the pressed state shows as a fill, quiet beside the data.
  • –does not use outline: Borders on every toggle would compete with the data.
It also shapes Badge: neutral Badge: info Badge: success Badge: warning Badge: danger Sidebar: default Sidebar: outline Empty: default Item: default Item: outline
3If it changed
5
Toggle decisions would reopen

and 44 answers that follow from it: A lot, Scan and compare, People keep their place, and 41 more.

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

The same actions repeat many times an hour.

2So Toggle
  • ✓uses default: In the table toolbar, with no border; the pressed state shows as a fill, quiet beside the data.
It also shapes Button: ghost Input group: inline-end Sidebar: default Alert: default Toast: success
3If it changed
1
Toggle decision would reopen

and 11 answers that follow from it: Time on task, All day, Three, and 8 more.

How much is on one screen?content.q2
1The answer
A lot

Experts want many records on one screen.

2So Toggle
  • –does not use default: Toggles sit in the table toolbar, at the smaller size like compact buttons.
  • ✓uses sm: The table toolbar, matching compact buttons. It still fits a mouse.
  • –does not use lg: Controls stay small so more records fit on one screen.
It also shapes Button: default Button: compact Button: large Input group: block-start Input group: block-end Field: horizontal Sidebar: lg
3If it changed
3
Toggle decisions would reopen

and 14 answers that follow from it: Quiet, Only the steps the screens need, Small, and 11 more.

What is the smallest target?accessibility.values
1The answer
Compact, for a pointer

Mouse and keyboard; compact targets are enough.

2So Toggle
  • ✓uses sm: The table toolbar, matching compact buttons. It still fits a mouse.
It also shapes Button: default Button: compact Item: sm
3If it changed
1
Toggle decision would reopen

No other answer follows from it.

Words​

  • Lead with the object, then the state: "Alert 2041 contained."
  • Use numbers, not words for numbers.
  • One verb per button. No "please", no exclamation marks.
  • Use: Investigate, Filter, Assign, Escalated, Resolved. Avoid: Oops, Awesome, Please, Simply.

The labels components use, from the voice tokens:

  • content.action.commit: Save
  • content.action.cancel: Cancel
  • content.action.destroy: Delete
  • content.action.retry: Retry
  • content.action.empty-action: Change filters

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

Dense data 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.8:0.5rem
Cornercomponent.toggle.radiusradius.controlvar(--toggle-radius)radius.2:0.125rem
Text sizecomponent.toggle.font-sizetype.body.font-sizevar(--toggle-font-size)font.size.14:0.875rem
Weightcomponent.toggle.font-weighttype.label.font-weightvar(--toggle-font-weight)font.weight.500:500
Background hover
Changes with theme
on hover
component.toggle.background-hovercolor.surface.sunkenvar(--toggle-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label hover
Changes with theme
on hover
component.toggle.label-hovercolor.text.mutedvar(--toggle-label-hover)Lightslate.light.600:#535D6DDarkslate.dark.400:#969FAD
Border focus
Changes with theme
on focus
component.toggle.border-focuscolor.border.focusvar(--toggle-border-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
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)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label on
Changes with theme
when on
component.toggle.label-oncolor.text.primaryvar(--toggle-label-on)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
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)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Focus ring offset focus
Changes with theme
on focus
component.toggle.focus-ring-offset-focuscolor.surface.canvasvar(--toggle-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Outline
Border
Changes with theme
component.toggle.outline.bordercolor.border.strongvar(--toggle-outline-border)Lightslate.light.500:#6B7585Darkslate.dark.400:#969FAD
Shadowcomponent.toggle.outline.shadowelevation.cardvar(--toggle-outline-shadow)shadow.none
Background hover
Changes with theme
on hover
component.toggle.outline.background-hovercolor.surface.sunkenvar(--toggle-outline-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label hover
Changes with theme
on hover
component.toggle.outline.label-hovercolor.text.primaryvar(--toggle-outline-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
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.32:2rem
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.12:0.75rem