squirrelscan
GitHub

role=text with focusable children: why it breaks

role=text flattens a subtree into one announced string, so a link inside it cannot be operated. squirrel reports every case it finds.

What role=text is

role="text" asks assistive technology to treat an element’s whole subtree as a single flat string, so a heading broken across several <span> elements is announced as one phrase instead of several fragments.

It is not defined in the WAI-ARIA 1.2 recommendation, nor in the 1.3 working draft, so it is a vendor extension and support varies. Where it is honored, the subtree stops being a set of separate objects, and anything interactive inside it stops being reachable as a separate object too.

What squirrel checks

One check, aria-text, at severity warning. It finds every [role="text"] element and looks for focusable descendants. Focusable means matching a[href], button, input, select or textarea, or carrying any tabindex other than -1. The tabindex lookup is case-insensitive, so React’s tabIndex in server-rendered markup is caught.

  • Warn with N role="text" element(s) with focusable descendants. Items read span#hero-title (2 focusable), up to ten with a count of the rest.
  • Pass with All N role="text" element(s) have no focusable descendants.
  • Info with No elements with role="text" found.

How to fix it

<h2>
  <span>Read our</span>
  <a href="/report">2026 accessibility report</a>
</h2>

Take role="text" off the container, or move the interactive element outside it. Keeping both means the link is announced as part of a sentence with no way to activate it.

If the goal was to stop a screen reader chopping a heading into pieces, the usual cause is extra markup for styling. Removing the wrapper elements, or setting them to display: inline, fixes the announcement without a vendor role.

Rule IDa11y/aria-text
CategoryAccessibility
ScopePer-page
Severitywarning
Weight4/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/aria-text"]

Disable all Accessibility rules

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

Enable only this rule

[rules]
enable = ["a11y/aria-text"]

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 container is listed with a count of the focusable elements inside it. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close