squirrelscan
GitHub

ARIA roles that require a particular parent role

role=listitem outside a list, or a tab outside a tablist, loses its meaning. squirrel checks explicit roles and native li, tr, td and option.

What a required parent role is

Some ARIA roles only mean something inside a container. A tab belongs to a tablist, an option to a listbox, a listitem to a list. Assistive technology reads the pair together to say “tab 2 of 5” or “list, 12 items”.

Take the child out of its container and both halves break. The child is announced with a role that implies a position it cannot report, and the container never appears.

What squirrel checks

One check, aria-required-parent, at severity error, in two passes.

The first pass covers thirteen child roles and their permitted parents: cell, columnheader, gridcell and rowheader need a row; listitem needs a list or group; menuitem, menuitemcheckbox and menuitemradio need a menu, menubar or group; option needs a listbox or group; row needs a grid, rowgroup, table or treegrid; rowgroup needs a grid, table or treegrid; tab needs a tablist; treeitem needs a tree or group.

For each element it walks up the ancestor chain comparing effective roles. An effective role is the explicit role when there is one, otherwise an implicit role mapped from the tag: <ul>, <ol> and <dl> are lists, <table> a table, <tr> a row, <td> a cell, <th> a column header, <select> a listbox, <nav> a navigation landmark. An <input> maps by its type, and an <a> counts as a link only when it has an href.

The second pass repeats the test for native elements carrying no explicit role: <li>, <option>, <tr>, <td>, <th>, <thead>, <tbody> and <tfoot>.

  • Fail with N element(s) missing required parent role. Items read div[role="tab"]: needs parent with role=tablist, or li (implicit role="listitem"): needs parent with role=list|group, up to ten with a count of the rest.
  • Pass with All elements have required parent roles.

How to fix it

<div role="tablist" aria-label="Account settings">
  <button role="tab" aria-selected="true">Profile</button>
</div>

Move the child inside the container, or give the container the matching role.

The ancestor walk has no depth limit, so a <div> sitting between a <ul> and its <li> still satisfies this rule even though HTML does not allow it. a11y/listitem checks the direct parent and catches that case.

Rule IDa11y/aria-required-parent
CategoryAccessibility
ScopePer-page
Severityerror
Weight7/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/aria-required-parent"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/aria-required-parent"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. Each element is listed with the parent role it needs and does not have. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close