Skip to main content
Web / React · WCAG 2.2 AA

Switch

Two-state on/off control representing a persistent setting. Uses role="switch" with aria-checked, or native checkbox semantics when applicable.

Selection

The criteria an agent checks before retrieving this component.

Use when

  • Use when a control represents a persistent binary setting that remains on or off beyond the current interaction (e.g., "Enable notifications", "Dark mode").
  • Use when the setting takes effect immediately when toggled, without requiring form submission.
  • Use when the control reflects the current state of a system or application preference.

Try a different component when

  • Do not use when the control triggers an in-place action or transient feature toggle within the current context (use button.toggle).
  • Do not use when the choice is a value submitted with a form rather than a setting that takes effect immediately (use checkbox.basic).
  • Do not use when selecting one or more options from a set of related choices (use checkbox.group).
  • Do not use when more than two states are required (use button.toggle).

Must Haves

Non-negotiable structure. Every generated instance must satisfy these rules.

  • The switch has role="switch".
  • The switch has a visible text label, either programmatically associated with the control (label[for]) or matched by an accessible name on the control (aria-label or aria-labelledby).
    • The accessible name begins with the visible label text, and may append a short amount of additional context for screen reader users (via aria-label, aria-labelledby, or offscreen .sr-only text).
  • The accessible name describes the setting the switch controls, worded so it is true when the switch is on (e.g., "Enable notifications").
  • The switch's on/off state is exposed via aria-checked ("true"/"false"), or via the native checked property when the switch is an input[type="checkbox"].
    • The exposed state stays in sync with the switch as it toggles.
  • The switch is focusable so Tab and Shift+Tab reach it: a native input[type="checkbox"] is focusable by default; a non-native element (div/button with role="switch") must include tabIndex="0".
  • Keyboard:
    • Space toggles the switch.
    • Enter toggles the switch.
      • Exception: If using native input input[type="checkbox"], then only Space toggles the switch, not Enter.
  • If multiple switches are presented as a labeled set, group them with fieldset + legend or role="group" + aria-labelledby.
    • The group's label describes the purpose of the set (WCAG 2.4.6).
  • Associate any additional descriptive static text with the switch via aria-describedby on the switch element itself. Do not place aria-describedby on a fieldset or group container — a screen reader announces a description when its element receives focus, and the container is never focused, so it is announced unreliably; put group-level context in the legend.
  • Ensure a visible focus state (e.g., a 2px solid outline offset by 1-2px) around the switch control.

Donts

Avoid these accessibility and UX barriers.

  • Do not use a switch for non-setting actions; use it only for persistent on/off settings.
  • Do not omit aria-checked when using div or button with role="switch".
  • Do not use both checked and aria-checked on input[type="checkbox"].

Customizable

Alternatives and options that give the AI agent some room to move.

  • The base element: <div role="switch"> (as in the golden pattern), <button role="switch">, or <input type="checkbox" role="switch">. Native elements reduce the keyboard and focus wiring that must be hand-rolled.
    • A native HTML switch control (<input type="checkbox" switch>) is emerging in browsers (WebKit ships an implementation) and may become the preferred base once support is broad.
  • Whether the visual design resembles a sliding switch.
  • Whether the accessible name is contained within the switch or referenced externally.
  • Whether state text ("On"/"Off") is visually displayed.
  • Whether multiple switches may be grouped.

Golden Pattern

The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.

JSX
"use client";

export function SwitchDemo() {
const [notifications, setNotifications] = useState(false);

function toggle() {
setNotifications((v) => !v);
}

return (
<div>
{/*
This example uses a <div>.
The same pattern may also be implemented using:
- <button role="switch">, or
- <input type="checkbox" role="switch">
*/}

<div
role="switch"
aria-checked={notifications ? "true" : "false"}
tabIndex={0}
aria-labelledby="sw-label"
onClick={toggle}
onKeyDown={(e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
toggle();
}
}}
>
<span id="sw-label">Notifications</span>
<span aria-hidden="true">
{notifications ? "On" : "Off"}
</span>
</div>

<fieldset>
<legend>Playback Settings</legend>
<p id="playback-desc">
These settings apply to all videos.
</p>

<div
role="switch"
aria-checked="true"
tabIndex={0}
aria-labelledby="autoplay-label"
aria-describedby="playback-desc"
>
<span id="autoplay-label">Autoplay</span>
<span aria-hidden="true">On</span>
</div>

<div
role="switch"
aria-checked="false"
tabIndex={0}
aria-labelledby="captions-label"
aria-describedby="playback-desc"
>
<span id="captions-label">Always show captions</span>
<span aria-hidden="true">Off</span>
</div>
</fieldset>
</div>
);
}

Acceptance Checks

The component’s test spec — an optional body of checks for verification.

Keyboard

  • Tab moves focus to each switch.
  • Space toggles state.
  • Enter toggles state.
  • Focus remains on the switch after toggling.

Screen Reader

  • Switch is announced with its accessible name and role ("switch").
  • State is announced correctly as on/off.
  • Group label is announced when using fieldset/legend or role="group".
  • Additional descriptive text is announced when associated via aria-describedby.