Skip to content

Child roles need their required parent role

  • Rule: axe/aria-required-parent
  • Checks: Certain ARIA roles must be contained by particular parents
  • WCAG: 1.3.1
  • Fix class: mechanical: A fix needs no authored text or visual decision.
  • People who use a screen reader or a braille display, most of them blind.

An element with a role of its own sits between the item and its container, such as a <ul> inside a role="listbox"

Section titled “An element with a role of its own sits between the item and its container, such as a <ul> inside a role="listbox"”

Make that element the container, give it role="none", or remove it. axe looks past elements with no role, and past a group where the item allows one (as an option in a listbox does), but not past a <ul>, an <li> or other roles.

<ul role="listbox" aria-label="Fruit" tabindex="0" aria-activedescendant="fruit-apple">
<li role="option" id="fruit-apple" aria-selected="true">Apple</li>
<li role="option" id="fruit-pear" aria-selected="false">Pear</li>
</ul>

The item has no container at all, such as a tab without a tablist

Section titled “The item has no container at all, such as a tab without a tablist”

Wrap the items in an element with the container role (tablist, listbox, menu, tree, row), or use native elements that bring the structure with them. If the element is not part of such a widget, remove its item role.

The container and its items are in different parts of the page, such as a menu rendered at the end of <body>

Section titled “The container and its items are in different parts of the page, such as a menu rendered at the end of <body>”

Keep the items inside the container’s subtree. Where the markup cannot move, list the items’ ids in the container’s aria-owns.

Removing the item role to silence the rule

Section titled “Removing the item role to silence the rule”

The rule passes, but the item loses its selected or checked state and its place in the set, which screen reader users need to operate the widget.

Giving a wrapper role="none" while it keeps a tabindex or a global ARIA attribute such as aria-controls

Section titled “Giving a wrapper role="none" while it keeps a tabindex or a global ARIA attribute such as aria-controls”

Browsers ignore none and presentation on an element that is focusable or has a global ARIA attribute, so the wrapper keeps its role and still breaks the structure.

The part of the fix that differs in React, in its syntax. The rest of the guidance holds.

A menu or listbox renders its items through createPortal

Section titled “A menu or listbox renders its items through createPortal”

Portal the whole popup, the element with role="menu" or role="listbox" together with its items, not the items alone. A portal moves elements to another place in the DOM, and the accessibility tree follows the DOM, not the React tree.

import { createPortal } from 'react-dom';
function OrderActionsMenu({ open, container }) {
if (!open) return null;
return createPortal(
<ul role="menu" aria-label="Order actions">
<li role="menuitem" tabIndex={-1}>
Edit order
</li>
<li role="menuitem" tabIndex={-1}>
Cancel order
</li>
</ul>,
container
);
}

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.