( Robust / WCAG 4.1.2 )
ARIA toggle field has no accessible name
What is this issue?
An element carrying role="checkbox", role="radio", or role="switch" has no accessible name, the text a screen reader announces to identify an element. It comes from the first source that applies, in order: aria-labelledby, aria-label, visible label text, then other fallbacks specific to the element type.
These three roles all expose a current state through aria-checked (true, false, or mixed for checkboxes). The state announces correctly on its own (“checked” or “not checked”), but without a name, the user has no way to connect that state to a specific setting.
Why does this matter?
A toggle exists to change one specific thing (dark mode, email notifications, a filter) and its whole value depends on the user knowing exactly what they’re flipping. “Checked” with no name attached is a fact with no subject; the user learns something changed, but not what.
This is a particularly common failure on custom-styled switches, since the visual redesign from a native checkbox to a pill-shaped toggle usually means swapping <input type="checkbox"> for a div with role="switch", and that swap drops the native <label> association silently unless it’s deliberately rebuilt. A settings screen full of unlabelled switches turns every toggle into a guess.
Who is affected?
- Screen reader users: hear only “checked” or “not checked” with no indication of which setting is affected, especially damaging on a settings page with several toggles in a row.
- Voice control users: operate a toggle by speaking its accessible name, such as “click Dark mode.” With no name, the toggle can’t be targeted by voice at all, regardless of its visible label text.
- Cognitive disabilities: users relying on a clear name to confirm they’re changing the intended setting lose that confirmation, increasing the risk of toggling the wrong option and having to retrace which change did what.
What users experience
Sana uses TalkBack on her Android phone to adjust notification settings in a banking app. The app renders each toggle as a custom div with role="switch" and no aria-label, styled to look like an iOS-style pill switch. TalkBack announces “switch, off” as she moves through five settings in a row (transaction alerts, marketing emails, biometric login, and two more) with every single one sounding identical, leaving her unable to tell which switch controls which setting without asking someone to read the screen to her.
How do I fix it?
Add aria-label naming the setting the toggle controls, such as aria-label="Dark mode". This works because the name and the state are announced together: “Dark mode, switch, off”, giving the full picture in one announcement instead of a bare state with no subject.
When visible text already labels the setting (a row in a settings list showing “Dark mode” next to the switch), use aria-labelledby pointing at that text’s id instead of aria-label. Point it at the static setting name, not any text that changes with the toggle’s state, so the name doesn’t shift unexpectedly every time the user flips it.
Where the design allows it, keep the underlying control a native <input type="checkbox"> or type="radio"> wrapped in a <label>, styled with CSS to look like a switch. This preserves the native label mechanism, keyboard support, and form submission behavior, and needs no ARIA role at all.
Code Examples
<div role="switch" aria-checked="true"></div><!-- Method 1: aria-label naming the setting -->
<div role="switch" aria-checked="true" tabindex="0" aria-label="Dark mode"></div>
<!-- Method 2: aria-labelledby pointing at a static setting name -->
<span id="dark-mode-label">Dark mode</span>
<div role="switch" aria-checked="true" tabindex="0" aria-labelledby="dark-mode-label"></div>
<!-- Method 3: styled native checkbox (no ARIA role required) -->
<label class="switch-style">
Dark mode
<input type="checkbox" checked>
</label>Methods 1 and 2 give the custom role="switch" element the name it’s missing, so the announced state has a subject attached. Method 3 sidesteps the problem entirely: a native checkbox styled with CSS to look like a switch keeps its accessible name from the <label> for free, with no ARIA needed.
Framework Examples
A reusable Switch component is the most common source of this rule’s failures in component-driven UIs, since one component definition without a required label prop produces every unlabelled toggle on a settings page at once. Make the label a required prop tied directly to aria-label, so a missing name is caught before the component ships.
function Switch({ label, checked, onChange }) {
return (
<div
role="switch"
aria-checked={checked}
aria-label={label}
tabindex="0"
onClick={() => onChange(!checked)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') onChange(!checked);
}}
>
<span aria-hidden="true" class={checked ? 'thumb thumb--on' : 'thumb'} />
</div>
);
}
// TypeScript: label: string (required) - a caller cannot render
// <Switch checked={x} onChange={y} /> without also passing a name.
The framework-specific value here is enforcing the label at the component’s interface level, not the markup itself; the JSX would look identical without it, but a required prop turns a missing name into a build error instead of a silent accessibility failure discovered later in an audit.
Common Mistakes
Mistake: “The setting row has visible text, so the switch inside it is labelled.” Visible text sitting in the same row as a switch does nothing for the accessible name unless it’s connected with aria-labelledby or the switch is wrapped in a <label>. A screen reader reads the DOM’s programmatic structure, not the visual layout of a settings row.
Mistake: “aria-checked already tells the user what’s happening.” aria-checked only communicates the current state (true/false/mixed); it never supplies a name. “Checked” with no name is a complete but meaningless announcement; the state and the name are two separate, both-required pieces of information.
Mistake: “Labelling the switch with its current on/off text keeps it accurate.” Pointing aria-labelledby at text that changes with the toggle’s state (like “Dark mode: On”) means the accessible name itself changes every time the user flips the switch, which some screen readers announce as a confusing name change rather than a state change. Label the static setting name only; let aria-checked handle the state.
How RedFlag Detects This
Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s aria-toggle-field-name rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule selects every element with role checkbox, radio, or switch and checks whether the accessible name computation resolves to non-empty text.
False negative: axe-core confirms a name exists but can’t judge whether it’s accurate. A switch labelled aria-label="Toggle" or aria-label="Setting" passes automatically even though it doesn’t say which setting. False positive: none typical for this check, since resolving to a non-empty accessible name is a binary condition. Manual step: read the announced name for each toggle and confirm it names the actual setting, not a generic placeholder.
Manual Testing
- Open the page in Chrome or Firefox with NVDA or JAWS running.
- Tab through every checkbox, radio button, and switch on the page, especially custom-styled toggles on a settings screen.
- Listen to what’s announced; it should include both a specific setting name and the state (“Dark mode, switch, off”), not the state alone (“switch, off”).
- On Android, repeat the pass with TalkBack, since custom switch components are especially common in mobile-first settings screens.
- If a group of related radio buttons is involved, confirm each option announces its own distinct name in addition to the shared group name.
Related WCAG Success Criteria
4.1.2 Name, Role, Value: Every interface component must expose a name, role, and current value to assistive technology. A toggle with no accessible name has a role and, through aria-checked, a value, but no name, which is exactly the gap this criterion requires closing.
1.3.1 Info and Relationships: Information and relationships conveyed through presentation must also be available programmatically. A setting name that’s visually next to its switch but not connected with aria-labelledby is this exact failure: the relationship is obvious on screen and absent in the code.
Related Issues
ARIA command has no accessible name covers the same missing-name failure on action-triggering roles instead of state-toggling ones: the same fix pattern, a different category of ARIA role.
ARIA input field has no accessible name is the equivalent failure for textbox, combobox, and searchbox roles, which accept typed or selected values instead of toggling a binary state.
Form input has no label is the native-HTML twin of this rule: the identical problem on a real <input type="checkbox"> or type="radio"> instead of a custom ARIA role.
ARIA attribute is required but missing is worth checking on the same element, since a valid checkbox or switch role also requires aria-checked to be present, independent of whether it has a name.
References
- W3C Understanding 4.1.2: Name, Role, Value
- ARIA Authoring Practices: Switch Pattern
- MDN: ARIA switch role
Frequently asked questions
What roles does this rule cover?
checkbox, radio, and switch, the three ARIA roles that represent a two-state (or, for radio, mutually exclusive multi-state) toggle. All three expose their current state through aria-checked and all three need an accessible name for that state to mean anything.
Is aria-checked enough on its own without a name?
No. aria-checked only communicates true, false, or mixed for the current state; it says nothing about what setting is being turned on or off. The accessible name is what supplies that missing context, and both are required for the toggle to be usable.
Does switching from a native checkbox input to a styled ARIA switch lose the label automatically?
Only if the label connection is not rebuilt during the switch. A native input inside a label element keeps its name for free; the moment a team replaces it with a div role="switch" for custom styling, that native association is gone and has to be rebuilt with aria-label or aria-labelledby by hand.
Can one label serve both a switch and its visible on/off text, like Dark mode: On?
Yes, using aria-labelledby pointing at the setting name text (Dark mode), not the changing on/off state text, since the state itself is already announced separately through aria-checked. Labelling with the full changing string risks the name updating unexpectedly every time the toggle flips.
Does a visually-hidden label work the same way for a custom toggle as for a native checkbox?
Yes. A CSS visually-hidden technique that keeps text in the accessibility tree while hiding it on screen works identically whether the label is connected to a native input or referenced with aria-labelledby on a custom role. Never use display:none or visibility:hidden, since both remove the text from the accessibility tree too.