ARIA attributes must match role rules
Last updated:
Related Guides
Elements must not use ARIA attributes unsupported by their role
Only ARIA states and properties permitted for an element's role may be used.
This often appears in custom widgets, copied snippets, and component libraries.
Users of assistive technology can hear misleading announcements or lose expected behavior when invalid ARIA is present.
Why It Matters
Screen readers rely on the role to know which ARIA states and properties are meaningful. If a control exposes an attribute that the role does not support, announcements may be wrong (for example, "checked" on a button), leading to errors.
Invalid ARIA can also mask native semantics or suppress accessibility features. This creates confusion for blind users and anyone navigating by voice or keyboard.
This maps to WCAG 2.2 Success Criterion 4.1.2 (Name, Role, Value): roles and associated states must be programmatically determinable and accurate.
Common Causes
- Adding aria-* by habit without checking role support.
- Copying code between components with different roles (e.g., moving
aria-checkedfrom a switch to a button). - Building custom widgets on non-semantic elements and picking the wrong role.
- Mixing global naming attributes with a role meant to be presentational or inert.
- Framework defaults that attach the same aria-* to multiple roles.
How to Fix
- Identify the element's computed role (explicit role attribute or the implicit role from the HTML element).
- Look up the role in the WAI-ARIA roles table to see which states and properties are allowed and which are required.
- Remove any aria-* not permitted for that role.
- If the behavior needs a specific state, choose the correct attribute for the role (e.g., use
aria-pressedfor a toggle button; usearia-checkedfor checkbox, radio, or switch). - Prefer native HTML controls that already support the needed semantics (e.g., input
type="checkbox"instead of a div with a role). - If you must use ARIA for a custom widget, ensure all required states/properties for that role are present and kept in sync with interactions.
- Recommendation: Avoid role="presentation"/"none" on interactive or focusable elements, as this can invalidate states and names you might add.
How to Test
- Keyboard check:
- Tab to each interactive control.
- Activate with Enter/Space and confirm the correct state attribute for the role changes (e.g.,
aria-pressedtoggles on a button). - Ensure focus order and behavior match expectations.
- Screen reader check (NVDA, JAWS, VoiceOver, TalkBack):
- Navigate to each control.
- Confirm the role and state are announced correctly (e.g., "Button, pressed" not "Button, checked").
- Verify names come from appropriate sources (text,
aria-label, oraria-labelledby) and no contradictory attributes are present. - Mobile/touch check:
- With VoiceOver or TalkBack, explore controls.
- Ensure rotor/quick nav lists and announcements reflect the correct role and state.
- Quick inspection checklist:
- For each element with a role, list its aria-* attributes.
- Cross-check each attribute against the role's allowed states/properties.
- Remove or replace any that are not allowed.
Good Example
A toggle button uses the correct state for its role (aria-pressed), not aria-checked.
<button id="mute" type="button" aria-pressed="false">Mute notifications</button>
<script>
const btn = document.getElementById('mute');
btn.addEventListener('click', () => {
const pressed = btn.getAttribute('aria-pressed') === 'true';
btn.setAttribute('aria-pressed', String(!pressed));
});
</script>Bad Example
A button incorrectly uses aria-checked, which is not supported for role=button.
<button id="mute" aria-checked="true">Mute</button>
<script>
// Incorrect: aria-checked is not valid for a button role
const bad = document.getElementById('mute');
bad.addEventListener('click', () => {
const checked = bad.getAttribute('aria-checked') === 'true';
bad.setAttribute('aria-checked', String(!checked));
});
</script>Quick Checklist
- Every element's ARIA attributes are permitted for its explicit or implicit role.
- Required states/properties for the role are present and update on interaction.
- Native elements are used when possible; custom roles only when necessary.
- Toggle buttons use
aria-pressed; checkable widgets (checkbox/radio/switch) usearia-checked. - No contradictory or duplicate states (e.g., both
aria-pressedandaria-checkedon the same control). - Presentational roles are not applied to focusable or interactive elements.
- Screen reader announcements match the visual UI and intended behavior.