Components · Actions
Toggle
A button with an on/off state, driven by aria-pressed — for formatting controls and view switches. It reads as a ghost button when off and holds a muted fill when on. Group several into a toolbar segment for mutually-related options.
Preview
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Toggle" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `toggle` spec (variants: off, pressed, disabled) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `toggle` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
┌────────────┐
│ [icon] │ ← 32px height, transparent at rest
└────────────┘
Fill 0 (outlined) at rest
Fill 1 (filled) when pressed
| State | Background | Text | Notes |
|---|---|---|---|
| Default (unpressed) | transparent |
{colors.muted} |
Outlined icon |
| Hover | {colors.primary-bg} |
{colors.primary} |
— |
| Focus | transparent + 2px solid {colors.border-focus} |
{colors.muted} |
Keyboard only |
| Active (click down) | {colors.surface-pressed} |
{colors.ink} |
Momentary press |
| Pressed (sustained) | {colors.surface-muted} |
{colors.ink} |
Filled icon (Fill 1) |
| Pressed hover | {colors.primary-bg} |
{colors.primary} |
Hovered while pressed |
| Disabled | {colors.disabled-bg} |
{colors.disabled-text} |
not-allowed |
Note: toggle-active (momentary click) and toggle-pressed (sustained on) are intentionally different. Do not normalize them.
When to use
- Toolbar actions that persist state visually: bold, italic, filter on/off, view mode switch.
- Icon-only buttons where the pressed state is meaningful and must be communicated.
When not to use
- For boolean form fields that submit a value — use Toggle Switch.
- For navigation — use sidebar or tab nav.
- For actions without a persistent pressed state — use Icon Button.
Do
- Always pair with a tooltip showing the action name.
- Use icons that have both outlined and filled variants in Material Symbols.
- Reflect the
pressedstate from application state — do not manage internally.
Don't
- Don't use Toggle for actions that require confirmation.
- Don't use Toggle for navigation or page transitions.
aria-label: action verb — "Bold", "Toggle filter", "Italic".- Tooltip: must match
aria-labelexactly. aria-pressedvalue changes: announce viaaria-liveif the change is not visually obvious.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Move focus to toggle |
Space |
Toggle pressed state |
Enter |
Toggle pressed state |
Accessibility
- ARIA role:
role="button"witharia-pressed="true"oraria-pressed="false". aria-labelrequired — the icon alone is not accessible.aria-disabled="true"on disabled toggles.- Focus ring:
2px solid {colors.border-focus}at 2px offset.
Spec
Values and token references resolved straight from the Zetta spec.
toggle
- backgroundColor
- transparent
- textColor
- {colors.muted}
- borderColor
- transparent
- borderWidth
- 1px
- borderRadius
- {rounded.base}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 500
- height
- 32px
- minWidth
- 32px
- iconSize
- 16px
- cursor
- pointer
toggle-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
toggle-active
- backgroundColor
- {colors.surface-pressed}
toggle-pressed
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
toggle-pressed-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
toggle-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- 2px
toggle-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed