Inclusive Design 24 (#id24) heading fix for VoiceOver using role="text" (simpler fix)
Channel: Patrick Lauke
Date: 2022-08-06

Here's a small fix that we recently made to the https://inclusivedesign24.org website.

0:00 - Problematic headings with nested spans
0:59 - How VoiceOver/iOS announces them
1:54 - Adding a wrapper span with  role="text"
2:36 - How VoiceOver/iOS announces the fixed headings
3:03 - Thoughts on this ugly hack

On our schedule page, each session has a heading. The heading is admittedly a bit overstuffed, but it includes various pieces of information - the time and date of the session in the user's local time, the UTC time and date of the session, the speakers, and the title of the session.

For styling purposes, all these pieces  are wrapped in a series of nested span elements - but these are all still part of the same h2 heading.

While admittedly a bit awkward, this is a valid structure.

However, it leads to a particularly awkward experience in VoiceOver - swiping through the page, each span is announced separately, and a user has to swipe separately through each piece of the heading.

Each span is also counted as a separate heading when VoiceOver announces the total number of headings on the page.

In the end, the solution was to wrap a span with role="text" to the content of the heading.
role="text" is non-standard - it was originally proposed for ARIA 1.1, but was eventually dropped. But it has an effect in VoiceOver: each separate span is now seen as just a text node. When swiping through the page, the h2 heading only creates a single stop, and VoiceOver also correctly announces the total number of headings on the page.

Note that we also added a few additional, visually-hidden span elements here, with some extra non-breaking spaces and punctuation, to smooth over how these headings are announced by screen readers in general.

To be clear, this is an ugly, proprietary hack targeted purely at fixing what is essentially a bug in VoiceOver. But it does lead to a much better user experience.