Toggle Switch
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
- 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
- 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.
- 1Hidden input —
.toggle-input. Visually hidden but focusable. Drives all states via CSS:checked,:disabled,:focus-visibleusing the~sibling selector. - 2Track —
.toggle-track. The pill-shaped background. Colour changes when checked. - 3Thumb —
.toggle-thumb. The white circle that slides left (off) or right (on) viatransform: translateX. - 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.
<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.
Accessibility
Built on a native <input type="checkbox"> — all ARIA semantics are implicit. The checked state maps directly to on/off.
| Attribute / Behaviour | Value | Notes |
|---|---|---|
| Role | switch (or checkbox) | Implicit from native <input type="checkbox">. Add role="switch" explicitly when semantics matter to assistive tech |
| Keyboard — toggle | Space | Toggles the switch on or off |
| Keyboard — focus | Tab / Shift+Tab | Moves focus to and from the toggle |
aria-checked | true / false | Communicated automatically via native checked property |
disabled | HTML boolean | Removes from tab order and blocks interaction |
| Label | Visible .toggle-label | Never use aria-label as a substitute for a visible label — the setting must be readable without a screen reader |
API
| Class / attr | On | Description |
|---|---|---|
| .toggle | <label> | Wrapper. Makes the whole row a click/tap target for the input inside. Owns the size CSS custom properties. |
| .toggle-sm | .toggle | Modifier — small size (28×16 track, 12px thumb). |
| .toggle-lg | .toggle | Modifier — 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 | .toggle | Documentation helper — forces the hover visual at rest. Used in the Full matrix; not needed in production. |