A matrix comparing a plain div, a div with role=button, and a native button across four rows: reported role, tab stop, Enter/Space activation, and focus ring. Adding role=button changes only the reported role to button, Place Order; the other three rows stay unmet until the native button column, which is the only one checked all the way down.

ARIA roles for developers: every role is a promise you still owe

An ARIA role changes what an element reports to the accessibility tree. It builds none of the behaviour that report implies.

What a role changes in the accessibility tree, and what it leaves the author owingThe same div reading Place Order, taken four ways: plain, with role=button, as a native button, and carrying a role name the browser does not recognise. Measured in Chromium 151.0.7922.34. The role=button branch reports a button and supplies none of the behaviour attached beneath it.

A role is a promise, not an attribute#

Applying a role tells assistive technology that behaviour already exists. However, the browser will not go and build that behaviour for you.

The W3C ARIA Authoring Practices Guide states this as authoring practice rather than as a specification requirement. It carries no version line of its own, so it is living guidance and reads as practice.

Its opening principle is headed "Principle 1: A role is a promise", and the APG carries a <div role="button">Place Order</div> example of its own. It glosses that markup as a promise "that the author of that div has also incorporated JavaScript that provides the keyboard interactions expected for a button."

The same page puts the negative plainly: "Unlike HTML input elements, ARIA roles do not cause browsers to provide keyboard behaviors or styling."

Therefore the question to ask of any role in your codebase is not whether it is spelled correctly. Instead, ask whether anyone ever wrote the half the browser does not supply.

What a role actually changes#

A role rewrites what the element reports into the accessibility tree. Nothing else about the element moves.

ARIA in HTML is a W3C Recommendation dated 11 August 2026, and it states the positive half normatively. Section 1 says authors "MAY use the ARIA role and aria-* attributes to change the exposed meaning (semantics) of HTML elements".

Exposed meaning is the whole surface, and meanwhile the pixels, the tab order and the event handlers are untouched. Measured in Chromium 151.0.7922.34, only the report changed.

What a role changed and what it left you owing, measured in Chromium 151.0.7922.34
PropertyPlain divdiv with role="button"Native button
Exposed rolestatic textbuttonbutton
Accessible namenonePlace OrderPlace Order
In the tab sequenceNo, you add itNo, you add itYes, from the browser
Activates on Enter and SpaceNo, you write itNo, you write itYes, from the browser
Focus ringNo, you draw itNo, you draw itYes, from the browser
Who supplies the behaviourYouYouThe browser

Exactly one row moves between the first column and the second. Four rows say you. Yet the div and the button are still nothing alike, because only the report changed.

ARIA roles for developers, ordered by what they cost#

WAI-ARIA 1.2 sorts the vocabulary into six categories, and they differ mostly in how much work each one hands you.

The specification is a W3C Recommendation of 6 June 2023. Section 5.3 lists the categories in this order: Abstract, Widget, Document Structure, Landmark, Live Region and Window.

Show data table
Roles listed under each category heading in WAI-ARIA 1.2 section 5.3. Do not sum these and do not read them as a partition of the vocabulary: separator is listed under Widget Roles when focusable and under Document Structure Roles when not, so it is counted twice here.
Item Roles listed in the category
Document structure 38
Widget (20 standalone, 9 composite) 29
Abstract (never usable by an author) 12
Landmark 8
Live region 5
Window 2

Document structure and widget roles carry the bulk of the vocabulary, and the abstract group is the one category an author may never write.

Roles per WAI-ARIA 1.2 category Roles listed under each category heading in WAI-ARIA 1.2 section 5.3. Do not sum these and do not read them as a partition of the vocabulary: separator is listed under Widget Roles when focusable and under Document Structure Roles when not, so it is counted twice here. Counted from the enumerated lists in WAI-ARIA 1.2 section 5.3. The specification publishes no counts of its own.

One category is closed to you entirely, and section 5.3.1 states that "Abstract roles are used for the ontology. Authors MUST NOT use abstract roles in content."

A landmark mostly relabels a region. In contrast, a composite widget commits you to full focus management of its descendants. Therefore reading the category first is a cheap cost estimate.

Where the native-HTML-first rule lives now#

The rule to reach for a native element first is carried today by a Recommendation. Meanwhile the document that wording comes from has been discontinued.

The wording comes from W3C Using ARIA, section 2.1, the First Rule of ARIA Use. In full: "If you can use a native HTML element [HTML] or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so."

Using ARIA became a W3C Discontinued Draft on 24 February 2026, and its own status section says "the four rules of ARIA are kept for historical purposes and for easier reference, but there are no plans to continue work on this document."

Still, the obligation has a current normative home. ARIA in HTML section 1 permits role and aria-* changes "except where ARIA features conflict with the strong native semantics" of an element. Strong native semantics is the key phrase, and that half of the clause backs a review comment today.

