ARIA role missing required children (aria-required-children)

The aria-required-children rule flags an element whose ARIA role promises a structure it does not contain: a tablist with no tab, a listbox with no option, a menu with no menuitem. Assistive technology announces the container and then finds nothing it can use in it. The fix is to give the children the roles the container requires – or to drop the container's role.

What the rule means

Some ARIA roles are containers that only make sense with particular children, defined in WAI-ARIA as "required owned elements". A few common pairs:

  • tablist → tab
  • listbox → option (optionally in a group)
  • menu / menubar → menuitem, menuitemcheckbox, menuitemradio
  • list → listitem
  • grid / table → row (in a rowgroup or directly)

The rule fails when a container with one of these roles has no child with a required role, or has children with other roles in between. Children can be real descendants or be attached with aria-owns.

Who is affected

Screen reader users. A tablist announces "tab list" and a count of tabs; a listbox announces options and lets people move between them with the arrow keys. When the children do not carry the roles, the count is zero, the arrow-key navigation has nothing to move to, and the widget becomes a set of unlabelled buttons or plain text – often with no indication that anything is selected.

Why the check fails

  • Tabs built from buttons inside role="tablist", without role="tab" on each button.
  • Custom selects with role="listbox" whose items are plain <div>s or <li>s.
  • Wrappers in between: a <div class="tab-wrapper"> between the tablist and its tabs, without role="presentation".
  • Empty containers rendered before the items load, for example search suggestions – mark them aria-busy="true" until they are filled.
  • Copied markup where the container role survived and the item roles did not.

How to fix it

Give each child the role the container expects, with the states that belong to it.

<!-- Before: a tablist with plain buttons -->
<div role="tablist" aria-label="Product details">
  <button type="button" class="tab is-active">Description</button>
  <button type="button" class="tab">Reviews</button>
</div>
<!-- After: real tabs, with their selected state and their panels -->
<div role="tablist" aria-label="Product details">
  <button type="button" role="tab" id="tab-desc" aria-selected="true" aria-controls="panel-desc">Description</button>
  <button type="button" role="tab" id="tab-rev" aria-selected="false" aria-controls="panel-rev" tabindex="-1">Reviews</button>
</div>
<div role="tabpanel" id="panel-desc" aria-labelledby="tab-desc">…</div>
<div role="tabpanel" id="panel-rev" aria-labelledby="tab-rev" hidden>…</div>

The roles are only half of it: a tablist also needs the arrow keys to move between tabs. The ARIA Authoring Practices Guide describes the keyboard behaviour for each pattern. If you do not want to build that, drop the ARIA roles and use plain buttons and headings – that is better than a half-built widget.

How to test it manually

  1. In the browser's developer tools, open the accessibility tree and select the container. Are its children listed with the expected roles?
  2. With a screen reader, move onto the widget. Does it announce the number of tabs, options or items?
  3. Use the arrow keys inside the widget. Does focus move between the items as the pattern describes?
  4. Check the widget in every state – empty, loading, filtered – not just with content.

1.3.1 Info and Relationships, Level A. Related: 4.1.2 Name, Role, Value, because each item also needs its own role and state.

How Reviseberg reports it

Reviseberg runs aria-required-children on every crawled page. The issue list shows the rule with its severity, WCAG 1.3.1, the number of affected elements and pages, and the points a fix gets you back; the detail view shows the container's selector and HTML snippet. Components are usually shared, so one fix in the component library often clears the rule on every page. Where axe cannot decide – some containers, such as a listbox, that are completely empty – the case goes to the Potential issues queue for a person to look at. A container marked aria-busy="true" while its items load is not failed.

  • undefined – the reverse: an item without its container.
  • undefined – a native list with the wrong children.
  • undefined – a role missing an attribute it needs.

Check your widgets – get a free scan

Frequently asked questions

Can I put a wrapper <div> between a tablist and its tabs?

Only if it adds nothing to the structure. Give it role="presentation" or role="none", or move the tablist role to the wrapper.

Does the rule check that the widget works with the keyboard?

No. It checks the roles in the markup. Arrow-key behaviour and focus management need a manual test.

Should I use ARIA tabs at all?

Only if you build the whole pattern. Plain buttons that show and hide sections are accessible too, and simpler to get right.

Sources

  1. W3C, Understanding SC 1.3.1 Info and Relationships – https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships
  2. Deque University, axe-core 4.13 rule aria-required-children – https://dequeuniversity.com/rules/axe/4.13/aria-required-children
  3. W3C, WAI-ARIA 1.2: Required Owned Elements – https://www.w3.org/TR/wai-aria-1.2/#mustContain
  4. W3C, ARIA Authoring Practices Guide: Tabs Pattern – https://www.w3.org/WAI/ARIA/apg/patterns/tabs/

See what a scan finds on your site

One page in about thirty seconds, no email needed. The full report covers up to 100 pages and a keyboard journey.