Skip to main content

Switch

A toggle control for enabling or disabling a binary setting with immediate effect. Use in settings panels and preference controls where the change applies without form submission. Don't use when the action needs a confirmation step.

OverviewStyleAccessibility

Description

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

PartRequired?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

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 24 / 32 / 40 / 48 / 64px (XS to XL). --orbit-switch-size-*-track-width
Track height 14 / 16 / 20 / 24 / 32px. --orbit-switch-size-*-track-height
Track border radius Full pill. --orbit-switch-radius
Thumb diameter 8 / 10 / 12 / 16 / 20px. --orbit-switch-size-*-thumb
Thumb inset 3 / 3 / 4 / 4 / 6px, centred in the track height
Hit area 44×44px minimum (extend via padding)
Gap (track to label) --op-space-12
Gap (label to description) --op-space-4
Thumb colour --orbit-switch-thumb-off (off), --orbit-switch-thumb-on (on)
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] with role="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 onChange handler that fires an async request directly. Switches imply immediacy — a form submit flow breaks that expectation.

Keyboard interaction

KeyAction
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 attributeWhen to useExample
role="switch" Applied to the input or button. Identifies the control as a switch, not a checkbox. <button role="switch" aria-checked="false">Notifications</button>
aria-checked Applied to a button-based switch. "true" when on, "false" when off. Not needed on checkbox-based switches — the checked property serves this role. <button role="switch" aria-checked="true">Notifications</button>
aria-label or &lt;label&gt; Applied to the switch. Always associate a visible label. Don't rely on icon-only switches without an accessible name. <label for="notify-switch">Notifications</label><button id="notify-switch" role="switch" aria-checked="false"></button>
aria-describedby Applied to the switch. Points to description text when present. <button role="switch" aria-describedby="notify-hint"></button><span id="notify-hint">Get notified when a submission is received.</span>
aria-busy Applied to the switch. "true" during async operations. Remove when complete. <button role="switch" aria-busy="true"></button>

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.

Was this page helpful?

Updated 2 October 2026