ARIA attributes must match role specs
Last updated:
Related Guides
ARIA attributes must not conflict with native states or roles
Only use ARIA attributes where the specification allows; never override native semantics. Issues often appear when aria-checked is added to a native checkbox or when treegrid-only attributes (aria-level, aria-posinset, aria-setsize, aria-expanded) are applied to rows in plain tables or grids. These mistakes confuse assistive technologies and create mismatched states.
Why It Matters
Assistive technologies rely on consistent name, role, and state information. If ARIA contradicts the underlying element, users hear conflicting announcements or miss key states. For example, a screen reader may announce a checkbox as checked while the browser exposes it as unchecked, or ignore hierarchical row info that only applies in a treegrid.
This directly affects blind and low-vision users who navigate by semantics, and can also confuse voice control users who issue commands based on roles and states. It relates to WCAG 2.2 Success Criterion 4.1.2 (Name, Role, Value).
Common Causes
- Setting
aria-checkedon a native inputtype="checkbox"instead of using the checked state. - Copying ARIA patterns without verifying which roles/attributes are allowed on the element.
- Applying
aria-level,aria-posinset,aria-setsize, oraria-expandedto<tr>orrole="row"inside a regular table or grid. - Building a custom checkbox without role, state management, or keyboard handling.
- Mixing native semantics (table) with widget semantics (treegrid) in the same component.
How to Fix
Native HTML checkboxes
- Remove
aria-checkedfrom inputtype="checkbox". - Use the checked attribute/property and keep it in sync with the UI state.
- Ensure the checkbox has a visible and programmatic label via
<label>,aria-label, oraria-labelledby.
Custom (non-native) checkbox
- Use a focusable element (e.g.,
<button>or<div tabindex="0">) withrole="checkbox". - Manage the state with aria-checked="true"/"false" and update it on interaction.
- Support keyboard: Space toggles the state; focus is visible.
- Keep the visual state,
aria-checked, and any internal data model synchronized. - Provide a clear label via text content or
aria-labelledby.
Row hierarchy attributes (treegrid only)
- Only use
aria-level,aria-posinset,aria-setsize, andaria-expandedwhen the parent widget is a treegrid (container hasrole="treegrid"). - Mark rows with
role="row"and cells withrole="gridcell"(or rowheader when appropriate). - Apply aria-level/aria-posinset/aria-setsize on the row to express hierarchy and set membership.
- Use
aria-expandedon the row (or the appropriate first-column gridcell) to indicate collapsed/expanded state. - If you are not implementing a hierarchical grid, remove these attributes.
Recommendation: Prefer native HTML controls where possible. Only add ARIA when necessary and supported for the element’s role.
How to Test
- Keyboard (checkboxes):
- Tab to each checkbox.
- Confirm the focus indicator is visible.
- Press Space to toggle. Verify visual and programmatic state change together.
- Screen reader (checkboxes):
- Navigate to the checkbox. Verify it is announced as "checkbox" with the correct checked/unchecked state.
- Toggle and confirm the announcement updates.
- Keyboard (treegrid):
- Ensure rows are reachable with Tab (or arrow keys within the grid as appropriate).
- If expandable, confirm keyboard toggles update
aria-expandedand the visible content. - Screen reader (treegrid):
- Verify the container is announced as a grid/treegrid.
- Confirm level is announced where appropriate and that expanded/collapsed states are read.
- Mobile/touch:
- Check that custom checkboxes can be activated by touch and have adequate target size (recommendation: at least 24x24 CSS px).
- Code review:
- Inspect the accessibility tree in DevTools.
- Confirm no
aria-checkedis present on native checkboxes. - Verify aria-level/aria-posinset/aria-setsize/aria-expanded appear only within a treegrid context.
Good Example
Native checkbox using the browser’s state:
<label>
<input type="checkbox" checked>
Receive product updates by email
</label>Treegrid row using hierarchy attributes correctly:
<div role="treegrid" aria-label="Downloads">
<div role="row" aria-level="1" aria-setsize="2" aria-posinset="1" aria-expanded="false">
<div role="gridcell">My Downloads</div>
</div>
</div>Bad Example
ARIA overriding a native checkbox state:
<label>
<input type="checkbox" aria-checked="true">
Receive product updates by email
</label>
Hierarchy attributes on a plain table (not a treegrid):<table>
<tr aria-level="1" aria-expanded="false">
<td>My Downloads</td>
</tr>
</table>Quick Checklist
- Do not set
aria-checkedon inputtype="checkbox". - For custom checkboxes, use
role="checkbox",aria-checked, and Space to toggle. - Keep visual state, ARIA state, and data model in sync.
- Use aria-level/aria-posinset/aria-setsize/aria-expanded only inside a
role="treegrid". - Do not apply treegrid-only attributes to plain tables or grids.
- Provide clear, programmatic labels for all controls and rows.
- Validate allowed roles and properties with the accessibility tree and linters.