Skip to Content
Component ExamplesToggle Controls

Toggle Controls (Switch, Toggle Button, Checkbox)

Three patterns for binary state — when to use each, and how screen readers announce them differently.

Maintained by Martin KrugerLast reviewed

Overview

Toggle controls let users switch between two states — on/off, mute/unmute, dark/light. There are three distinct patterns: switches (role="switch") for settings that take immediate effect, toggle buttons (aria-pressed) for actions that can be toggled, and checkboxes for form values submitted later. Each announces differently to screen readers, and choosing the wrong pattern confuses assistive technology users about what will happen when they interact.

WCAG Criteria:

Key requirements:

  • Use role="switch" with aria-checked for settings that take immediate effect (e.g., dark mode, notifications)
  • Use aria-pressed on a <button> for toggle actions (e.g., mute, bold, favorite)
  • Use <input type="checkbox"> for form values that are submitted later
  • The label NEVER changes when state changes — aria-checked or aria-pressed communicates the state
  • Space key toggles all three; switches and toggle buttons are single Tab stops
  • Always provide visible focus indicators

Switch vs. Div Toggle

Switch vs. Div Toggle

Inaccessible
Dark mode
View inaccessible code
<!-- Div toggle — no role, no state, no keyboard access --> <div onclick="toggleSwitch()"> <div class="track"><div class="thumb"></div></div> <span>Dark mode</span> </div>
Accessible
View accessible code
<!-- Semantic switch — role, state, and keyboard built in --> <button role="switch" aria-checked="false" onclick="let on = this.getAttribute('aria-checked') === 'true'; this.setAttribute('aria-checked', String(!on))"> <span class="track"><span class="thumb"></span></span> Dark mode </button>

What’s wrong with the div?

  • No switch role announced — screen readers see only static text
  • No aria-checked state — screen readers have no way to know if it’s on or off
  • Not focusable with the Tab key, so keyboard users cannot reach it
  • Cannot be toggled with Space or Enter
  • The visual animation is the only indication of state — invisible to assistive technology
  • onclick on a div only works with a mouse

Predicted semantic summary (exact output varies):

VersionAnnouncement
Inaccessible (<div>)“Dark mode” (just text, no role, no state)
Accessible (role="switch")“Dark mode, switch, off” / “Dark mode, switch, on”

Toggle Button

Toggle Button with aria-pressed

Inaccessible
View inaccessible code
<!-- Label changes on toggle — confusing for screen reader users --> <button onclick="this.textContent = this.textContent === 'Mute' ? 'Unmute' : 'Mute'"> Mute </button>
Accessible
View accessible code
<!-- Stable label + aria-pressed conveys state --> <button aria-pressed="false" onclick="let p = this.getAttribute('aria-pressed') === 'true'; this.setAttribute('aria-pressed', String(!p))"> Mute </button>

What’s wrong with changing the label?

  • Screen reader users hear “Mute, button” then “Unmute, button” — the label flip implies it could be a completely different button
  • There’s no programmatic state — the screen reader cannot tell the user whether mute is currently active or inactive
  • With aria-pressed, the label stays “Mute” and the state is clear: “not pressed” means mute is off, “pressed” means mute is on
  • Stable labels also help speech recognition users who need to say the button name to activate it — a changing name is a moving target

Predicted semantic summary (exact output varies):

VersionAnnouncement
Label-swapping button”Mute, button” then “Unmute, button” (which is the current state?)
Toggle button (aria-pressed)“Mute, toggle button, not pressed” / “Mute, toggle button, pressed”

When to Use Which Pattern

PatternElementState attributeUse when
Switch<button role="switch">aria-checkedSettings that take immediate effect (dark mode, notifications, Wi-Fi)
Toggle button<button aria-pressed>aria-pressedActions that can be toggled (mute, bold, favorite, pin)
Checkbox<input type="checkbox">checkedForm values submitted later (agree to terms, opt-in preferences)

Key differences:

  • Switch = immediate effect, no submit step. Screen readers announce “switch, on/off.”
  • Toggle button = an action with two states. Screen readers announce “toggle button, pressed/not pressed.”
  • Checkbox = deferred submission as part of a form. Screen readers announce “checkbox, checked/not checked.”
  • All three toggle with the Space key. Switches and toggle buttons also toggle with Enter.
  • The label always stays the same — the state attribute alone communicates the change.

That stable-label rule applies to stateful toggles. It does not apply to every two-way action: a disclosure button can keep a stable label and expose aria-expanded, while a Show password action can legitimately change to Hide password without aria-pressed. Choose one coherent model based on whether the control represents persistent state or the next action.


Resources