4.1.2 Name, Role, Value
What it requires
For every user-interface component — links, buttons, form fields, and custom widgets such as menus, tabs, sliders, and toggles — the following must be exposed to assistive technology so it can be programmatically determined and, where the user can change it, set:
- Name — the accessible label that identifies the control (for example, “Search” on a button).
- Role — what kind of component it is (button, checkbox, tab, etc.).
- Value & states — the current value and any states or properties, such as checked, expanded, selected, or disabled, kept in sync as they change.
Native HTML elements provide name, role, and value for free. The criterion fails most
often when controls are built from generic <div> or
<span> elements without the ARIA roles, names, and states needed to
convey the same information.
- Screen reader users — hear an unlabeled or mis-typed control (“clickable” instead of “Submit, button”), so they cannot tell what it is or its state.
- Voice-control users — cannot target a control by name if it has no accessible name to say.
- Switch and other AT users — rely on role and state to understand and operate custom widgets.
How to detect it
| Check | How | Catches |
|---|---|---|
| Accessible name | Inspect each control in the browser accessibility tree; confirm a meaningful name. | Icon-only buttons, unlabeled inputs. |
| Correct role | Verify custom widgets expose a valid role, not a bare div/span. |
“Fake” buttons and links built from generic elements. |
| State sync | Toggle the control; confirm aria-expanded, aria-checked, etc. update. |
States that never change or are missing. |
| Screen reader pass | Navigate with NVDA/VoiceOver; listen for name + role + state on each control. | Most real-world failures, in context. |
| Automated scan | Run axe / Lighthouse for missing names and invalid ARIA. | Many name/ARIA issues; cannot judge wrong-but-present roles. |
How to fix it
- Use the native element first —
<button>,<a href>,<input>— so name, role, and value come built in. - Give every control an accessible name: visible text, a
<label>, oraria-label/aria-labelledbywhen no visible text exists. - When you must build a custom widget, add the correct ARIA
roleand follow the matching ARIA Authoring Practices pattern. - Expose and update state with the right ARIA attributes (
aria-expanded,aria-checked,aria-selected) whenever it changes. - Re-test with a screen reader to confirm name, role, and state are all announced.
<button type="button" aria-expanded="false" aria-controls="menu">
Account menu
</button>
<ul id="menu" hidden>
<li><a href="/profile">Profile</a></li>
</ul>
The native <button> supplies role and name; aria-expanded
carries the state and is updated in script as the menu opens and closes.
Copy-paste tests
Automated coverage
These axe-core rules flag failures of this criterion:
area-alt, aria-allowed-attr, aria-braille-equivalent,
aria-command-name, aria-conditional-attr, aria-deprecated-role,
aria-hidden-body, aria-hidden-focus, aria-input-field-name,
aria-prohibited-attr, aria-required-attr, aria-roledescription,
aria-roles, aria-tab-name, aria-toggle-field-name,
aria-tooltip-name, aria-valid-attr, aria-valid-attr-value,
button-name, duplicate-id-aria, frame-title,
frame-title-unique, input-button-name, input-image-alt,
label, link-name, nested-interactive, select-name,
summary-name. Run them via the axe DevTools browser extension or axe-core in CI.
Automated tools only catch some failures.
Run this in the browser console
// Read-only: surface interactive elements that may lack an accessible name.
const sel = 'button, a[href], input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="checkbox"], [role="switch"], summary, iframe';
const suspect = [...document.querySelectorAll(sel)].filter(el => {
const name = (el.getAttribute('aria-label')
|| (el.getAttribute('aria-labelledby') && 'labelledby')
|| el.textContent.trim()
|| el.getAttribute('title')
|| el.getAttribute('alt')
|| (el.labels && el.labels.length && 'label')
|| (el.matches('input,select,textarea') && el.placeholder));
return !name;
});
suspect.forEach(el => el.style.outline = '2px solid red');
console.table(suspect.map(el => ({ tag: el.tagName, role: el.getAttribute('role'), type: el.type, html: el.outerHTML.slice(0, 80) })));
console.log('Likely unnamed interactive elements:', suspect.length);
What to check manually: confirm each accessible name actually describes the control's purpose (not just that one exists), and that custom widgets expose the correct role and report state changes (expanded, checked, selected) to a screen reader as you operate them.
Related
- All WCAG 2.2 success criteria
- Learn catalog — lessons mapped to criteria.
- Lesson: Accessible names — naming controls so 4.1.2 passes.