Quote the first rule by name, because that is the name the rule carries. Then cite the Recommendation when someone disputes it. For the wider case for starting accessible rather than retrofitting, our post on creating accessible websites covers ground this one skips.

When the role you wrote is not the role exposed#

In Chromium 151.0.7922.34, a role name the browser does not recognise is dropped in silence. Meanwhile your markup still looks correct in the source. That is the fourth branch of the figure at the top of this post.

Two cases were measured. A div carrying role="tabbar", which is not a role in the specification, exposed only static text. A div carrying role="widget", an abstract role, exposed only static text as well. Because a misspelling is a name outside the specification, it lands in the same place.

The abstract rule quoted above is an author conformance requirement. In particular, it governs what you may write, and it says nothing about what a user agent does with it. Therefore the behaviour had to be measured rather than inferred.

Nothing errored, and the page did not change visually. Consequently a review that reads the source attribute rather than the computed role will pass a dead role every time.

Three places do catch it. First, the invalid-role and abstract-role rules in eslint-plugin-jsx-a11y. Second, the aria-roles rule in axe. Third, the computed role that browser devtools shows you, which our accessibility testing tools roundup sets beside what each of those catches and misses.

When a container role changes its own contents#

The presentational children rule in WAI-ARIA 1.2 is a recommendation to browsers, not an instruction to you. Reading it as a rule about your own markup sets the wrong expectation.

Section 5.2.9 says that for such a role "The DOM descendants are presentational" and that "User agents SHOULD NOT expose descendants of this element through the platform accessibility API." SHOULD NOT is an RFC 2119 term and it is addressed to user agents. Moreover, the specification's own next sentence anticipates a browser that does otherwise: "If user agents do not hide the descendant nodes, some information may be read twice."

In practice that second sentence is what got measured, in the Chromium DevTools accessibility tree.

A heading inside four containers: what stayed listed in the Chromium 151.0.7922.34 DevTools accessibility tree, and the name each container computed
CaseHeading listed beneath itContainer name computed
Plain divYesnone
role="button"YesShip it
role="group"Yesnone
Native buttonYesShip it

So descendants stayed listed in that tree under both containers, which is the case the rule contemplates rather than an anomaly. That tab shows the browser's own tree, so what any of it implies about the platform accessibility API the section quotes is an inference the Accessibility tab cannot settle. Instead, the axis that discriminates is the accessible name. Because button computes its name from its subtree, the button is named Ship it, while the group container exposes no name at all.

Therefore the thing to check is not whether your heading survived. It is whether the name the container computed is the name you meant, and whether anything beneath it is now in line to be read twice.

aria-hidden removes the element entirely#

A role edits what an element exposes. In contrast, aria-hidden="true" takes it out of the accessibility tree completely and leaves it fully focusable.

Measured in Chromium 151.0.7922.34, a button carrying aria-hidden="true" exposed nothing at all. Yet it remained focusable.

For scale, the WebAIM Million 2026 edition draws on February 2026 data. It is an accessibility evaluation of "the home pages for the top 1,000,000 web sites", taken from the Tranco ranking and evaluated with WAVE against the rendered DOM. Those home pages averaged 23.3 aria-hidden="true" attributes each, up 30 percent year on year and more than 250 percent since 2020. Because that counts attributes rather than focusable elements hidden, it sizes the exposure and not the defect.

An accordion marked as a tablist#

Putting role="tablist" on an accordion promises a keyboard contract the accordion does not implement.

Some of what tablist commits you to is normative. WAI-ARIA 1.2 section 5.4 gives tablist a required owned element of tab and a superclass role of composite. That owned-element requirement is the flat obligation, and because accordion panels are not tab elements, an accordion breaks it immediately.

The same table adds two recommendations. Authors SHOULD manage focus of descendants. Also, authors SHOULD hide other tabpanel elements until their tab is selected. Both are RFC 2119 recommendations rather than conformance requirements.

The rest is APG authoring practice. In particular, roving tabindex and arrow-key handling come from the APG Tabs pattern, which says "Left Arrow : moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab."

Then there is the tab sequence. The Tabs pattern treats the tab list as a single stop. In contrast, the APG Accordion pattern says "Tab : Moves focus to the next focusable element; all focusable elements in the accordion are included in the page Tab sequence." Those two genuinely contradict each other.

Arrow keys are a different axis. Because the accordion pattern carries no arrow-key requirement at all, this is an obligation set against an absence rather than an opposite.

Where Tab actually went in three widgets, measuredMeasured Tab order in Chromium 151.0.7922.34. The accordion marked up as a tablist put both headers in the page tab sequence, and the APG accordion markup did exactly the same. The single-stop model the tablist role promised was never there.

