Is each script, where necessary, compatible with assistive technologies?

A screen reader user navigates a dashboard: dropdown menus, modals, accordions, all generated in JavaScript. If these components don't expose their role and name to assistive technologies, the screen reader announces "div" or says nothing. The user doesn't know what they can activate, or how.

This criterion applies to any component created or controlled by JavaScript: tabs, accordions, carousels, dropdown menus, modal windows, tooltips. Each component must meet three conditions: a role consistent with its purpose (often button or link), an explicit and non-empty name, a name that reflects the current state when that state changes. A button that opens a panel must announce "Show details", then switch to "Hide details" once activated.

Two alternatives allow you to satisfy the criterion without modifying the inaccessible component: provide an equivalent accessible component on the same page, or provide an alternative that gives access to the same functionality. These fallback solutions exist, but the alternative must be truly equivalent. A simplified page that drops half the functions does not pass.

Without role or name, a JavaScript component is silent to assistive technologies.

3 tests to ensure that scripts remain compatible with assistive technologies

Role, name and state of JavaScript components

  1. Identify all interface components created or controlled by JavaScript: dynamic buttons, menus, accordions, modals, tabs, sliders.
  2. For each component, verify the three points:
    • It has a role consistent with its purpose (e.g., role="button", role="tab", role="dialog").
    • It has an explicit name (neither empty nor generic like "Click here").
    • Its name reflects the current state or content controlled (e.g., "Expand section" becomes "Collapse section" after activation).
  3. If these conditions are not met, verify that an equivalent accessible component is available on the same page.
  4. Otherwise, verify that an accessible alternative provides access to the same functionality. Result: if one of the three options is satisfied, the test is validated.

Rendering of JavaScript components by screen reader

  1. Review the components that validated test 7.1.1.
  2. Test each component with at least one RGAA reference screen reader and browser combination (e.g., NVDA and Firefox, VoiceOver and Safari).
  3. Verify that the screen reader correctly renders the role, name, and state changes of the component.
  4. If rendering is incorrect or incomplete, verify that an accessible alternative provides access to the same functionality. Result: if the component is correctly rendered or if a functional alternative exists, the test is validated.

Relevance of name and role of JavaScript components

  1. Review the components that validated test 7.1.1.
  2. Verify that each component has:
    • A relevant visible label (it clearly describes the function of the component).
    • A relevant role (consistent with what the component actually does).
  3. If the component has an accessible name (aria-label, aria-labelledby, <title>...), verify two things:
    • This accessible name is relevant.
    • It contains at least the text of the visible label. Result: if both conditions are met, the test is validated.

Examples

❌ Non-compliant : Accordion without role or ARIA state

<div class="accordion-toggle" onclick="toggleSection('details')">
  Show details
</div>
<div id="details" class="accordion-content" style="display:none">
  <p>Hidden content of the section.</p>
</div>

The screen reader announces "Show details" as plain text, without an interactive role. The user doesn't know they can activate this element. They also can't trigger it from the keyboard: no tabindex, no handling of Enter or Space keys. And even if they found a way to activate it, nothing indicates that the section is now open.

✅ Compliant : Accessible accordion with synchronized state

<button
  type="button"
  aria-expanded="false"
  aria-controls="details-content">
  Show details
</button>
<div id="details-content" hidden>
  <p>Hidden content of the section.</p>
</div>
 
<script>
  const btn = document.querySelector('[aria-controls="details-content"]');
  const content = document.getElementById('details-content');
 
  btn.addEventListener('click', () => {
    const expanded = btn.getAttribute('aria-expanded') === 'true';
    btn.setAttribute('aria-expanded', String(!expanded));
    content.hidden = expanded;
  });
</script>

The <button> element provides native role and keyboard handling (Enter and Space) without additional code. aria-expanded communicates the state: the screen reader announces "Show details, button collapsed", then "Show details, button expanded" after activation. aria-controls links the button to the area it controls. The aria-expanded attribute is updated in the JavaScript on each click.

Tips and pitfalls

⚠️ aria-label that overrides the visible label without containing it

A button displays "Send" but its aria-label is "Submit contact form". A voice input user who says "Click Send" cannot activate the button: its accessible name doesn't contain that word. Criterion 7.1.3 requires that the accessible name contain at least the visible label. Best practice: start the aria-label with the visible text ("Send, contact form").

⚠️ aria-expanded frozen after interaction

The aria-expanded attribute is present on page load, but the JavaScript forgets to update it when the state changes. The screen reader announces "collapsed" even when the panel is open. This is the most common error in audits of accordions and menus. The ARIA state must be synchronized with the DOM state on each interaction.

💡 Prefer native HTML elements

A <button> natively handles role, focus, and keyboard events. A <div role="button" tabindex="0"> requires recoding everything manually: key handling, visible focus, AT announcement. The rule is simple: use ARIA only when native HTML cannot do the job. This is rare.

⚠️ Symbols used as visible labels

A button displays ">" for "Next", or "B" for "Bold" in an editor. The visible text is a functional symbol, not a literal description. In this case, the accessible name must express the function of the symbol, not reproduce it: aria-label="Next" or aria-label="Bold". Applying the symbol as-is as the accessible name would be non-compliant.

⚠️ Mathematical expressions: the symbol can remain literal

If the visible label is a mathematical expression like "A>B", it is acceptable to use it as-is as the accessible name. The voice input user knows their tool and knows how to spell out this expression ("A greater than B" or "A bigger than B"). This tolerance applies only to mathematical expressions. Icons and functional symbols remain subject to the general rule.

💡 Punctuation and capitalization: ignored without consequence

Test 7.1.3 requires that the accessible name contain the visible label, but punctuation and case can be ignored. A button displaying "SEND!" with an aria-label="send" is compliant on this point. Do not create artificial non-compliance based solely on differences in case or punctuation marks.

Frequently asked questions

What is the difference between test 7.1.1 and test 7.1.3 in RGAA?

Test 7.1.1 verifies existence and consistency: does the component have a logical role and name? Test 7.1.3 verifies quality and correspondence: is the accessible name relevant and does it contain at least the visible text? A component can validate 7.1.1 (it has a role and a name) and fail 7.1.3 if its aria-label doesn't repeat the visible label. Both tests are complementary.

Why test RGAA compatibility with a real screen reader rather than an automated tool?

Test 7.1.2 requires verification of actual rendering. Automated tools can detect missing ARIA attributes, but cannot verify that NVDA correctly announces a dynamic state after interaction. Test with at least one RGAA reference combination: NVDA and Firefox, or VoiceOver and Safari.

How can an accessible alternative validate RGAA criterion 7.1 despite an inaccessible component?

Yes, provided the alternative is truly equivalent and the user is informed of its existence. It must provide access to the same functionality, without loss of information or discriminatory steps. A link to "Accessible version" leading to a degraded page that removes functions does not constitute a valid alternative.

Why is a <div role="button"> without tabindex non-compliant with RGAA criterion 7.1?

Yes. Without tabindex="0", the element is not reachable by keyboard, so it cannot be used by screen reader users who navigate using the Tab key. The component also fails criterion 7.3, which requires keyboard handling for scripts. The native <button> element avoids both problems from the start.

How do you verify that an accessible name contains the visible label according to test 7.1.3?

Open the browser's accessibility inspector ("Accessibility" tab in Chrome or Firefox DevTools) and note the calculated accessible name. Compare it with the visible text in the interface. The accessible name must contain this visible text, excluding punctuation and case differences. If an aria-label or aria-labelledby is present, it takes precedence over the text content of the element.

References