Page MenuHomePhabricator

Headings inaccessible for assistive tech on mobile
Closed, ResolvedPublic

Description

Users with visual impairments on iPhone rely on headings as the primary navigation landmarks for browsing web pages. The VoiceOver Rotor lets users choose a navigation mode by rotating two fingers on the screen. When "Headings" is selected, VoiceOver can jump directly between headings on a page, letting users skim articles without reading every word.

Rotor heading navigation is broken on Wikipedia for iOS VoiceOver users. When a user selects "Headings" from the Rotor on a Wikipedia article, only the page title is surfaced. All article section headings are invisible to VoiceOver, which responds with an error haptic and says "heading not found". This is a critical accessibility regression that removes a fundamental navigation mechanism for blind and low-vision users.

User report (via Slack thread, forwarded to accessibility mailing list):

Hello Wikipedia team, my name is Diego and I am visually impaired and a VoiceOver user of iOS devices. Recently, on Wikipedia articles when I browse them, I cannot browse by headings within the rotor option. It gives me an error haptic feedback and it says "heading not found." This issue is affecting many VoiceOver users. I hope it can be resolved soon. — Diego (using Safari on iOS)


Steps to replicate the issue:

  1. Open Safari on an iPhone with VoiceOver enabled (Settings > Accessibility > VoiceOver > On, or ask Siri: "Turn on VoiceOver")
  2. Navigate to any Wikipedia article with multiple sections (e.g. https://en.wikipedia.org/wiki/Paris)
  3. Once the page has loaded, activate the VoiceOver Rotor by rotating two fingers on the screen
  4. Swipe through Rotor options until you reach "Headings"
  5. With "Headings" selected, swipe down with one finger to jump to the next heading

What happens?:

  • VoiceOver produces an error haptic feedback and announces "heading not found"
  • Only the page title and "Related Articles" headings are surfaced.
broken headingsfunctional headings
voiceover-rotor-safari-headings-minerva.gif (600×889 px, 542 KB)
voiceover-safari-navigate-heading-vector22.gif (600×889 px, 1 MB)

On desktop, a screen reader user can navigate to all headings using the Headings mode:

* Historiography
* Development of the theory in the 19th century
* Kenneth McIntyre and development of the theory in the 20th century
* Purported evidence and arguments
* Interpretation of the Dieppe Maps
* etc...

On mobile the screenreader only sees the page title and "related articles" heading on the page, followed by "Heading not found".

What should have happened instead?:

  • VoiceOver should announce each section heading (e.g. "History, heading level 2") in sequence as the user swipes
  • The user should be able to jump between all article sections using Rotor heading navigation
  • This is standard, expected behaviour on any well-structured web page and works correctly on desktop with screen readers such as NVDA/JAWS

Developer notes

History
This patch fixed T395024 (arguably a worse accessibility issue where screen reader users were unaware of content in collapsed sections) by moving aria-expanded and related attributes from a non-existent element to the mw-heading div that wraps the heading element. It introduced the current issue for reasons detailed below.

Current markup

<div class="mw-heading mw-heading2 section-heading collapsible-heading open-block" 
     tabindex="0" role="button" aria-controls="content-collapsible-block-1" 
     aria-expanded="true">
    <span class="mf-icon mf-icon-expand mf-icon--small indicator mf-icon-rotate-flip"> </span>
    <h2 id="History">History</h2>
</div>
1. role="button" collapses all children into a single accessible element

When you put role="button" on a <div> element, VoiceOver treats the entire div as one interactive element. That means:

  • The heading inside is no longer navigable as a heading
  • The user cannot use Rotor navigation ("Headings") to jump to it
  • VoiceOver may read the heading text as the button label, but it loses semantic structure

This is a loss of structural semantics, which is bad for navigation and for users who skim by headings.

2. Buttons cannot contain headings

According to ARIA and HTML accessibility mapping rules:

Interactive controls should not contain block-level sectioning content (like <h1><h6>).

This is handled inconsistently across screen readers. On VoiceOver for iOS specifically:

  • The whole element is treated as a single button
  • The inner heading is not exposed as a heading role
  • Only the first text node is often read as the accessible name

This causes the heading to be ignored entirely in the Rotor heading navigation tree.

Technical best-practice reference

https://www.w3.org/WAI/ARIA/apg/patterns/accordion/


Acceptance criteria

  • Headings are correctly identified by the Rotor "Headings" option on iOS VoiceOver
  • Heading navigation works consistently across collapsed and expanded section states
  • Accessibility mailing list / users have been notified of the fix

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

The perfect workable markup IMHO would be (attribute indentation for readability):

<h2 id="History" class="mw-heading mw-heading2 section-heading">
  <button
    class="collapsible-heading open-block"
    aria-controls="content-collapsible-block-1"
    aria-expanded="true"
  >
    <span class="mf-icon mf-icon-expand mf-icon--small indicator mf-icon-rotate-flip"
          aria-hidden="true"></span>
    <span class="collapsible-heading__text">History</span>
  </button>
</h2>

With use of the native <button> element, there'd be no need for tabindex or role="button". That's in many parts equal to the WAI ARIA Accordion pattern already mentioned.
Side-note: In case we get rid of the <Space> within .mf-icon-expand we could even get rid of the aria-hidden attribute on the indicator element.

Again, removing the existing button role comes with possible problematic effects:

It becomes a generic focusable element. Depending on the screen reader, users might just hear:
“History” or “History, group” or similar.

Users won’t be told “this is a button” or “this is actionable,” so it’s less obvious that they can activate it to expand/collapse content.

Button shortcuts / rotor navigation might not find it

Some assistive tech lets users:

  • Jump between buttons (e.g., VoiceOver rotor set to “Form Controls” or “Buttons”)
  • Get a list of all buttons

I agree that updating the markup so that it reflects the WAI ARIA Accordion pattern, is an ideal solution and can be a long term fix. I've also heard that we might consider removing the collapsible sections all together, which would be another ideal, long term fix.

However, in the short term, we need a fix that does not move around the markup so that we do not negatively interfere with the current work RG is doing. This will come with trade offs but the current bug is extremely severe for AT users and should be fixed asap. Short term fixes I can think of are removing or moving the button role.

LMora-WMF changed the task status from Open to In Progress.Dec 10 2025, 5:01 PM
LMora-WMF claimed this task.

Change #1220393 had a related patch set uploaded (by LorenMora; author: LorenMora):

[mediawiki/extensions/MobileFrontend@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1220393

Change #1220394 had a related patch set uploaded (by LorenMora; author: LorenMora):

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1220394

Volker_E changed the point value for this task from 3 to 5.Jan 12 2026, 6:20 PM

Change #1220394 abandoned by LorenMora:

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

Reason:

Going with a better approach

https://gerrit.wikimedia.org/r/1220394

Change #1220394 restored by LorenMora:

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1220394

Change #1198573 abandoned by LorenMora:

[mediawiki/extensions/MobileFrontend@master] Accessibility issue with browsing by headings

Reason:

Going with a better approach

https://gerrit.wikimedia.org/r/1198573

Pre-Merge Dev QA - Combined the related patches in one PatchDemo here and tested on iOS 26.1 iPhone Air. Rotor announces number of headings. And can traverse the headings with swipe up/down. GIF

Also tested Parsoid patches and those are announcing headings on rotor as well GIF

Change #1226378 had a related patch set uploaded (by LorenMora; author: LorenMora):

[mediawiki/extensions/MobileFrontend@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1226378

Test wiki created on Patch demo by EGardner (WMF) using patch(es) linked to this task:
https://dcbb1ca7a9.catalyst.wmcloud.org/w/

Volker_E renamed this task from Browsing by headings via rotor on VoiceOver impossible – headings not found to Re-enable browsing by headings via rotor on VoiceOver (headings not found).Jan 15 2026, 6:57 PM
Volker_E edited projects, added: Regression; removed: Patch-For-Review.

Change #1220394 merged by jenkins-bot:

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1220394

Change #1220393 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1220393

Change #1226379 had a related patch set uploaded (by LorenMora; author: LorenMora):

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

https://gerrit.wikimedia.org/r/1226379

Change #1230451 had a related patch set uploaded (by DLynch; author: DLynch):

[mediawiki/extensions/MobileFrontend@master] Revert "Toggler: Update heading toggler to match WAI ARIA pattern"

https://gerrit.wikimedia.org/r/1230451

Change #1230454 had a related patch set uploaded (by DLynch; author: DLynch):

[mediawiki/extensions/MobileFrontend@wmf/1.46.0-wmf.12] Revert "Toggler: Update heading toggler to match WAI ARIA pattern"

https://gerrit.wikimedia.org/r/1230454

We've had to revert this because heading markup changes need careful scrutiny and testing. This has completely broken DiscussionTools on mobile (T415303), and also caused some very strange behavior for any headings that contain markup (e.g. links), see:

image.png (960×1,719 px, 454 KB)

Change #1230454 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@wmf/1.46.0-wmf.12] Revert "Toggler: Update heading toggler to match WAI ARIA pattern"

https://gerrit.wikimedia.org/r/1230454

Mentioned in SAL (#wikimedia-operations) [2026-01-22T22:08:24Z] <kemayo@deploy2002> Started scap sync-world: Backport for [[gerrit:1230454|Revert "Toggler: Update heading toggler to match WAI ARIA pattern" (T415303 T407908)]]

Mentioned in SAL (#wikimedia-operations) [2026-01-22T22:10:23Z] <kemayo@deploy2002> kemayo: Backport for [[gerrit:1230454|Revert "Toggler: Update heading toggler to match WAI ARIA pattern" (T415303 T407908)]] synced to the testservers (see https://wikitech.wikimedia.org/wiki/Mwdebug). Changes can now be verified there.

Mentioned in SAL (#wikimedia-operations) [2026-01-22T22:15:07Z] <kemayo@deploy2002> Finished scap sync-world: Backport for [[gerrit:1230454|Revert "Toggler: Update heading toggler to match WAI ARIA pattern" (T415303 T407908)]] (duration: 06m 43s)

Change #1230451 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@master] Revert "Toggler: Update heading toggler to match WAI ARIA pattern"

https://gerrit.wikimedia.org/r/1230451

I didn't revert https://gerrit.wikimedia.org/r/c/mediawiki/skins/MinervaNeue/+/1220394 because it seemed to maintain backwards-compatibility with the prior markup. This does mean that there's now a Remove after 14 days comment in that less file that doesn't apply, which should probably get cleaned up.

Moving this task back to "Needs Refinement" until we can come up with an approach to changing markup in this part of MobileFrontend that won't disrupt mobile talk pages using DiscussionTools. Once we have an agreed-upon approach, this task's acceptance criteria and QA instructions should be updated accordingly.

Change #1235889 had a related patch set uploaded (by Eric Gardner; author: Eric Gardner):

[mediawiki/extensions/MobileFrontend@master] MakeSectionsTransform: Add ARIA attributes to existing markup

https://gerrit.wikimedia.org/r/1235889

Test wiki created on Patch demo by EGardner (WMF) using patch(es) linked to this task:
https://bf05af8eb8.catalyst.wmcloud.org/w/

egardner changed the task status from In Progress to Stalled.Feb 11 2026, 4:48 AM

Change #1226379 abandoned by LorenMora:

[mediawiki/skins/MinervaNeue@master] Toggler: Update heading toggler to match WAI ARIA pattern

Reason:

going with different approach

https://gerrit.wikimedia.org/r/1226379

Change #1226378 abandoned by LorenMora:

[mediawiki/extensions/MobileFrontend@master] Toggler: Update heading toggler to match WAI ARIA pattern

Reason:

going with different approach

https://gerrit.wikimedia.org/r/1226378

Test wiki on Patch demo by LMora-WMF using patch(es) linked to this task was deleted:

https://a8921d2059.catalyst.wmcloud.org/w/

RG is running an experiment to allow us to move away from the current mobile sections entirely. If this is the case, then we won't need collapsible sections, and that may avoid this issue.

We will provide code review and support in the meantime.

RE may work on this, but please do not merge until after April 9th! RG can help merge it after that date to avoid influencing the data for the experiment.
cc: @Jdrewniak

Jdrewniak renamed this task from Re-enable browsing by headings via rotor on VoiceOver (headings not found) to Navigation via headings broken on iOS VoiceOver.Apr 24 2026, 2:57 PM
Jdrewniak updated the task description. (Show Details)
bwang renamed this task from Navigation via headings broken on iOS VoiceOver to Headings inaccessible for assistive tech on mobile.Apr 24 2026, 5:51 PM
AnneT removed the point value 5 for this task.

If I understand correctly, this is now resolved by the subtasks for Parsoid-rendered content. I'm not sure what the next step is:

  • Call this task resolved now?
  • Wait for Parsoid to become the default parser?
  • Implement the fix for the old parser as well?

I'll leave it to you to decide.

I don't think we need to fix the old parser. We can wait for Parsoid to become the default parser (it is already the case on English Wikipedia).

@LMora-WMF what do you think?

Sorry for the late reply here. Thanks y'all, I will close out the task now as it has been resolved. Note, this has been fixed in Parsoid and will not be fixed in the legacy parser as we have migrated away from largely supporting it.

LMora-WMF claimed this task.

This has been fixed in Parsoid, with no plans to fix in the old parser as we have moved away from supporting it.