ADA Compliance Professionals

    ARIA attributes must match role rules

    Last updated:

    Who it helps:
    Blind
    Standard:
    WCAG 2.2 Level A

    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-checked from 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-pressed for a toggle button; use aria-checked for 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-pressed toggles 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, or aria-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.

    HTML
    <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.

    HTML
    <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) use aria-checked.
    • No contradictory or duplicate states (e.g., both aria-pressed and aria-checked on the same control).
    • Presentational roles are not applied to focusable or interactive elements.
    • Screen reader announcements match the visual UI and intended behavior.