A single sentence with a lot of impact
Some ARIA roles can completely remove their children’s semantics.
This behaviour is defined in the Core Accessibility API Mappings 1.2 specification:
If the host language element is overridden by a WAI-ARIA role whose semantics or structure is not equivalent to the native host language semantics or to a subclass of those semantics, then treat any child elements having roles specified as Allowed Accessibility Child Roles as having presentation or none.
Here’s a plain language breakdown of that sentence:
- When you apply certain ARIA roles, the browser stops using the element’s native structure.
- From that point on, only child elements that explicitly match the ARIA role’s expected structure remain accessible.
- Any other child elements are treated as presentation only.
In these cases, ARIA doesn’t build on existing semantics. It replaces the entire semantic model of the element, including how the browser interprets its children.
Once this happens, the browser no longer makes any assumptions about native HTML relationships. Every meaningful part of the widget must be stated explicitly using ARIA roles.
Is this documented anywhere else?
This rule appears in other specifications, but only indirectly. It’s never stated as clearly as it is in Core AAM. For example:
5.2.6 Allowed Accessibility Child Roles
Explains which child roles are allowed, but not what happens if they’re missing.
5.2.7 Required Accessibility Parent Role
Defines structural expectations, but not the consequence of breaking them.
An example: menu and menuitem
Let’s look at an example:
<ul role="menu">
<li>Save</li>
<li>Open</li>
</ul>
At a glance, this looks reasonable.
- A list has been repurposed as a
menu. - The list items look like
menuoptions.
However, this is not how ARIA works.
By applying role="menu", you’re opting into the ARIA menu widget model whether you intend to or not.
- Native list semantics are discarded.
<li>elements no longer have any implicit meaning.- No menu items exist in the accessibility tree.
If you inspect this example in Chrome’s accessibility tree, you can see how the list items lose their semantics and are not exposed as menu items.
From an accessibility perspective, this is now a menu with no items.
To make this work, the child semantics must be redefined explicitly:
<ul role="menu">
<li role="menuitem">Save</li>
<li role="menuitem">Open</li>
</ul>
This is required to rebuild the menu’s structure in the accessibility tree.
Other common examples where child semantics are wiped
The menu example is not unique. The same kind of structural replacement happens with many widget roles.
Here are some other examples:
menu→menuitem,menuitemcheckbox,menuitemradiotablist→tablistbox→optiontree→treeitemgrid→row→gridcellmenubar→menuitem
In all of these cases, native HTML semantics may be ignored once the ARIA widget role is applied.
When this rule does not apply
So far, this article has shown how some ARIA roles replace an element’s entire semantic structure and wipe out native child semantics.
However, there is an important exception. The specification includes a qualifying phrase that is easy to miss:
“…whose semantics or structure is not equivalent to the native host language semantics…”
Those few words carry a lot of weight. They define when ARIA replaces semantics and when it merely reinforces them.
Here are two rules to help understand what is happening:
- Rule 1: Some ARIA roles replace the entire semantic model of an element, including how its children are interpreted.
- Rule 2: That replacement only happens when the ARIA role’s semantics are not equivalent to the native HTML model.
When the ARIA role introduces a different widget model, native structure is discarded. It must be rebuilt explicitly using ARIA roles.
However, when the child elements already have native semantics that match the ARIA role’s expected model, those semantics are preserved.
Here’s an example where semantics are preserved:
<div role="radiogroup" aria-labelledby="group-heading">
<h3 id="group-heading">Do you like boats?</h3>
<div>
<input type="radio" id="boats-y" name="boats">
<label for="boats-y">Yes</label>
</div>
<div>
<input type="radio" id="boats-n" name="boats">
<label for="boats-n">No</label>
</div>
</div>
In this case, the child elements already match the radiogroup model. So, ARIA is reinforcing an existing model rather than replacing it:
- Native radio inputs already expose
role="radio". - The grouping behaviour already exists in HTML.
- The ARIA radiogroup role matches the native semantic model.
The radio buttons remain intact and correctly exposed to assistive technologies.
Here’s a similar example where semantics are not preserved:
<div role="radiogroup">
<div>Yes</div>
<div>No</div>
</div>
In this case, the child elements do not expose role="radio" and do not match the expected radiogroup model.
Because the semantics are not equivalent, the exception no longer applies. The radiogroup role introduces a new widget model, and the child semantics are discarded.
To make this accessible, the child roles would need to be redefined explicitly.
<div role="radiogroup">
<div role="radio">Yes</div>
<div role="radio">No</div>
</div>
Note: This example is not a complete or accessible radio group. It omits keyboard support, state management, and interaction logic, and is shown only to demonstrate how child roles would need to be redefined.
Why this distinction matters
Without this context, ARIA can feel inconsistent.
Sometimes ARIA roles seem to wipe child semantics, and at other times they do not.
This difference isn’t arbitrary. It depends on whether the ARIA role introduces a new widget model or aligns with an existing native one.
Once you recognise this distinction, a lot of previously confusing ARIA behaviour starts to make sense.
Conclusion
Understanding which ARIA roles replace structure, and which merely reinforce it, is important. Especially when building non-native widgets.
Related articles
How aria-labelledby really works
How aria-labelledby works, including naming precedence, multiple references, hidden content, broken IDs, and shadow DOM boundaries.
Tests for aria-labelledby and aria-describedby
A series of tests to determine how hidden content is used by aria-labelledby and aria-describedby.
Eight quick things to remember when using aria-owns
Eight quick things to rememer when using aria-owns including what it is, when to use it and when not to use it.
Why role="button" on a <button> does nothing
Adding role="button" to a native <button> looks harmless, but it doesn’t change anything. This article explains why that ARIA role is ignored and how this behaviour is defined in the specification.