Accessibility
What ARIA is (and when not to use it)
ARIA is a W3C specification that adds roles and states to HTML for assistive technology. Use it only when native HTML falls short: cases and limits.
Key points
- ARIA describes roles, states and properties for the accessibility tree; it adds no behaviour.
- The first rule is not to use it when a native HTML element already does the job.
- A wrong role is worse than no role: it breaks name, focus and announcement.
ARIA (Accessible Rich Internet Applications) is a W3C specification that adds roles, states and properties to HTML for assistive technology. It adds no behaviour: if keyboard handling or focus is missing, ARIA will not create it.
What ARIA actually adds
Three families of attributes. role states what kind of control an element is; aria-* describes state and properties, for example aria-expanded or aria-invalid; live regions such as role="status" announce a change without moving focus. The current specification is WAI-ARIA 1.2, a W3C Recommendation dated 6 June 2023, published under the W3C Document License.
The first rule
The WAI-ARIA Authoring Practices open with a simple rule: if a native HTML element already provides the semantics and behaviour you need, use it and do not add a role. A <button> is clickable, takes focus and answers Enter and Space; a <div role="button"> gets the name but not the rest. The guide is published by the W3C under the W3C Document License; the semantics of the elements live in the HTML Living Standard, WHATWG, CC BY 4.0.
Three cases where ARIA helps
- Composite widgets: a tab set or a combobox built from scratch needs roles,
aria-selectedand arrow-key handling. - Dynamic state: a panel that opens declares
aria-expanded; a form error declaresaria-invalidand a linked message. - Asynchronous announcement: a
role="status"reports “3 results” after a search without stealing focus.
Three cases where it breaks
The first is aria-hidden="true" on a container that holds a link or a button: the control stays reachable by keyboard but disappears from the accessibility tree. The second is aria-label on a button that already has visible text: the accessible name changes and no longer matches what the user sees or dictates. The third is a role that promises behaviour nobody implemented, such as role="tablist" with no arrow-key navigation. In all three cases the screen reader announces something the code does not keep.
How to check it
An automated test finds static errors, not the flow. Pa11y is open source under the LGPL-3.0 licence and applies WCAG rules to the rendered page.
npx --yes pa11y https://example.com --standard WCAG2AA
The command returns a list of violations with selector and criterion. Then run the test no tool replaces: tab through the page, open and close the widget with Enter, Space and Escape, and listen to the announcement with VoiceOver or NVDA.
The EneUI catalogue
EneUI indexes the accessibility category and the dialog components. A verified record is Origin UI's dialog, with an MIT licence declared upstream: it depends on @radix-ui/react-dialog and does not reinvent the focus trap. Checked on 1 November 2026 against 111,985 components.
The limitation
ARIA changes the accessibility tree, not the behaviour. If the widget does not manage focus, keyboard and tab order, no attribute will save it; and a wrong role can make the component worse than a <div> with no attributes.
Where to continue
The operational checklist is in accessible React components. The most common mistake, with cause and fix, is in aria-label on buttons; the comparison of headless primitives is in Radix, Base UI and React Aria.
Frequently asked questions
Is ARIA mandatory?
No. ARIA is only needed when a native HTML element does not cover the role or state. A native button does not need role="button".
Can I use aria-label on any element?
No. aria-label gives an accessible name only to elements that support it, and it must not contradict visible text. On a button with text, the visible name is enough.
Does ARIA add keyboard support?
No. ARIA describes roles, states and properties, but it does not handle events. Enter, Space, Escape and the arrow keys stay in your code.
Does aria-hidden="true" hide an element from the keyboard too?
No, and that is the problem. A focusable control inside an aria-hidden container stays reachable but is never announced; remove it from the tab order or do not hide it.
How do I check ARIA without a screen reader?
An automated test covers only part of it. Pa11y or axe report invalid roles and missing names; the flow still needs a keyboard test and VoiceOver or NVDA.