
role="treeitem" needs an accessible name from visible text, aria-label, or aria-labelledby. Without one, a screen reader announces an anonymous "tree item" and your whole tree widget turns into a guessing game. The fix is usually one attribute, or un-hiding a label that was already there.What this check actually flags
A tree widget is the file explorer pattern: role="tree" on the container, role="treeitem" on every node, and aria-expanded on the branches that open and close. Documentation sidebars, category pickers, folder browsers, org charts, and permission managers all lean on it. This check fires when any element carrying role="treeitem" ends up with no accessible name: no readable text content, no aria-label, no aria-labelledby, nothing the browser can hand to assistive technology.
The failure is invisible in a normal browser session, which is exactly why it survives code review. The node looks fine on screen, the label is right there in pixels. But NVDA or VoiceOver lands on it and announces something like "tree item, 2 of 14, collapsed" and then stops. The user knows a branch exists and where it sits in the list, and has no idea what it is. Multiply that by every node and the whole widget is dead weight for anyone relying on a screen reader.
Why I treat this as a blocker, not a nice-to-have
axe-core ships this as the aria-treeitem-name rule and rates it serious. It maps to WCAG success criterion 4.1.2, Name, Role, Value: the moment you claim a role, you owe assistive technology a name to go with it. A control that announces its role but not its identity is arguably worse than a plain div, because it promises interaction it cannot explain.
There is a practical SEO angle too. Trees almost always hold navigation, and the same build mistakes that strip names from the accessibility tree, icon fonts as the only content, empty shells hydrated after load, labels tucked behind aria-hidden, also hand crawlers weak or empty anchor text. Every time I have traced this issue in an audit, the root cause was a rendering or templating problem worth fixing on its own merits. It also drags down the Lighthouse accessibility score that stakeholders actually look at, and unnamed interactive controls are exactly the kind of finding accessibility complaints and demand letters get built on.
How a treeitem gets its name, and how it loses it
Browsers compute an accessible name in a strict order: aria-labelledby wins, then aria-label, then the element's own text content. That sounds hard to fail, yet teams manage it in very predictable ways:
- Icon-only nodes. The node is a folder glyph with no text, and nobody added a label attribute.
- aria-hidden on the label. A wrapper span carries
aria-hidden="true", usually copy-pasted from an icon element, and it removes the visible text from name computation. - Icon fonts and CSS-injected content. The visible "label" is a glyph painted by CSS. What reaches the accessibility tree is a meaningless private-use character, or nothing at all.
- Virtualized lists. The framework renders placeholder shells for scroll performance and fills them in later. Both audits and real users catch the empty state.
- Truncation scripts. Long labels get moved into a
titleattribute or a tooltip component, and the node itself is left hollow.
Choosing the right naming method
| Method | How it works | Best for | Watch out for |
|---|---|---|---|
| Visible text content | The node's own text becomes the name automatically | Almost every tree | An aria-hidden wrapper silently erases it |
| aria-labelledby | Points at the id of another element holding the text | Names composed from several parts | A broken or duplicate id fails with no error |
| aria-label | Hardcoded string on the treeitem itself | Icon-only nodes | Needs translation and drifts from the visual label |
| title attribute | Last-resort fallback in name computation | Nothing, honestly | Inconsistent support across screen readers, skip it |
How to detect it
Start with axe DevTools on any page that renders the tree: it flags aria-treeitem-name by name and lists the exact offending nodes. Lighthouse runs axe-core under the hood, so its accessibility category catches this too and slots neatly into CI. For coverage beyond a single page, Screaming Frog (version 20 and up) can run axe accessibility checks during a crawl, which tells you every template that ships the broken component instead of leaving you to play whack-a-mole one URL at a time.
A quick console check works when you just want an answer right now:
[...document.querySelectorAll('[role="treeitem"]')]
.filter(n => !n.innerText.trim()
&& !n.getAttribute('aria-label')
&& !n.getAttribute('aria-labelledby'))Then do one manual pass with a real screen reader. NVDA is free on Windows, VoiceOver is built into macOS. Arrow through the tree and listen. Five minutes of that tells you more than any report.
How to fix it, step by step
- Inventory the damage. Run axe or the console snippet and identify which component renders the unnamed nodes. It is nearly always one shared component, which makes this a cheap fix with wide reach.
- Prefer visible text. Put the label inside the treeitem as real text. Visible text stays in sync with the UI, gets translated with everything else, and never goes stale.
- Label icon-only nodes with
aria-labelthat says what the node is, not what the icon looks like: "Invoices", never "folder icon". - Audit
aria-hidden. Confirm no wrapper span or ancestor is hiding the label from the accessibility tree. This is the most common silent killer. - Name loading states. If nodes hydrate asynchronously, give the placeholder a name like "Loading" instead of shipping empty shells.
- Re-test and lock it in. Verify with axe and a screen reader, then wire the rule into CI so a component refactor cannot quietly reintroduce it.
DO
- Put the label in real, visible text inside the treeitem whenever possible
- Use
aria-labelonly for genuinely icon-only nodes - Keep
aria-expandedon branch nodes so state is announced - Test the whole widget with a keyboard plus NVDA or VoiceOver before shipping
- Add the axe rule to CI so regressions get caught automatically
DON'T
- Don't wrap the label in
aria-hidden="true" - Don't rely on an icon font glyph as the node's only content
- Don't stuff the name into a
titleattribute and call it done - Don't ship placeholder shells with no name during async loading
- Don't slap
role="treeitem"on things that are not trees in the first place
Questions that come up
Is this a hard WCAG failure or just a best practice?
Does fixing this help rankings directly?
Can I use the title attribute as the accessible name?
Should I even build a custom ARIA tree?
Unnamed treeitems rarely travel alone.
If one component ships without accessible names, the rest of the template usually has friends. I run a full technical and accessibility crawl and hand you a prioritized fix list your devs can action the same week.
Claude Vincent is a technical SEO consultant focused on crawlability, rendering, and AI-search visibility. He writes the field guides and case studies at SEO ProCheck, with a bias toward the durable, unglamorous work that decides whether search engines and AI answer engines can actually read and cite a site.
About SEO ProCheck
Technical SEO consulting and GEO strategy with 20 years of enterprise experience. Case studies, resources, and tools for search and AI visibility.
Work With Me
Technical SEO audits, GEO strategy, site migrations, and international SEO. Hourly consulting for teams who need hands-on support, not just reports.







