Description
Switch toggles a single setting between on and off. The change takes effect immediately — no form submission is required.
Settings panels, notification preferences, feature flags, and any binary control where the effect is immediate and reversible.
When a setting applies instantly (like a system toggle), a Switch gives users immediate, visible confirmation that their action had an effect. A checkbox in this context implies the change needs a Save button, which creates ambiguity.
Switch vs Checkbox
This distinction matters. Both look like binary controls — but they behave differently and communicate different things:
| Switch | Checkbox | |
|---|---|---|
| Effect timing | Immediate — change applies on toggle | Deferred — change applies on form submit |
| Context | Settings, preferences, system controls | Form fields, agreements, multi-select lists |
| Requires confirmation? | No — if yes, don't use Switch | Usually yes (form submit) or no (agreement) |
| Label describes | The setting name, not the action ("Email notifications", not "Turn on emails") | The option being selected |
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Track | Required | The pill-shaped background. Changes colour to indicate on (brand/green) or off (neutral grey). |
| Thumb | Required | The circular handle that slides left (off) or right (on). Its position communicates state at a glance. |
| Label | Required | Names the setting being controlled. Must describe the setting, not the action. "Email notifications" — not "Enable email notifications". |
| Description | Optional | A supplementary line explaining what the setting does or its implications. Use when the label alone may be ambiguous. |
States
| State | Behaviour |
|---|---|
| Off | Grey track, thumb at left. The setting is disabled / inactive. |
| On | Brand-colour track, thumb at right. The setting is enabled / active. |
| Disabled (off) | Dim grey track, reduced opacity. The setting cannot be changed. Communicate why nearby. |
| Disabled (on) | Dim brand track, reduced opacity. Setting is on and locked. Use when a higher-level setting forces it on. |
| Focus | Focus ring on track. 2px solid focus ring, --op-color-focus-ring, 2px offset. |
| Loading | Track dimmed, thumb animated. Used when toggling has a network delay. Provide an aria-busy="true" signal and restore when done. |
Usage guidelines
When to use
- A single setting can be turned on or off independently of other settings.
- The change takes effect immediately — no Save button is involved.
- The setting is at a system or preference level (not inside a transactional form).
When not to use
- When the action needs confirmation before it applies — show a confirmation dialog and use a different trigger.
- Inside a standard form that requires a Save action — use a Checkbox.
- When the two states have names other than on/off (e.g. "Allowed" / "Blocked") — use a Radio group or Dropdown.
Do / Don't
Do
Write labels as nouns, not imperatives. "Dark mode" and "Email notifications" — not "Enable dark mode" or "Turn on emails".
Don't
Don't use a Switch for an action that can't easily be undone. If turning something off permanently deletes data, use a Modal confirmation flow instead.
Do
Provide immediate visual feedback when a Switch is toggled. If there's a server call, show a loading state — don't let the UI appear broken while waiting.
Don't
Don't add "On" / "Off" text inside the track. The thumb position already communicates state visually; text inside a small track is illegible at standard sizes.
Layout & Spacing
| Element | Spec |
|---|---|
| Track width | 44px |
| Track height | 24px |
| Track border radius | 12px (full pill) |
| Thumb diameter | 20px |
| Thumb offset (off) | 2px from left edge |
| Thumb offset (on) | 2px from right edge |
| Hit area | 44×44px minimum (extend via padding) |
| Gap (track to label) | --op-space-12 |
| Gap (label to description) | --op-space-4 |
| Label font size | --op-text-sm, font-weight: 500 |
| Description font size | --op-text-xs |
| State | Track | Thumb |
|---|---|---|
| Off | --op-color-bg-disabled |
--op-color-bg-primary |
| On | --op-color-interactive-default |
--op-color-bg-primary |
| Disabled | --op-color-bg-disabled, 50% opacity |
--op-color-bg-primary, 50% opacity |
| Description text | --op-color-text-secondary |
— |
Engineering notes
- When using
input[type=checkbox]withrole="switch", the browser announces the state as "on" / "off" rather than "checked" / "unchecked". This is the correct language for a switch control. - Always handle the error case: if a network request fails after a toggle, revert the switch state and communicate what happened. Leaving the UI out of sync with the actual server state causes confusion and support issues.
- Don't submit a form to persist a switch change. Use an
onChangehandler that fires an async request directly. Switches imply immediacy — a form submit flow breaks that expectation.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Moves focus to the switch. |
| Space | Toggles the switch state. |
| Enter | Toggles the switch state (on button-based implementations). |
Why it matters
The role="switch" attribute is what makes the component meaningful to screen readers. Without it, a visually styled toggle announces itself as a plain checkbox, which implies it's part of a form awaiting submission — the opposite of a switch's immediate-effect semantics. Using the correct role ensures users with assistive technology understand that their preference has been applied, not queued.
Focus
When toggled, screen readers should announce the new state. With role="switch" and a properly associated label, this happens automatically:
- Turning on: "[Label] — switch, on"
- Turning off: "[Label] — switch, off"
If the toggle triggers a server call and there's a delay, use aria-busy="true" on the switch during the request. When complete, remove it so the state change is re-announced.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
| ||
| ||
| ||
| ||
|
Watch out
Don't add a visible "On" / "Off" label next to the switch that changes on toggle without also updating the programmatic state. If the visible label says "On" but aria-checked still says false, screen readers and sighted users receive contradictory information.
Things to avoid
- Don't omit
role="switch". A visually styled toggle that announces as a checkbox misleads screen reader users about how it works. - Don't use a switch for a setting that requires confirmation before applying. Use a modal confirmation pattern instead — a switch that triggers a dialog is confusing UX.
- Don't create icon-only switches without an accessible name. Every switch must have a label, even if it's visually hidden with a screen-reader-only utility class.