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-labeloraria-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-onlytext).
- The accessible name begins with the visible label text, and may append a short amount of additional context for screen reader users (via
- 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 nativecheckedproperty when the switch is aninput[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/buttonwithrole="switch") must includetabIndex="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.
- Exception: If using native input
- If multiple switches are presented as a labeled set, group them with
fieldset+legendorrole="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-describedbyon the switch element itself. Do not placearia-describedbyon afieldsetor 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 thelegend. - 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-checkedwhen usingdivorbuttonwithrole="switch". - Do not use both
checkedandaria-checkedoninput[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.
- A native HTML switch control (
- 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.
"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/legendorrole="group". - Additional descriptive text is announced when associated via
aria-describedby.