dotjay.com / lab / tests / screen-readers / voiceover-text-breaks-workarounds

VoiceOver text breaks and workarounds (aria-label, role="text")

Last updated: January 26, 2024

This aim here is to allow multiple styles or a visible line break within a single heading element. Most screen readers handle this well, but VoiceOver reads headings with child elements as multiple items.

Update, March 2023

In my testing below, the only techniques that successfully allieviates this problem, is to use role="text" on each child element or role="text" on a wrapper element.

When role="text" is applied to a container element, it forces VoiceOver to recalculate the accessible name of the heading element, reducing it to a single element. However, note that role="text" is not official spec now. This works worked (see update below) on macOS and iOS.

This would stop VoiceOver from treating each child element as a separate heading as shown in this demo and explanation by Patrick H Lauke on YouTube.

Update, January 2024

It seems that VoiceOver now has a more consistent experience across each of the techniques below. While VoiceOver now reads the headings as a single announcement, it also always announces that there are multiple items within the element:

heading level 4, 2 items, The digital world [short pause] as it should be

I'm in the process of testing this further in order to update the results table.

Related question on Stack Overflow: How do I stop Apple VoiceOver double-reading headings with multiple elements?

Test cases

1. Plain heading

For control purposes.

The digital world as it should be

2. Heading with line break element

This creates two lines, but VoiceOver reads the headings as multiple items.

The digital world
as it should be

3. Heading with pre-line style and line return

Using CSS3's write-space, inspired by Sandrina Pereira's testing and results. This does not create the a visible line break required.

The digital world as it should be

4. Heading with pre-line style and encoded line return

This does not create the a visible line break required.

The digital world as it should be

5. Two-style heading text

Uses an inline child element to create two styles. VoiceOver reads this heading as multiple items.

The digital world as it should be

6. Two-style heading text with role=text

role="text" is added to each child element, causing VoiceOver to recalculate the accessible name of the heading element, reducing it to a single element.

role=text is non-standard, but supported by Webkit browsers. See demo and explanation by Patrick Lauke on YouTube.

Note that this should not be used when there's a link inside the heading. See test 11 below.

The digital world as it should be

7. Two-style heading text with aria-label

In theory, this should work and be well supported, although it should be used with caution as aria-label overrides the accessible name in the accessibility tree. However, in practice, this approach still results in VoiceOver reading the heading as multiple items.

The digital world as it should be

8. Two-style wrapped heading text

Uses a block level child element to create a visible line break and two styles. VoiceOver reads this heading as multiple items.

The digital world
as it should be

9. Two-style wrapped heading text with role=text

A child element with role="text" is added that wraps all the heading text, causing VoiceOver to recalculate the accessible name of the heading element, reducing it to a single element.

role=text is non-standard, but supported by Webkit browsers. Note that this should not be used when there's a link inside the heading.

The digital world as it should be

10. Two-style wrapped heading text with aria-label

In theory, this should work and be well supported, although it should be used with caution as aria-label overrides the accessible name in the accessibility tree. However, in practice, this approach still results in VoiceOver reading the heading as multiple items.

The digital world
as it should be

11. Two-style wrapped heading text with link and role=text

The addition of role="text" means that the link inside the heading cannot be accessed or activated.

The digital world as it should be

12. Two-style wrapped heading text with link and aria-label

When there's a link inside a heading or part of a heading, it is expected behaviour that there are two distinct elements. However, we can use aria-label to ensure that the heading text is announced.

The digital world
as it should be

The digital world
as it should be

Results

A "Pass" indicates that VoiceOver treats the heading as a single element, which is what we want.

Results last updated March 2023. New tests are underway.

Test macOS VoiceOver + Safari macOS VoiceOver + Chrome iOS VoiceOver + Safari iOS VoiceOver + Chrome
1. Plain heading ✅ Pass ✅ Pass ✅ Pass ✅ Pass
2. Heading with line break element ❌ Fail (2 items) ❌ Fail (3 items) ❌ Fail (2 items) ❌ Fail (2 items)
3. Heading with pre-line style and line return ✅ Pass ✅ Pass ✅ Pass ✅ Pass
4. Heading with pre-line style and encoded line return ✅ Pass ✅ Pass ✅ Pass ✅ Pass
5. Two-style heading text ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items)
6. Two-style heading text with role=text ✅ Pass ❌ Fail (2 items) ✅ Pass ✅ Pass
7. Two-style heading text with aria-label ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items, each announcing the full aria-label) ❌ Fail (2 items, each announcing the full aria-label)
8. Two-style wrapped heading text ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items)
9. Two-style wrapped heading text with role=text ✅ Pass ❌ Fail (2 items) ✅ Pass ✅ Pass
10. Two-style wrapped heading text with aria-label ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items, each announcing the full aria-label) ❌ Fail (2 items, each announcing the full aria-label)
11. Two-style wrapped heading text with link and role=text ❌ Fail (1 item, but link cannot be accessed or activated) ❌ Fail (2 items) ❌ Fail (1 item, but link cannot be accessed or activated) ❌ Fail (1 item, but link cannot be accessed or activated)
12. Two-style wrapped heading text with link and aria-label ❌ Fail (2 items) ❌ Fail (2 items) ❌ Fail (2 items, first announcing the full aria-label, then link and its link text) ❌ Fail (2 items, first announcing the full aria-label, then link and its link text)

Back to the Tests index.

Back to the Lab index.