How to fix an ARIA role that is missing its required children?
aria-required-children page
The issue
A screen reader announces a tab list, a menu or a list, and then finds nothing inside it to step through. The items are on screen, but they don’t carry the role the container needs, so the user can’t tell how many there are or move between them the way that kind of widget promises.
The cause is a container role whose children don’t have the matching role. A role="tablist" needs elements with role="tab"; a role="menu" needs menu items. It often happens when roles are added to the outer element of a custom widget and not to the items, or when a wrapper <div> sits between them. WCAG 1.3.1 requires structure shown visually to be available in the markup.
Scope
Every visible element whose role requires children is checked, whether the role is written in a role attribute or comes from the element itself. A <ul> or <ol> is a list, a <table> is a table and a <tr> is a row, so these count too. Probeo looks for at least one descendant, at any depth, with a required role.
| Role | Needs at least one |
|---|---|
list |
listitem |
listbox |
option or group |
menu, menubar |
menuitem, menuitemcheckbox, menuitemradio or group |
radiogroup |
radio |
tablist |
tab |
tree |
treeitem or group |
table, grid, treegrid |
row or rowgroup |
rowgroup |
row |
row |
cell, gridcell, columnheader or rowheader |
feed |
article |
An element that uses aria-owns is set aside rather than reported, because the children it points to can’t be confirmed from the markup.
How to check you have it
- In probeo: the page reports
aria-required-children, “Certain ARIA roles must contain particular children”, with critical impact, naming the role and the child roles it is missing. - By hand: for each element with a container role, inspect its descendants in DevTools and confirm the items carry the role from the table above.
- With a tool: axe DevTools reports the same rule id. The accessibility pane in Chrome or Firefox DevTools shows the roles of a container’s children.
The fix
Give the items inside each container the role the container requires, or use the native element that has both.
For lists and tables, native markup does this already: <ul> with <li>, <table> with <tr> and <td>. For widgets with no native element, put the item role on each item, not only on the container.
<div role="tablist" aria-label="Plans">
<button role="tab" aria-selected="true" aria-controls="p1" id="t1">Monthly</button>
<button role="tab" aria-selected="false" aria-controls="p2" id="t2">Annual</button>
</div>If the container has no items at all, such as an empty <ul> rendered before data arrives, leave it out until there is something to put in it.
Done when: every container role has at least one child with a required role, and the check above comes back clean.
Do not
- Do not add a container role to an element whose children you can’t also give roles to.
- Do not leave an empty
<ul>,<ol>or<table>on the page. - Do not use
role="presentation"on the items to silence the check; it removes them from the structure entirely.
FAQ
Why is an empty <ul> reported?
A <ul> is a list, and a list needs list items. An empty one is announced as a list with no items, which tells a screen reader user nothing. Leaving it out of the markup until it has content avoids that.
Do the children have to be direct children?
Not for this check. Probeo accepts a required role at any depth inside the container. Assistive technology handles intervening elements best when they have no role of their own, so keep wrappers to plain <div> or <span>.
What if the items come from aria-owns?
Probeo can’t confirm children referenced by aria-owns, so it sets those containers aside instead of reporting them. Check them by hand in the accessibility tree.
Which product detects this
Probeo checks this on every page of your site as part of Site Clarity. This page explains one instance; the product tells you every page that has it.