ARIA Rule

ARIA roles that need a particular parent must sit inside it aria-required-parent

Some roles only make sense inside a particular parent. A menuitem lives in a menu or menubar, an option in a listbox, a tab in a tablist, a treeitem in a tree, a row in a table, grid or treegrid. Put the child role on an element with no such parent and the screen reader cannot say how many there are, which one this is, or how to move between them.

Websight reports this as Certain ARIA roles must be contained by particular parents

Who it affects

Someone using a screen reader lands on Copy link, menu item and has no idea a menu has opened, how many items it has, or that the arrow keys should move through it.

Someone using a screen reader in browse mode reads the items as loose text scattered in the page, with no start and end to the menu.

Someone using voice control may find the item is not treated as a menu command at all, so saying its name does nothing.

Broken versus fixed

Both examples are live and rendered in a sandboxed frame. The broken one is deliberately broken.

Broken

Looks like a menu. A screen reader announces: Copy link, menu item, with no menu around it, no 1 of 3, and no hint that arrow keys apply.

Fixed

Announced: Share, menu, Copy link, menu item, 1 of 3. The arrow key handling is script you still need to write; the demo is static.

Show the markup behind both examples
Broken
<style>body{font-family:system-ui;padding:16px;margin:0}.btn{border:1px solid #cbd5e0;background:#fff;border-radius:6px;padding:6px 12px}.pop{margin-top:6px;width:180px;border:1px solid #cbd5e0;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.08);background:#fff}.pop div{padding:8px 12px}</style>
<button class="btn" aria-expanded="true" aria-haspopup="menu">Share</button>
<div class="pop">
  <div role="menuitem" tabindex="0">Copy link</div>
  <div role="menuitem" tabindex="-1">Email</div>
  <div role="menuitem" tabindex="-1">Print</div>
</div>
Fixed
<style>body{font-family:system-ui;padding:16px;margin:0}.btn{border:1px solid #cbd5e0;background:#fff;border-radius:6px;padding:6px 12px}.pop{margin-top:6px;width:180px;border:1px solid #cbd5e0;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.08);background:#fff}.pop div{padding:8px 12px}</style>
<button class="btn" id="share" aria-expanded="true" aria-haspopup="menu">Share</button>
<div class="pop" role="menu" aria-labelledby="share">
  <div role="menuitem" tabindex="0">Copy link</div>
  <div role="menuitem" tabindex="-1">Email</div>
  <div role="menuitem" tabindex="-1">Print</div>
</div>

Test it by hand in two minutes

  1. Inspect the element with the child role and walk up its ancestors. Look for the parent role it needs (menu or menubar for menuitem, listbox for option, tablist for tab, tree for treeitem).
  2. If a wrapper sits between the child and the parent, check it has role="presentation" or role="none", or is a group the parent allows.
  3. Turn on VoiceOver (Cmd+F5) or NVDA and move to the item. You should hear the parent role first, or a position like 1 of 3.
  4. Press the arrow keys. In a real menu, listbox or tablist the screen reader should move through the items and announce each one.

The fix

Before
<div>
  <div role="menuitem">Copy link</div>
  <div role="menuitem">Email</div>
</div>
After
<div role="menu" aria-label="Share">
  <div role="menuitem">Copy link</div>
  <div role="menuitem">Email</div>
</div>

Add the parent role to the element that wraps the items and give it a name. If the items are really just links in a dropdown, drop role="menuitem" and use a plain list of links instead; menu roles bring keyboard expectations with them.

Tab with no tablist

Before
<nav>
  <button role="tab" aria-selected="true">Details</button>
  <button role="tab">Reviews</button>
</nav>
After
<div role="tablist" aria-label="Product">
  <button role="tab" aria-selected="true">Details</button>
  <button role="tab" aria-selected="false">Reviews</button>
</div>

A tab needs a tablist parent. The nav element is not one.

Options in a plain div

Before
<div>
  <div role="option">Small</div>
  <div role="option">Large</div>
</div>
After
<div role="listbox" aria-label="Size">
  <div role="option" aria-selected="true">Small</div>
  <div role="option" aria-selected="false">Large</div>
</div>

Options need a listbox. If this is a dropdown in a form, a native select is simpler and already correct.

Common mistakes

  • Putting the parent role on the grandparent and leaving an unlabelled div in between.
  • Giving the parent role to the trigger button rather than the container of items.
  • Removing the child role from every item to clear the finding, so the menu is now a pile of divs.
  • Using role="menu" for site navigation; those are links, and a list of links is what screen readers expect there.
  • Adding role="list" to the wrapper because a menu is a list, which is not the parent menuitem needs.

WCAG criteria this rule maps to

axe-core tags this rule as WCAG 2.2 level A. It is one of the checks for:

The scanner can see a child role with no suitable parent in its ancestors, allowing for presentational wrappers. It cannot tell whether the menu or listbox actually works with a keyboard. Automated tools find only part of the accessibility problems on a page; a pass on this rule is not a pass on the criterion.

Scan your own page for this and 63 other checks

Open any page in Chrome, click Websight, and get a score out of 100 with every issue explained and a draft fix snippet per element.

Add Websight to Chrome

Free scans, no account, nothing leaves your computer. Automated tools find only part of the problems; the extension says so on every report.