How to Name Toggle Fields for Accessibility: Complete Guide (2026)

Saar Twito9 min read
Saar Twito
Saar TwitoFounder & SEO Engineer

Hi, I'm Saar - a software engineer, SEO specialist, and lecturer who loves building tools and teaching tech.

View author profile →

What Is an Accessible Toggle Field?

A toggle field is any UI control with two states — on/off, expanded/collapsed, pressed/unpressed. To be accessible it needs three things: an accessible name (what it controls), a role (button, switch, or disclosure), and a programmatic state (aria-pressed, aria-checked, or aria-expanded) that updates whenever the user flips it.

Key Facts (TL;DR)

  • WCAG criterion: WCAG 2.2 SC 4.1.2 Name, Role, Value (Level A) — every UI component must expose its name, role, and current state programmatically.
  • Three required pieces: Name (what it controls), Role (button / switch / disclosure / tab), and State (aria-pressed, aria-checked, or aria-expanded).
  • Most common failure: A visually obvious switch with no role and no state — looks fine, but a screen reader announces only "button" or nothing at all.
  • Switch vs. button: Use role="switch" with aria-checked for on/off settings. Use a plain <button> with aria-pressed for two-state actions like "Like" or "Mute."
  • Disclosure pattern: Accordions and "Show more" controls use aria-expanded on a real <button>, paired with aria-controls pointing at the panel.
  • Legal exposure: WCAG 2.2 Level AA conformance is the legal benchmark under the European Accessibility Act (EAA, in force June 2025), the ADA, and Canada's AODA. Unlabeled toggles are a frequent finding in accessibility lawsuits.
  • Voice control breaks too: Users say "click dark mode" — without an accessible name, the voice command has nothing to match.

Think of a toggle like a light switch on a wall. If the switch has no label, you don't know what it controls. If you can't feel whether it's up or down, you don't know if the light is on. Sighted users get both pieces "for free" from visual styling — assistive technology only gets them if you wire them up explicitly.

Why Mislabeled Toggles Break the Experience

A toggle without a name and a state is silently broken — the visuals look correct, but anyone not using a sighted pointer is locked out.

  • Screen readers:Without state, a toggle announces only "button" — the user has no idea whether it's currently on. Without a name, they have no idea what it controls.
  • Voice control: Dragon, Voice Control, and Voice Access all match commands against the accessible name. A toggle named only by an icon cannot be invoked by voice.
  • Keyboard users: A <div> styled like a switch is unreachable by Tab and unresponsive to Space/Enter. Native <button> handles all of that automatically.
  • Cognitive load:Users with cognitive disabilities rely on confirmation that an action took effect. A toggle whose state isn't announced after activation forces them to guess.
  • Legal risk: Toggle controls without a name or state are a recurring pattern in EAA, ADA, and AODA complaints. The fix is usually a single attribute — far cheaper than litigation.

The Three Required Pieces of Information

WCAG 2.2 SC 4.1.2 requires every interactive control to expose a name, a role, and a current value/state. For a toggle, those map to three concrete things:

1. Name — What the Toggle Controls

The accessible name is whatever the control is for: "Dark mode", "Email notifications", "Mute microphone". It can come from visible text inside the button, an aria-label, or an aria-labelledby reference. A toggle whose only content is an icon needs an explicit aria-label.

2. Role — What Kind of Control This Is

Pick the role that matches the pattern: a plain <button> with aria-pressed for two-state actions, role="switch" with aria-checked for settings-style on/off toggles, aria-expanded on a button for disclosures and accordions, and role="tab" with aria-selected for tabs in a tablist.

3. State — Whether It's On Right Now

The state attribute (aria-pressed, aria-checked, or aria-expanded) must reflect the current value and update every time the user flips it. A static aria-pressed="false" that never changes is the same as having no state at all.

How to Check Your Toggles

