ARIA attributes are not general-purpose annotations. Each ARIA state or property is only meaningful when used on the correct role and in the correct context.
If an ARIA attribute conflicts with native HTML semantics or is applied to a role that does not support it, browsers and assistive technologies may expose inconsistent or contradictory information.
WCAG Success Criterion 4.1.2 — Name, Role, Value
WCAG 4.1.2 requires that user interface components expose:
- Name — what the control is called
- Role — what kind of control it is
- Value / State — its current condition (checked, expanded, selected, etc.)
All of this must be programmatically determinable, meaning assistive technologies can reliably obtain it from the browser, not infer it visually.
A Few Grounding Concepts
What Is a Role?
A role tells assistive technologies what a component is.
Examples:
- button
- checkbox
- radio
- treeitem
Native HTML elements already have implicit roles.
For example, <button> has the role button automatically.
What Is a State?
A state describes the current condition of a component.
Examples:
- checked / unchecked
- expanded / collapsed
- disabled / enabled
Native HTML elements automatically expose their state through the browser.
What Are Native Semantics?
Native semantics are the built-in meaning and behaviour provided by HTML elements.
For example:
- already exposes:
- role: checkbox
- state: checked / unchecked / mixed
- No ARIA is required to describe this.
ARIA exists only to describe custom widgets that cannot use native HTML.
How This Works Under the Hood (Why Conflicts Happen)
Browsers build an accessibility tree, a structured representation of the page, and expose it to assistive technologies through platform accessibility APIs.
The process is roughly:
- Native HTML semantics are mapped first
- ARIA is applied on top, only where allowed
- Some native semantics are strong and cannot be overridden
When ARIA duplicates or contradicts native semantics, the specification allows browsers and assistive technologies to ignore or deprioritise the ARIA, which can lead to inconsistent output. This is why ARIA cannot reliably override native HTML behaviour.
The Core Rule
If HTML already provides a role or state natively, do not replace or duplicate it with ARIA. ARIA should fill gaps, not compete with the platform.
Example 1: aria-checked on Native Checkboxes
Incorrect Usage
<label>
<input type="checkbox" aria-checked="true">
I agree to make my website accessible
</label>
Why This Is a Problem
<input type="checkbox">already exposes its checked state natively- The ARIA in HTML specification prohibits using ARIA attributes that duplicate or conflict with native semantics
aria-checkedhere is redundant and may be ignored- Native state and ARIA state can diverge, producing inconsistent announcements
Some screen readers prioritise the native state, others may expose both.
Result: unreliable state exposure, which can lead to a failure of SC 4.1.2.
Correct Usage (Native First)
<label>
<input type="checkbox" checked>
I agree to make my website accessible
</label>
- Native semantics are exposed consistently
- No ARIA required
- Most robust and accessible solution
Mixed State (Indeterminate)
If a mixed state is required:
checkbox.indeterminate = true;
Do not use aria-checked="mixed" on a native checkbox.
When ARIA Is Appropriate for Checkboxes
ARIA becomes appropriate only when you do not use a native checkbox.
<div
role="checkbox"
aria-checked="false"
tabindex="0">
Subscribe
</div>
This requires full manual implementation:
- Keyboard interaction (Space toggles state)
- State updates
- Focus management
This complexity is exactly why native controls are always preferred.
Example 2: Conditional ARIA on Table Rows
Some ARIA attributes are conditional, and they are only allowed when the element participates in a specific widget role.
Incorrect Usage
<table>
<tr aria-level="1" aria-expanded="false">
<td>My Downloads</td>
</tr>
</table>
Why This Fails
aria-levelandaria-expandedonly have a defined meaning in hierarchical widgets- A normal table row does not support hierarchy
- Outside of a supported role, these attributes have no defined meaning
Assistive technologies may ignore them or expose a misleading structure.
Correct Usage (Treegrid)
<table role="treegrid">
<tr role="row" aria-level="1" aria-expanded="false">
<td role="gridcell">My Downloads</td>
</tr>
</table>
Now:
- The parent role (treegrid) supports hierarchy
- The row supports expansion semantics
- Screen readers can announce the structure correctly
Why This Matters to Users
When ARIA attributes are used outside their allowed roles:
- State announcements may not match the visual state
- Controls may announce contradictory information
- Navigation becomes inconsistent across screen readers
Users who rely on assistive technologies lose trust in the interface.
Common Anti-Patterns
- Adding ARIA to native controls “just in case”
- Using
aria-checkedon native checkboxes - Applying tree attributes outside of tree roles
- Assuming ARIA overrides HTML behaviour
- Mixing native and ARIA states for the same concept
WCAG Impact
Misusing ARIA attributes may result in a failure of WCAG 4.1.2 when:
- The role exposed is incorrect
- The state announced does not match the visual state
- Assistive technologies cannot reliably determine name, role, or value
This is not about ARIA being forbidden; it’s about ARIA being exact.
How to Test
Automated
- axe-core rules related to:
aria-allowed-attraria-required-attraria-valid-attr-value
Manual (Essential)
- Toggle the control using only the keyboard
- Listen for state changes in:
- NVDA + Firefox
- JAWS + Chrome
- VoiceOver + Safari
- Verify that:
- Announced state matches visual state
- No contradictory information is spoken
If native and ARIA states ever disagree, the implementation is broken.
Wrapping Up
ARIA attributes only work when used exactly as defined for the role.
- Native HTML already exposes reliable semantics
- ARIA cannot safely override strong native behaviour
- Conditional ARIA attributes require the correct role context
Use native elements first.
Use ARIA only when necessary.
And always use it exactly as specified.
That’s how you get predictable, interoperable accessibility, not just passing audits.