switch
A type of checkbox that represents on/off values, as opposed to checked/unchecked values. Switches have immediate effect when toggled, unlike checkboxes which typically require form submission.
Overview
The switch role is a specialized form of checkbox that represents an on/off toggle with immediate effect. Unlike checkboxes, switches don't support a mixed/indeterminate state - they are either on (aria-checked="true") or off (aria-checked="false").
Switches are commonly used for settings that take effect immediately, such as enabling/disabling features, toggling dark mode, or controlling notifications.
Switch vs Checkbox
Use a switch when the action takes immediate effect (like toggling dark mode). Use a checkbox when the selection will be submitted later (like agreeing to terms). Switches communicate "on/off" while checkboxes communicate "checked/unchecked".
Live Demo: Switch Controls
Use dark theme throughout the app
Receive push notifications
Save your work automatically
Connected
Keyboard support: Focus any switch, then press Space or Enter to toggle. Use Tab to move between switches.
Code Examples
Basic Switch
<!-- Basic Switch -->
<div class="switch-container">
<span id="wifi-label">Wi-Fi</span>
<button
role="switch"
aria-checked="true"
aria-labelledby="wifi-label"
>
<span class="switch-track">
<span class="switch-thumb"></span>
</span>
</button>
</div>
<!-- aria-checked="true" means ON
aria-checked="false" means OFF -->Switch with Button Element
<!-- Switch Using Native Button -->
<button
role="switch"
aria-checked="false"
aria-label="Enable notifications"
class="switch"
>
<span class="switch-track">
<span class="switch-thumb"></span>
</span>
<span class="switch-label">Notifications</span>
</button>
<script>
const switchButton = document.querySelector('[role="switch"]');
switchButton.addEventListener('click', () => {
const isChecked = switchButton.getAttribute('aria-checked') === 'true';
switchButton.setAttribute('aria-checked', !isChecked);
});
// Keyboard support is automatic with <button>
</script>
<style>
.switch {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
background: none;
border: none;
padding: 0;
}
.switch-track {
width: 48px;
height: 28px;
background: #ccc;
border-radius: 14px;
position: relative;
transition: background 0.2s;
}
[aria-checked="true"] .switch-track {
background: #4f46e5;
}
.switch-thumb {
width: 24px;
height: 24px;
background: white;
border-radius: 50%;
position: absolute;
top: 2px;
left: 2px;
transition: left 0.2s;
}
[aria-checked="true"] .switch-thumb {
left: 22px;
}
</style>Switch vs Checkbox
<!-- Switch vs Checkbox: When to Use Each -->
<!-- Use CHECKBOX when:
- Selecting multiple independent options
- Form submission with on/off values
- The state isn't immediately applied -->
<label>
<input type="checkbox" name="terms" />
I agree to the terms
</label>
<!-- Use SWITCH when:
- The setting takes effect immediately
- It's a binary on/off toggle
- It controls a system/app state -->
<button role="switch" aria-checked="true" aria-label="Dark mode">
<span class="switch-track">
<span class="switch-thumb"></span>
</span>
</button>
<!-- Key difference:
Switches have IMMEDIATE effect
Checkboxes typically require form submission -->Switch with Description
<!-- Switch with Description -->
<div class="setting-row">
<div class="setting-info">
<span id="autosave-label" class="setting-name">Auto-save</span>
<span id="autosave-desc" class="setting-description">
Automatically save your work every 5 minutes
</span>
</div>
<button
role="switch"
aria-checked="false"
aria-labelledby="autosave-label"
aria-describedby="autosave-desc"
class="switch"
>
<span class="switch-track">
<span class="switch-thumb"></span>
</span>
</button>
</div>
<!-- aria-describedby provides additional context
for screen reader users -->Grouped Switches
<!-- Group of Related Switches -->
<fieldset>
<legend>Notification Settings</legend>
<div class="switch-group">
<div class="switch-row">
<label id="email-label">Email notifications</label>
<button
role="switch"
aria-checked="true"
aria-labelledby="email-label"
></button>
</div>
<div class="switch-row">
<label id="push-label">Push notifications</label>
<button
role="switch"
aria-checked="false"
aria-labelledby="push-label"
></button>
</div>
<div class="switch-row">
<label id="sms-label">SMS notifications</label>
<button
role="switch"
aria-checked="false"
aria-labelledby="sms-label"
></button>
</div>
</div>
</fieldset>
<!-- Use fieldset/legend to group related switches -->React Component
// React Switch Component
import { useState, useCallback } from 'react';
interface SwitchProps {
label: string;
description?: string;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
}
function Switch({
label,
description,
checked,
onChange,
disabled = false,
}: SwitchProps) {
const handleClick = useCallback(() => {
if (!disabled) {
onChange(!checked);
}
}, [checked, disabled, onChange]);
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
handleClick();
}
}, [handleClick]);
const labelId = `switch-label-${label.toLowerCase().replace(/\s+/g, '-')}`;
const descId = description
? `switch-desc-${label.toLowerCase().replace(/\s+/g, '-')}`
: undefined;
return (
<div className="switch-container">
<div className="switch-info">
<span id={labelId} className="switch-label">{label}</span>
{description && (
<span id={descId} className="switch-description">
{description}
</span>
)}
</div>
<button
type="button"
role="switch"
aria-checked={checked}
aria-labelledby={labelId}
aria-describedby={descId}
aria-disabled={disabled}
onClick={handleClick}
onKeyDown={handleKeyDown}
className={`switch ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''}`}
>
<span className="switch-track">
<span className="switch-thumb" />
</span>
</button>
</div>
);
}
// Usage
function Settings() {
const [darkMode, setDarkMode] = useState(false);
const [notifications, setNotifications] = useState(true);
return (
<div className="settings">
<Switch
label="Dark Mode"
description="Use dark theme throughout the app"
checked={darkMode}
onChange={setDarkMode}
/>
<Switch
label="Notifications"
description="Receive push notifications"
checked={notifications}
onChange={setNotifications}
/>
</div>
);
}Disabled Switch
<!-- Disabled Switch -->
<div class="switch-container">
<span id="premium-label">Premium features</span>
<span id="premium-desc" class="subdued">
Upgrade to enable premium features
</span>
<button
role="switch"
aria-checked="false"
aria-labelledby="premium-label"
aria-describedby="premium-desc"
aria-disabled="true"
disabled
class="switch disabled"
>
<span class="switch-track">
<span class="switch-thumb"></span>
</span>
</button>
</div>
<style>
.switch.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.switch.disabled .switch-track {
background: #ddd;
}
</style>
<!-- Use both aria-disabled and native disabled
for complete accessibility -->Keyboard Support
Primary toggle key
Alternative toggle key (when using button element)
Standard navigation
Reverse navigation
Best Practices
Use switches for settings that take immediate effect
Use a <button> element as the base for automatic keyboard support
Provide clear labels that describe what the switch controls
Include aria-describedby for additional context when helpful
Ensure sufficient color contrast between on/off states
Consider adding icons or text to indicate on/off state for colorblind users
Don't use switches when the action requires form submission
Don't use aria-checked="mixed" - switches are binary only
Don't rely solely on color to indicate state
Don't use a switch when multiple related options exist - use checkboxes
Supported ARIA Attributes
aria-checkedRequiredRequired. "true" for on, "false" for off (no "mixed")
aria-labelAccessible name for the switch
aria-labelledbyReferences element(s) that label the switch
aria-describedbyReferences additional description
aria-disabledIndicates if the switch is disabled
aria-readonlyIndicates if the switch is read-only
Common Use Cases
Accessibility Notes
Screen Reader Announcements
Screen readers announce switches as "[label], switch, [on/off]". When toggled, the new state is announced. NVDA and JAWS specifically announce "on" or "off" rather than "checked" or "not checked" used for checkboxes.
Visual State Indication
Don't rely solely on color to indicate state. The switch thumb position provides a clear visual cue. Consider adding icons (like ✓/✗) or text labels (ON/OFF) to further clarify state for users with color vision deficiencies.
Immediate vs Deferred Actions
Switches should have immediate effect. If your toggle requires a "Save" action, use a checkbox instead. The switch pattern sets user expectations that the change happens instantly.

