Buttons are interactive controls that trigger actions.
For assistive technology users, every button must clearly communicate what action it performs.
This rule applies to:
- Native
<button>elements - Any element with
role="button"
WCAG Success Criterion 4.1.2 — Name, Role, Value
User interface components must have a programmatically determinable name, role, and value. If a button has no accessible name, assistive technologies can identify it as a button (role) but cannot identify what it does (name), which commonly fails WCAG 4.1.2.
What Is an Accessible Name?
When a screen reader encounters a button, it does not analyse what the button looks like. Instead, it reads information from the accessibility tree, a structured representation of the page that the browser exposes to assistive technologies.
The accessible name is the text string associated with the button in that tree.
- If the name exists, a screen reader might announce:“Close, button”
- If the name is missing, it may announce only:“Button”
Visible proximity, icons, or layout do not matter unless they are programmatically associated with the button.
Why Visible Text Works Automatically
Native HTML buttons have built-in semantics.
<button>Save</button>
The browser automatically:
- Assigns the role (button)
- Uses the text content (Save) as the accessible name
- Exposes both through the accessibility tree
This is why native <button> elements are always preferred.
How Accessible Names Are Calculated
Accessible names are calculated using the Accessible Name and Description Computation (AccName) algorithm defined by the W3C.
For buttons, the simplified precedence is:
aria-labelledbyaria-label- Visible text content (name-from-content)
- title (fallback only, inconsistent)
The first valid source found becomes the accessible name.
⚠️ This is a simplified explanation. Actual behaviour depends on role and context, but this order is accurate for standard button patterns.
Correct Markup Solutions
1. Visible Text Content (Preferred)
<button>Save</button>
- Most robust solution
- Automatically localized
- No ARIA required
2. Icon-Only Buttons (Correct Pattern)
<button aria-label="Close">
<svg aria-hidden="true">...</svg>
</button>
- SVG is hidden from assistive technologies
- aria-label provides the accessible name
- Required when no visible text exists
3. Using aria-labelledby
<div id="labeldiv">Delete</div>
<button aria-labelledby="labeldiv"></button>
- Uses text from another element
- The referenced element must exist and contain discernible text
- Multiple IDs may be used (names are concatenated)
4. Nested Text Contributes to the Name
<button>
<span>Save</span>
</button>
- Nested text nodes are included in name computation
- This is still name-from-content
5. <input> Buttons (Different Rules)
<input type="submit" value="Save">
- value does provide the accessible name for input buttons
- This is different from <button>, where value is ignored
- Important distinction for beginners
Patterns to Use With Caution
aria-label Overriding Visible Text
<button aria-label="Confirm">Save</button>
- Screen readers announce “Confirm, button”
- Visible text says “Save”
- This may fail WCAG 2.5.3 (Label in Name)
Best practice:
The accessible name should include the visible label text.
title Attribute (Fallback Only)
<button title="Settings"></button>
- Used only if no other name source exists
- Announced inconsistently across screen readers
- Not keyboard-discoverable tooltip content
- Should not be relied on as the primary label
Incorrect Markup Solutions
Empty Button
<button></button>
- No text
- No accessible name
value on <button>
<button value="Save"></button>
- value does not name
<button>elements - Produces an unnamed button
Empty or Broken ARIA
<button aria-label=""></button>
<button aria-labelledby="missing"></button>
- Empty
aria-labelremoves the name - Broken
aria-labelledbyreference results in no name
SVG Without a Name
<button>
<svg>...</svg>
</button>
- If the SVG has no accessible name and is not hidden
- The button may still end up unnamed, depending on implementation
- Always provide a text alternative or ARIA label
Buttons With role="button"
Using role="button" on a non-button element:
- Does not provide keyboard behaviour
- Does not manage focus automatically
- Does not handle disabled states
- Still requires an accessible name
<div role="button" tabindex="0">Save</div>
This must also implement keyboard handling (Enter / Space) via JavaScript.
Use native <button>whenever possible.
ARIA is a fallback, not a replacement for semantic HTML.
Why This Matters
Without a discernible name:
- Screen reader users must guess the button’s purpose
- Voice control users cannot activate the button reliably
- Navigation becomes slower and error-prone
A button that is visually clear but programmatically unnamed is not accessible.
How to Test
Automated
- axe-core: button-name
- Lighthouse accessibility audits
Manual (Recommended)
- Tab to the button
- Listen to the announcement in:
- NVDA + Firefox
- JAWS + Chrome
- VoiceOver + Safari
- Confirm you hear something like:“Save, button”
If you only hear “Button”, the control is missing an accessible name.
Wrapping Up
Buttons must communicate what action they perform, not just that they are clickable.
If a button’s accessible name is empty:
- The role is exposed
- The action is not
That disconnect breaks WCAG 4.1.2 and creates real usability barriers. If a screen reader can’t name the button, the button is broken.