Detection mixes automated scanning with a quick screen-reader pass — automation catches missing names and roles, manual testing catches state that doesn't update.

  • Greadme deep scan — flags toggle controls that are missing an accessible name, are missing a role, or have a state attribute that never changes, and pairs each finding with an AI-generated fix or a one-click GitHub PR.
  • Greadme crawler scan — runs the same checks across every indexable page so you catch the same broken toggle pattern in headers, settings panels, and product pages in one pass.
  • Greadme AI visibility analyzer — checks how generative search systems describe your interface; missing toggle names often surface as missing or generic feature descriptions.
  • Screen reader (NVDA, JAWS, VoiceOver, TalkBack) — Tab to the toggle, listen for "[name], [role], [state]". If any of the three is missing, it's broken.
  • Keyboard only — confirm Tab reaches the toggle, Space/Enter activates it, and the visual state matches what assistive tech announces.
  • Chrome DevTools → Accessibility pane — inspect the element to see the computed name, role, and state in the accessibility tree.

Code Examples: Bad vs. Good Toggles

Bad — Div Styled as a Switch

No role, no name, no keyboard support, no state. Looks fine to sighted mouse users; invisible to everything else.

<!-- Bad: not a real control at all -->
<div class="switch" onclick="toggleDarkMode()">
  <span class="thumb"></span>
</div>

Good — Switch With Name, Role, and State

A real <button> with role="switch", an explicit name, and aria-checked that updates on every click.

<!-- Good: name + role + state -->
<button
  type="button"
  role="switch"
  aria-checked="false"
  id="dark-mode-toggle"
>
  Dark mode
</button>

<script>
  const btn = document.getElementById('dark-mode-toggle');
  btn.addEventListener('click', () => {
    const on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', String(!on));
  });
</script>

Good — Two-State Action Button (aria-pressed)

For actions like "Like," "Bookmark," or "Mute," use aria-pressed on a normal button. The label stays the same; the state attribute changes.

<button type="button" aria-pressed="false" aria-label="Like this post">
  <svg aria-hidden="true" focusable="false">...</svg>
</button>

Good — Disclosure / Accordion (aria-expanded)

Accordions and "Show more" controls use aria-expanded with aria-controls pointing at the panel they reveal.

<button
  type="button"
  aria-expanded="false"
  aria-controls="billing-panel"
  id="billing-trigger"
>
  Billing details
</button>
<div id="billing-panel" role="region" aria-labelledby="billing-trigger" hidden>
  ...
</div>

Good — React Pattern (state stays in sync)

The state attribute is derived from component state, so it cannot drift out of sync with the visual.

function DarkModeSwitch() {
  const [on, setOn] = useState(false);
  return (
    <button
      type="button"
      role="switch"
      aria-checked={on}
      onClick={() => setOn(v => !v)}
    >
      Dark mode
    </button>
  );
}

Common Toggle Mistakes and How to Fix Them

Problem: Div or Span Used as a Switch

What's happening: A <div> with click handlers and styled like a switch has no role, no name, and no keyboard support. Tab skips over it; Space and Enter do nothing.

Fix: Replace it with a real <button>. Add role="switch" and aria-checked for settings, or aria-pressed for action toggles. The browser handles focus, keyboard activation, and the "button" role automatically.

Problem: Toggle Has a Name but No State

What's happening: Screen readers announce "Dark mode, button" but never say "on" or "off." Users have to guess the current state.

Fix: Add aria-pressed (action button) or aria-checked with role="switch" (settings toggle), and update it whenever the value changes.

Problem: aria-pressed Never Updates

What's happening: The attribute is set in the markup but never re-set in the click handler. Assistive tech announces a state that's wrong half the time — worse than no state at all.

Fix: Bind the attribute to component state in React/Vue, or call setAttribute('aria-pressed', ...) on every change in vanilla JS. Verify with a screen reader that activation announces the new state.

Problem: Icon-Only Toggle With No Accessible Name