In practice the gap is measurable, and the two chains that should have differed render identically.

Not every ARIA mistake is equally serious#

A role that restates what the element already means is advised against and unnecessary. Still, treating it as a barrier would overstate the source.

ARIA in HTML, the Recommendation of 11 August 2026, says this in section 1: "It is NOT RECOMMENDED for authors to set the ARIA role and aria-* attributes to values that match the implicit ARIA semantics defined in either table. Doing so is unnecessary and can potentially lead to unintended consequences."

NOT RECOMMENDED is an RFC 2119 term in that document. Therefore it reads as SHOULD NOT rather than MUST NOT, and the specification advises against a redundant role without forbidding it.

Yet its own wording keeps the door open on unintended consequences. Consequently calling a redundant role harmless goes further than the sentence does.

Still, nothing here softens the sections above. A dead role, a hidden focusable element and a promised keyboard model that does not exist sit in a different class.

How common this is on real home pages#

Among the top one million home pages measured in February 2026, 5.7 percent carried an ARIA menu. Then 22 percent of those menus introduced barriers.

Show data table
The two stage counts below the first are ARITHMETIC from WebAIM's published percentages, not counts WebAIM publishes: 5.7 percent of 1,000,000, then 22 percent of that. Introduced accessibility barriers is WebAIM's own phrase and it stays as written.
Stage Count (home pages) Share of first stage
Home pages evaluated 1000000 100%
Carried an ARIA menu (5.7%) 57000 5.7%
Those menus introduced accessibility barriers (22% of them) 12540 1.3%

The 22 percent applies to the menus, not to the million, which is the misreading the two bare percentages invite.

From one million home pages to the menus that introduced barriers The two stage counts below the first are ARITHMETIC from WebAIM's published percentages, not counts WebAIM publishes: 5.7 percent of 1,000,000, then 22 percent of that. Introduced accessibility barriers is WebAIM's own phrase and it stays as written. WebAIM Million 2026, February 2026 data

WebAIM states the finding this way: "5.7% of home pages had an ARIA menu (role="menu"), but 22% of those ARIA menus introduced accessibility barriers due to the lack of necessary ARIA menu markup and interactions."

Elsewhere in the same report, WebAIM attaches a non-causal caveat to its error counts, noting that pages carrying more ARIA were also more complex. That caveat belongs to those counts rather than to the menu figure, where WebAIM does attribute the barriers to missing markup. So broken ARIA markup is the finding carried here, and ARIA making pages worse is not.

A one-minute check on your own component#

You can settle whether a role was honoured before anyone argues about whether it was the right role.

First, open the page in Chromium 151.0.7922.34 or a current equivalent and select the element in devtools. Second, open the Accessibility tab inside the Elements panel and read the computed role and the computed accessible name. Next, read the child nodes listed beneath it. Then compare all three against what the role promised.

For example, run it on the role="button" wrapping a heading from earlier. The target result is a button node named Ship it, with the heading still listed beneath it.

However, that tab shows the browser's own tree rather than what an assistive technology consumes. So passing this check confirms the role was honoured, and it establishes nothing about what any screen reader says. For the layered method and the assistive-technology setup that follows, our accessibility testing post carries the guidance this one does not build.

When not to use an ARIA role at all#

Deleting the custom widget and shipping the native element removes every obligation the role created.

Several situations qualify outright. A clickable div becomes <button>. A styled span that navigates becomes <a href>. Swap a custom disclosure for <details> and <summary>. Swap a hand-built select for <select>. Because each of those native elements already carries the semantics and the behaviour, they are exactly what the first rule and the strong-native-semantics clause point at.

swap-button.html · html
<!-- The ARIA construction. Every line below the first is an obligation
     the role created and you now owe: the tab stop, the keyboard
     activation, the focus ring. -->
<div role="button" tabindex="0" class="btn" id="order">Place Order</div>

<script>
  const el = document.getElementById('order');
  el.addEventListener('click', placeOrder);
  el.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); placeOrder(); }
  });
</script>

<!-- ...and in your stylesheet, the focus ring the role did not draw:
     .btn:focus-visible { outline: 3px solid #0b6bcb; } -->

<!-- The native replacement. The ledger clears. -->
<button class="btn" onclick="placeOrder()">Place Order</button>

In each case the ledger clears. The tab stop, the Enter and Space handling, the focus ring, the state reporting and the owned children all stop being yours. Therefore this is not a retreat, because the obligations disappear instead of being paid.

If accessibility is being built into something new, our accessibility built in from the start work is where that fits. If something is already live and needs auditing and remediation, WCAG and ADA compliance is the other route.

Treat every role as a contract with a bill attached, and read the bill before you sign it. In the end, ARIA roles for developers is a short subject, because knowing what a role obliges is what tells you when not to write one.

Keep reading