About This Accessibility Rule
The role="text" attribute is a workaround for a specific screen reader behavior. When a text node is split by inline markup — for example, <h1>Good morning, <span>friend</span></h1> — VoiceOver on macOS and iOS may announce this as two separate phrases instead of reading it as one continuous string. Wrapping the content in an element with role="text" tells the screen reader to treat everything inside as a single text node, which produces a smoother reading experience.
However, role="text" comes with an important side effect: it overrides the semantic role of the container element and all of its descendants. Every child element is effectively treated as plain text. This means that if any descendant is focusable — such as an <a>, <button>, or <input> — the screen reader can no longer recognize it for what it is. The element remains in the tab order because the browser still considers it focusable, but VoiceOver will not announce its name, role, or value. The result is a “ghost” tab stop: the user presses Tab, focus moves to an element, but nothing is announced. This is a serious problem for blind users and keyboard-only users who rely on focus announcements to navigate and interact with a page.
Who is affected
-
Blind users using screen readers like VoiceOver cannot identify focusable elements trapped inside a
role="text"container. They encounter empty tab stops with no announcement. - Keyboard-only users experience confusing focus behavior — tabbing lands on elements that provide no information about what they are or what they do.
- Users with low vision who use screen readers in combination with magnification may also be impacted.
Related standards
This rule is classified as a Deque Best Practice. While it does not map directly to a specific WCAG success criterion, it relates closely to:
-
WCAG 4.1.2 (Name, Role, Value): All user interface components must have an accessible name and role that can be programmatically determined. A focusable element inside
role="text"loses its role and may lose its accessible name. - WCAG 2.4.7 (Focus Visible): Users must be able to understand where focus is. An empty tab stop with no screen reader announcement undermines this.
- WCAG 2.1.1 (Keyboard): All functionality must be operable through a keyboard. If a user cannot identify a focusable element, they effectively cannot use it.
How to fix it
-
Move focusable elements outside the
role="text"container so they retain their semantic roles and are properly announced. -
Only wrap non-interactive content with
role="text". Use it exclusively for its intended purpose: merging split text nodes into a single phrase for screen readers. -
Remove
role="text"entirely if the content includes interactive elements and you cannot restructure the markup. It is better to have VoiceOver announce text in separate phrases than to create inaccessible interactive elements.
Examples
Incorrect: focusable link inside role="text"
The link is inside the role="text" container, so VoiceOver treats it as plain text. The user can Tab to the link, but nothing is announced.
<p role="text">
For more details, visit
<a href="/about">our about page</a>.
</p>
Incorrect: focusable button inside role="text"
<div role="text">
Your session is about to expire.
<button>Extend session</button>
</div>
Correct: role="text" wraps only non-interactive content
The role="text" is applied to a <span> that contains no focusable elements, while the link sits outside it.
<p>
<span role="text">Good morning, <span>friend</span></span>.
Visit <a href="/dashboard">your dashboard</a> to get started.
</p>
Correct: role="text" on a heading with split text nodes
This is the primary use case for role="text" — ensuring VoiceOver reads a heading as a single phrase rather than separate fragments.
<h1>
<span role="text">Hello <br/>World</span>
</h1>
Correct: restructured to avoid the problem entirely
If you cannot separate interactive and non-interactive content, remove role="text" altogether.
<p>
For more details, visit
<a href="/about">our about page</a>.
</p>
The key principle is straightforward: role="text" is meant for presentation of static text only. Never place links, buttons, inputs, or any other focusable element inside it.
Learn more:
Help us improve our guides
Detect accessibility issues automatically
Rocket Validator scans thousands of pages with Axe Core and the W3C Validator, finding accessibility issues across your entire site.