What's happening: A button containing only an SVG bell icon has no text content, so the accessible name is empty. Voice control can't target it; screen readers announce only "button."

Fix: Add aria-label="Notifications" to the button (or visually hidden text), and mark the inner SVG with aria-hidden="true" so it doesn't leak into the name.

Which State Attribute to Use Where

The most common source of confusion is mixing up aria-pressed, aria-checked, and aria-expanded. Each maps to a specific UI pattern.

PatternRoleState AttributeUse For
Two-state buttonbutton (default)aria-pressedLike, Bookmark, Mute, Bold/Italic in a toolbar
Settings switchswitcharia-checkedDark mode, email notifications, on/off preferences
Checkboxcheckbox (native)checked / aria-checkedMulti-select form fields, "I agree" consent
Disclosure / accordionbuttonaria-expanded + aria-controlsShow more, FAQ accordion, collapsible section
Tab in a tablisttabaria-selectedTabbed interface (Account / Billing / Security)

FAQ

What's the difference between aria-pressed and aria-checked?

aria-pressed is for two-state action buttons — toolbar buttons, "Like," "Mute" — where activating the button performs an action and its "pressed" appearance is visual feedback. aria-checked is for switches and checkboxes — settings whose value is the state itself. If the toggle represents a setting ("Dark mode is on"), use role="switch" with aria-checked. If it triggers a one-shot action with a sticky pressed look, use aria-pressed.

Do I need role="switch" or is aria-pressed enough?

Both are valid. role="switch" with aria-checked tends to be announced as "switch, on/off," which matches mobile users' mental model best. aria-pressedis announced as "button, pressed/not pressed." Pick one pattern and apply it consistently — what matters is that the role and state are present.

Can I use a checkbox styled as a switch instead?

Yes — <input type="checkbox" role="switch"> is a common pattern. You get native keyboard support, native focus styles, and form submission for free, and the switchrole tells screen readers to announce "on/off" instead of "checked/not checked." Hide the native box visually and style a custom track and thumb on top.

What WCAG criterion does an unlabeled toggle violate?

WCAG 2.2 Success Criterion 4.1.2 Name, Role, Value (Level A). It requires every UI component to have a programmatically determinable name, role, and current state. A toggle missing any of those three fails 4.1.2 and, by extension, fails Level A — the lowest tier of WCAG conformance.

Does a visible label next to the switch count as the accessible name?

Only if it's wired up. Visible text near the control isn't automatically associated. Either put the label inside the <button>, point at it with aria-labelledby, or use a <label for="..."> when the toggle is a real <input type="checkbox">. Inspect the element in Chrome DevTools' Accessibility pane to confirm the computed name is what you expect.

How do AI search engines like ChatGPT and Perplexity see toggle controls?

Generative search systems and AI agents that browse the web increasingly drive interactive flows on behalf of users. They rely on the same accessibility tree that screen readers use — names, roles, and states. A toggle with no name or state is invisible to those agents, which means the feature it controls effectively doesn't exist for AI-mediated visitors. Greadme's AI visibility analyzer surfaces which controls are unreadable to those systems.

Should I announce state changes with a live region?

Usually no. When the state attribute (aria-pressed, aria-checked, aria-expanded) updates on a focused element, screen readers announce the new state automatically. A live region on top of that produces double announcements. Reserve live regions for asynchronous results that aren't tied to the focused control — for example, "3 results loaded" appearing elsewhere on the page.

Conclusion

Accessible toggles come down to three attributes: a name that says what the control does, a role that matches the pattern (button, switch, disclosure, tab), and a state attribute that updates whenever the user flips it. Get all three right and the same control works for sighted users, screen reader users, voice-control users, and keyboard-only users — at the cost of one extra attribute on every toggle.

Run a Greadme deep scanto find every toggle on your site that's missing a name, a role, or a state, and ship the fix as a one-click GitHub PR.