Toggle Switch

Atom

A boolean on/off control — like a checkbox, but the "on" state takes effect immediately (no separate submit). Built on a native <input type="checkbox"> for keyboard support and form integration; the thumb slide is pure CSS.

When to use

✅ Do
  • Use for binary settings that take effect immediately — no Save button required
  • Use for system-level preferences (notifications on/off, dark mode, auto-save)
  • Use when the two states are clearly opposite (On / Off, Enabled / Disabled)
  • Always pair with a label that describes the setting being toggled, not just the state
❌ Don't
  • Use when the change requires a Save or Submit action — use Checkbox instead
  • Use for selecting between options in a form — use Radio Button instead
  • Use for multi-select — use Checkbox instead
  • Use without a label — a bare toggle with no description is inaccessible
  • Use more than one toggle to control the same setting

DigiLawyer Patterns

No additional product-specific patterns for Toggle Switch. The same label-positioning rule from Checkbox applies — toggle always appears on the left, label text always to the right.

Anatomy

A Toggle Switch is composed of a hidden native checkbox, a pill-shaped track, a sliding thumb, and a label. The thumb translates left↔right via CSS transform when :checked — no JS required.

  1. 1Hidden input.toggle-input. Visually hidden but focusable. Drives all states via CSS :checked, :disabled, :focus-visible using the ~ sibling selector.
  2. 2Track.toggle-track. The pill-shaped background. Colour changes when checked.
  3. 3Thumb.toggle-thumb. The white circle that slides left (off) or right (on) via transform: translateX.
  4. 4Label.toggle-label. Always placed to the right of the track. Describes the setting, not just the state.

Default

Wrap a native checkbox input + a styled pill in a <label class="toggle">. The input is visually hidden but stays in the focus path; the .toggle-track holds the colored pill and the .toggle-thumb inside translates left↔right when :checked.

HTML
<label class="toggle">
  <input type="checkbox" class="toggle-input">
  <span class="toggle-track"><span class="toggle-thumb"></span></span>
  <span class="toggle-label">Email notifications</span>
</label>

Variants

Two visual states. Off shows the thumb on the left over a translucent gray pill; On slides the thumb to the right and fills the pill with the heading colour. Use a toggle (not a checkbox) when the state should take effect immediately, without a form submit.

Sizes

Three sizes — .toggle-sm (28×16), default .toggle (36×20), .toggle-lg (44×24). Track height, thumb size, and slide distance all scale together via CSS custom properties; same border-radius (full pill) across all sizes.

States

Three interaction states. Hover and focus are CSS-driven (real interaction needed, or use the .is-hover doc helper to force the hover visual). Disabled uses the HTML disabled attribute on the input — both off and on disabled states use the same neutral grey, intentional in the Figma source.

Full matrix

3 sizes × 3 states × 2 variants = 18 cells, mirroring the Figma source. Each cell stacks Off above On. Default row is interactive; Hover row uses the .is-hover documentation helper to force the hover visual at rest; Disabled row uses the native disabled attribute.

Default
Hover
Disabled
Small
Medium
Large

Accessibility

Built on a native <input type="checkbox"> — all ARIA semantics are implicit. The checked state maps directly to on/off.

Attribute / BehaviourValueNotes
Roleswitch (or checkbox)Implicit from native <input type="checkbox">. Add role="switch" explicitly when semantics matter to assistive tech
Keyboard — toggleSpaceToggles the switch on or off
Keyboard — focusTab / Shift+TabMoves focus to and from the toggle
aria-checkedtrue / falseCommunicated automatically via native checked property
disabledHTML booleanRemoves from tab order and blocks interaction
LabelVisible .toggle-labelNever use aria-label as a substitute for a visible label — the setting must be readable without a screen reader

API

Class / attrOnDescription
.toggle<label>Wrapper. Makes the whole row a click/tap target for the input inside. Owns the size CSS custom properties.
.toggle-sm.toggleModifier — small size (28×16 track, 12px thumb).
.toggle-lg.toggleModifier — large size (44×24 track, 20px thumb). Default (no modifier) is medium (36×20 track, 16px thumb).
.toggle-input<input type="checkbox">The native input, visually hidden but accessible and focusable. Drives the visual via :checked / :disabled.
.toggle-track<span>The visible pill background. Inherits size from the wrapper's custom properties.
.toggle-thumb<span>The circular knob inside the track. Translates left↔right based on :checked.
.toggle-label<span>The text label. Inherits muted color + reduced opacity when the input is disabled.
checked<input>HTML attribute — toggle starts in the "on" state.
disabled<input>HTML attribute — disables interaction; both off and on disabled states use the same neutral fill per the Figma source.
.is-hover.toggleDocumentation helper — forces the hover visual at rest. Used in the Full matrix; not needed in production.