ADA Compliance Professionals

    ARIA attribute values must be valid

    Last updated:

    Who it helps:
    Blind
    Standard:
    WCAG 2.2 Level A

    ARIA attributes must not use invalid or misspelled values

    Every aria- attribute must use a permitted value defined for that attribute.

    This applies to all ARIA states and properties on interactive and structural UI.

    Invalid values confuse assistive technologies and break announced states for many users.

    Why It Matters

    Assistive technologies rely on exact ARIA values to convey state and relationships. If a value is wrong, the AT may ignore it or announce the wrong state.

    People using screen readers, voice control, or switch devices are affected when a toggle reads as off while it is actually on, or when an element is announced without its description.

    For sighted keyboard users, invalid values can prevent expected behavior in custom widgets.

    Common Causes

    • Typos or invented tokens (e.g., aria-hidden="rtue" or aria-checked="yes").
    • Using the wrong type (e.g., numbers for booleans, commas in ID lists).
    • Values not allowed for the role (e.g., unsupported tokens on a given widget).
    • ID references that do not exist or are duplicated in the DOM.
    • State not kept in sync with UI changes (e.g., aria-expanded not updated on toggle).
    • Copying examples without checking the ARIA spec for allowed values.

    How to Fix

    • Identify the attribute and check its allowed value types in the WAI-ARIA states and properties reference (use ARIA 1.2).
    • Use only permitted tokens, spelled exactly. For booleans use "true" or "false". For tristate controls that support it, use "mixed".
    • Keep state in sync. When a control opens, set aria-expanded="true"; when closed, set it to "false". Update the DOM visibility as well.
    • Validate ID references. For aria-controls, aria-labelledby, aria-describedby, and similar, ensure the referenced ids exist, are unique, and are space-separated for lists.
    • Respect role-specific requirements. Some roles have implicit default values or require certain states (e.g., role="switch" uses aria-checked with true/false). Do not supply unsupported values.
    • Prefer native HTML where possible. If you must use ARIA on custom widgets, implement full keyboard and state management.
    • Recommendation: use lowercase for tokens, do not localize token values, and avoid setting attributes when the default/implicit value is correct.
    • About "undefined": if an attribute supports an undefined state, this usually means omit the attribute to represent "not set". Only use the literal token "undefined" when the specification explicitly allows it for that attribute.

    Allowed value types you will encounter:

    • true/false (boolean)
    • true/false/mixed (tristate)
    • true/false/undefined (where undefined differs from false)
    • ID reference (single id)
    • ID reference list (space-separated ids)
    • integer (no fraction)
    • number (real number)
    • string (free text)
    • token (one value from a fixed set)
    • token list (space-separated allowed tokens)

    WCAG alignment: Incorrect ARIA values commonly fail 4.1.2 Name, Role, Value.

    How to Test

    Keyboard check (for custom widgets):

    • Can you operate the widget with Tab, Enter, and Space?
    • Does the ARIA state (e.g., aria-expanded, aria-checked) update correctly on interaction?

    Screen reader check:

    • With NVDA + Firefox or VoiceOver + Safari, navigate to the control.
    • Confirm the announced role and state match the visual UI and update as you interact.

    Browser and code check:

    • Inspect the element in browser DevTools Accessibility panel. Verify the computed role and states; look for warnings about invalid ARIA.
    • Confirm every ID in aria-* references a unique element that exists in the DOM.
    • Run an automated checker to flag invalid ARIA values, then manually verify.

    Mobile/touch check:

    • With TalkBack or iOS VoiceOver, focus the control and ensure the state is read correctly as you activate it.

    Simple checklist:

    • Values are from the allowed set for that attribute.
    • ID references exist and are space-separated.
    • States change in sync with UI behavior.
    • No typos, no invented tokens, no localization of token values.

    Good Example

    HTML
    <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="panel-a" id="trigger-a">Shipping details</button>
    <div id="panel-a" role="region" aria-labelledby="trigger-a" hidden>
      <p>Orders ship within 2 business days.</p>
    </div>
    
    <div role="checkbox" tabindex="0" aria-checked="mixed" id="opt-news">Email preferences</div>
    
    <script>
      const btn = document.getElementById('trigger-a');
      const panel = document.getElementById('panel-a');
      btn.addEventListener('click', () => {
        const expanded = btn.getAttribute('aria-expanded') === 'true';
        btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
        panel.hidden = expanded;
      });
    
      const box = document.getElementById('opt-news');
      function toggleBox() {
        const v = box.getAttribute('aria-checked');
        const next = v === 'mixed' ? 'true' : (v === 'true' ? 'false' : 'mixed');
        box.setAttribute('aria-checked', next);
      }
      box.addEventListener('click', toggleBox);
      box.addEventListener('keydown', e => {
        if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); toggleBox(); }
      });
    </script>

    Why this is good:

    • aria-expanded uses only true/false and matches panel.hidden.
    • aria-controls points to an existing id.
    • The checkbox uses only true/false/mixed.

    Bad Example

    HTML
    <button type="button" aria-expanded="open" aria-controls="missing-panel">Details</button>
    <!-- No element with id="missing-panel" exists -->
    
    <div role="checkbox" tabindex="0" aria-checked="1">Subscribe</div>
    
    <p id="hint1">Enter your 6-digit code</p>
    <input type="text" aria-describedby="hint1, hint2" aria-hidden="rtue">

    What’s wrong:

    • aria-expanded="open" is not a permitted value.
    • aria-controls references a non-existent id.
    • aria-checked="1" is invalid; only true/false/mixed are allowed.
    • ID reference list uses commas; must be space-separated and all ids must exist.
    • aria-hidden value is misspelled and will be ignored.

    Quick Checklist

    • Use only allowed tokens for each aria-* attribute; avoid typos and invented values.
    • Keep ARIA state values synchronized with the actual UI state.
    • Provide existing, unique IDs for IDREF/IDREFS attributes; separate lists with spaces.
    • Do not localize token values; use standard tokens (recommend lowercase).
    • Respect role-specific defaults and required states; remove unsupported attributes.
    • Prefer native HTML elements; add ARIA only when necessary and fully supported.
    • Verify in DevTools and with screen readers; fix any invalid ARIA warnings.