squirrelscan
GitHub

role=treeitem: naming every node in a tree

A tree node that announces only its position in the tree is unusable. squirrel checks every role=treeitem for something to read out.

What a treeitem is

A tree presents nested items a user can expand and collapse: a file browser, a category picker, a table of contents. Each node carries role="treeitem" and a screen reader announces its name along with its level and position.

The name normally comes from the node’s own text. Icon-only nodes, and nodes whose label sits in a sibling element, need aria-label or aria-labelledby instead.

What squirrel checks

One check, aria-treeitem-name, at severity error. It finds every [role="treeitem"] element.

A name is accepted from a non-empty aria-label, an aria-labelledby pointing at an element with text, the node’s own text content, or a nested <img> with non-empty alt.

  • Fail with N treeitem(s) without accessible names. Items read li#node-3 or bare tag name, up to ten with a count of the rest.
  • Pass with N treeitem(s) have accessible names.
  • Info with No treeitem elements found.

Text content is read from the whole subtree, so a node whose only text lives inside a nested group counts as named. That is generous: check the announced name in a screen reader before trusting a pass on a deeply nested tree.

  • aria-treeitem-name-unconfirmed warns with N treeitem(s) without accessible names may be hidden by a style (unconfirmed) for a treeitem a style may hide in a way squirrel cannot settle.

Hidden treeitems

A treeitem out of the accessibility tree has nothing to announce, so it is not reported: one with the hidden attribute (except hidden="until-found"), aria-hidden="true" on itself or an ancestor, display: none on itself or an ancestor (set inline or by a <style> rule squirrel can decide), or inside a closed <dialog> or an unopened popover. It still needs a name before it is shown. When every unnamed one is skipped this way, the pass reads All treeitems in the accessibility tree have accessible names, with hiddenWithoutName in its details.

squirrel reads the markup, inline styles and the page’s <style> blocks, the same way as form-labels; see form-labels for what it can and cannot decide. External stylesheets are not read. A treeitem that carries a no-js class, itself or on an ancestor other than <html>, is reported as unconfirmed on a page with an external stylesheet or an @import, because a theme usually hides that fallback with a rule such as .js .no-js { display: none } once JavaScript runs.

How to fix it

<li role="treeitem" aria-expanded="false">
  <span>Invoices</span>
  <ul role="group">
    <li role="treeitem">2026-Q1.pdf</li>
  </ul>
</li>

Keep the node’s own label in the node and its children in a nested role="group". That is also what makes the level and position announcements correct.

An icon-only node needs aria-label on the treeitem itself, not on the icon inside it.

Rule IDa11y/aria-treeitem-name
CategoryAccessibility
ScopePer-page
Severityerror
Weight7/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/aria-treeitem-name"]

Disable all Accessibility rules

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

Enable only this rule

[rules]
enable = ["a11y/aria-treeitem-name"]

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. Every unnamed node is listed with a selector you can search for. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close