role="text" tells assistive technology to treat an element and all of its children as a single text run, with no internal structure. It exists for one narrow case: forcing a screen reader to read a fragmented piece of text — split across many <span> elements for styling, animation, or letter-by-letter rendering — as if it were a single sentence. The aria-text audit fails when role="text"is misused: applied to interactive children, used to hide content, or used as a generic "turn off semantics" switch.
role="text" can fail both.aria-text fires when an element with role="text" contains focusable or otherwise interactive children — those children get hidden from assistive tech.role="text"; Chrome and Firefox have limited or no support. Treat it as a Safari-specific hint, not a universal contract.role="text" as a non-standard role and it later entered the WAI-ARIA 1.3 Editor's Draft. It is still treated cautiously by the W3C and by accessibility checkers.aria-label on a wrapping element, or by writing the text without internal element splits in the first place.Think of role="text" like glue: it forces a row of separate cards into one solid block. Useful in narrow situations, dangerous if you glue over a button by mistake.
Designers and animation engines love splitting text into per-letter or per-word spans. CSS effects like staggered fade-ins, reveal-on-scroll, and animated underlines all rely on it. The downside: a screen reader walking the accessibility tree may pause between every span, treat each span as a separate text run, or skip whitespace between them. The result is something like "H. e. l. l. o." instead of "Hello."
role="text" on the wrapping element collapses all of those spans into a single text run, so the screen reader announces the full string smoothly. That is the entire intended use case.
The audit fires when role="text" is applied incorrectly. The two patterns it catches:
role="text" tells assistive tech to flatten everything inside the element into a single string. If one of the children is a <button>, an <a> with href, or an <input>, that control disappears from the accessibility tree. Keyboard and screen-reader users cannot reach it at all.
Developers sometimes use role="text" on a heading or list to "turn off" the role they did not want. This is a misuse — role="none" or role="presentation" exists for that purpose, and even those should be a last resort. role="text" is meant to combine fragmented text, not to silence semantics.
<!-- BAD: a button is hidden inside the flattened text -->
<h2 role="text">
Welcome back, <button>edit name</button>
</h2>
<!-- BAD: role="text" on a heading to "remove" the heading semantics -->
<h2 role="text">Section title</h2>
<!-- BAD: role="text" wrapping a real link -->
<span role="text">
Read our <a href="/policy">privacy policy</a>
</span>
<!-- GOOD: animation spans flattened into one text run, no interactive children -->
<h2 role="text" aria-label="Hello there">
<span>H</span><span>e</span><span>l</span><span>l</span><span>o</span>
<span> </span>
<span>t</span><span>h</span><span>e</span><span>r</span><span>e</span>
</h2>
<!-- GOOD: keep the link outside the role="text" wrapper -->
<p>
<span role="text">Welcome back to your dashboard.</span>
<a href="/policy">Read our privacy policy</a>
</p>
<!-- GOOD: an even simpler fix — don't split the text at all -->
<h2>Hello there</h2>The role only adds value when text is genuinely fragmented across many elements. If your markup is <p>Hello</p>, you do not need it. Remove the role and the audit clears.
If the wrapped block contains a button, link, or input, restructure the markup so the interactive control sits as a sibling of the role="text" element rather than a descendant of it.
When you do flatten letter-by-letter spans, set aria-label on the wrapper to the human-readable string. That way Safari/VoiceOver, and any other engine that supports role="text", announce a clean version even if the text computation differs.
The most robust fix is not splitting the text in the first place. Render the full sentence in a single text node and apply animation via CSS pseudo-elements or animated ::first-letter selectors. The accessibility tree stays clean and the visual result is identical.
aria-text audit, lists every misuse with the offending element and its hidden children, and offers a one-click GitHub PR fix where applicable.role="text" and verify that the "Children" field is empty (the role flattens them). If you see interactive descendants listed, those are now invisible to assistive tech.role="text" is most consistently honored. Tab and arrow through the wrapper; if any interactive child cannot be reached, the role is hiding it.The single legitimate use is collapsing many spans into one text run. Anything else is misuse.
Buttons, links with href, inputs, summary elements, anything with tabindex — none of these should sit inside a role="text" wrapper. They will disappear from the accessibility tree.
Browsers compute the flattened text differently. An explicit aria-label on the wrapper makes the announcement deterministic.
role="text" is not a way to remove a heading or list role you did not want. Use semantic markup that fits the meaning, or remove the original role.
Chrome and Firefox have limited support. Do not write code that depends on the role behaving the same in every browser; design the markup to be readable even without it.
CSS pseudo-elements and animated SVG can usually deliver the same visual effect without per-letter spans. If you can avoid the split, you can avoid the role.
role="text" on an entire paragraph or article kills the structure inside (lists, line breaks, emphasis cues). Keep it scoped to the smallest fragment that needs flattening.
Open Chrome DevTools' Accessibility tab and confirm the wrapped element has no interactive descendants in the tree. Automated audits catch the obvious cases; the tree shows the truth.
If you set aria-label on the wrapper, update it whenever the visible text changes. Drift between the two is its own accessibility bug.
eslint-plugin-jsx-a11y rules including jsx-a11y/aria-role and jsx-a11y/no-noninteractive-element-interactions help flag many of the misuse patterns before merge.
What's happening: A heading wraps the user's name and an "edit" pencil button. The developer adds role="text" to make the heading read smoothly. The pencil button is now invisible to screen readers and keyboard users.
Fix: Move the pencil button outside the heading. Keep the heading as plain text; place the button as a sibling element.
What's happening: A developer wants to remove a heading semantic and reaches for role="text". The audit flags it because the role is also hiding nested structure.
Fix: Use the right element instead. If it should not be a heading, change it from <h2> to <p> or <div>. role="presentation" is the official "remove semantics" role, but it is rarely the best answer.
What's happening: A text-reveal library wraps every word in <span> for staggered animation. Some screen readers add pauses between spans.
Fix: Add role="text" with an aria-label on the wrapper. Verify there are no interactive descendants before doing so.
What's happening: A snapshot test passes in Chrome and fails in Safari (or vice versa) because the two engines compute the text run differently when role="text" is present.
Fix: Set an explicit aria-label on the wrapper. The label takes priority over computed text in every engine that supports the role.
It verifies that elements with role="text" do not contain focusable or interactive descendants (buttons, links with href, inputs, etc.). The role flattens an element into a single text run, so any interactive child becomes unreachable.
It originated in WebKit as a non-standard role and was later added to the WAI-ARIA 1.3 Editor's Draft. It is supported most consistently in Safari with VoiceOver. Treat it as a hint that helps Safari users, not a universal contract.
When text is genuinely fragmented into multiple elements for visual reasons (per-letter or per-word animation, decorative wrapping spans) and you observe that screen readers introduce unwanted pauses or split the text in the announcement. Anything else is overuse.
role="presentation" (also role="none") removes the element's implicit role but keeps its structural children visible to assistive tech. role="text" goes further: it flattens everything inside into a single text run and makes the children disappear from the accessibility tree. They are not interchangeable.
Indirectly. The role does not directly change the rendered HTML that Google indexes, but misuse causes a noisier accessibility tree and weaker structured signals — which AI answer engines like Google AI Overviews and Perplexity rely on when picking citation candidates. Used correctly on small fragments, it is neutral. Used incorrectly to silence whole headings, it can hurt.
Often, yes. aria-label on a wrapping element overrides the computed text in most engines and is more widely supported than role="text". If you only need a clean announcement of an animated string, aria-label alone is usually enough — and it sidesteps the misuse risks of role="text" entirely.
Yes. Hiding interactive children fails Success Criterion 4.1.2 Name, Role, Value (Level A) — the controls have no announced role at all. Suppressing structural roles can also fail 1.3.1 Info and Relationships (Level A) when programmatic structure no longer matches visual structure.
role="text" is a narrow tool for one specific problem: smoothing out screen-reader announcements when designers split text into many elements for visual effect. Used inside that scope it is harmless and helpful. Used to wrap interactive children or to silence semantic structure, it actively breaks accessibility and earns a Level A WCAG failure. The safest defaults are: do not split text unless you have to; if you must, scope role="text" to the smallest fragment and pair it with an explicit aria-label.
Run a Greadme deep scan to see exactly which elements on your site are using role="text" incorrectly and which interactive controls are being hidden as a result — then restructure the markup so the role only covers plain fragmented text.