Problem/Motivation
Drupal core makes widespread use of invisible labels, aimed at screen reader users. Unfortunately these techniques can cause problems for speech control users, if used incorrectly. WCAG 2.1 introduces new success criterion 2.5.3: Label in Name to address these problems.
This issue is about auditing Drupal core for "Label in Name", and correcting any problems found.
Background reading:
- Understanding Success Criterion 2.5.3: Label in Name
- Improving web navigation for speech recognition users
- Exploring WCAG 2.1 — 2.5.3 Label in Name
- Make screenreader say button alt-attribute instead of innerText - A worked example of pass/failure markup techniques in a StackOverflow answer.
- Label and name from content mismatch - some examples of pass/fail involving
aria-label
Proposed resolution
- Survey ALL instances of .visually-hidden, aria-label, aria-labelledby.
- This is potentially a BIG survey, so we may split this plan up by module, theme, etc.
- For any problems found, file a child issue to correct them.
Example:
- PASS:
t("Manage fields <span class="visually-hidden">for @bundle</span>", ["@bundle" => $entity->bundle()]). A speech control user can activate this by saying "Click manage fields" and their assistive tech can narrow the choices down to the instances which match. So users can choose from a handful of relevant matches. - FAIL:
t("Manage <span class="visually-hidden">@bundle</span> fields", ["@bundle" => $entity->bundle()]). A speech control user cannot activate this by saying the visible link text. "Click manage fields" won't work, because that exact phrase doesn't appear in the names given to assistive tech. The user will have to say "Show numbers" to highlight all controls on the page, instead of just a few relevant matches.
TODO: Make a set of instructions for testing this. Flesh out the pass/fail examples to include scenarios with aria-label and aria-labelledby. A good starting point would be this answer the situations described in this Stack Overflow answer: Make screenreader say button alt-attribute instead of innerText
Remaining tasks
User interface changes
Update strings where any violations occur.
API changes
None.
Data model changes
None.
Comments
Comment #2
andrewmacpherson commentedI wonder if we can crowd-share this work, say as an activity in the Global Sprint Weekend.
Comment #3
mgiffordThat looks like it is 25-27.01.2019 - that right?
Would this be a big Google Spreadsheet we'd be building on? Not sure how to do this type of system wide review.
Comment #4
andrewmacpherson commentedThere's a multilingual UI translation aspect of this. A review of all core code will only confirm that the default English UI strings satisfy WCAG Label in Name.
However UI translators could cause a WCAG failure here. Most of our dynamic translatable strings work by putting variables inside. I understand this is so translators have flexibility over things like word order, idiom, choosing which parts of a sentence should be linked, and the freedom to choose appropriate visually-hidden text. But when translating a string which contains a visually-hidden span, a translator could cause a "Label in name" problem if they put visually-hidden text between two visible words.
Example:
t("Manage fields <span class="visually-hidden">for @bundle</span>", ["@bundle" => $entity->bundle()])t("Manage <span class="visually-hidden">@bundle</span> fields", ["@bundle" => $entity->bundle()])How can we address this?
Added a pass/fail example to the issue summary.
Comment #5
andrewmacpherson commentedI'll let the interface translation maintainer (Gabor) know about this.
Comment #6
andrewmacpherson commented#3 - @mgifford - Epic spreadsheet FTW! I don't relish the idea, but I can't suggest anything better :-)
Comment #8
andrewmacpherson commentedComment #17
mgiffordI think this is more of a search for issues, rather than "there are issues"
Comment #18
prabuela commentedComment #19
prabuela commentedThe best is as discussed earlier, split the module or theme sub issue, will help to fix the issue.
Comment #20
prabuela commentedComment #22
mgiffordWith a lot of help from AI, I've made some progress on this.
Goal
Assess and fix Drupal core components for WCAG 2.5.3 Label in Name compliance. This criterion ensures that the accessible name of a control contains — and best practice is to start with — the visible text label that users see on screen.
This matters for speech recognition users who say "Click [visible text]" to activate controls. If the accessible name doesn't match the visible text, the speech software cannot find the control, forcing the user to resort to slower workarounds like numbered overlays or spatial grids.
This issue tracks the assessment. Child issues will contain the fixes, one per component.
How Speech Recognition Identifies Controls
Speech recognition software (Dragon, Apple Voice Control, Windows Speech Recognition) matches spoken commands against the accessible name of each control on the page. The primary interaction is:
When multiple controls share the same accessible name, the software highlights all matches with numbers. The user must then speak the correct number to select the one they want. This adds a step to every interaction.
When no match is found, the user must use spatial methods — MouseGrid (Dragon), Show Numbers (Apple Voice Control) — displays numeric labels next to every visible interactive control, like "Show names" but shorter.
"Show grid" is the Apple command which overlay the page with numbered grids and require drilling down to the target. This is significantly slower and more tedious.
Three Categories of Violations Found
Category 1: [Advisory] Visually-hidden text appears before the visible label
The accessible name starts with text the user cannot see. When the user says "Click [visible text]", the software
cannotmay not be able to match because the accessible name begins with different text.Example:
The user says "Click Image" but the accessible name starts with "Show". The software may not find a match or may respond better if the user says the full "Show Image media".
Why this is a problem: Where possible, have the visible text at the beginning of the accessible name. Speech users will likely use the text they see rather than the words they cannot see.
To meet WCAG 2.5.3 the visible words must generally be:
Locations found:
Show @title media)Extendbefore menu item title)Source string/Translated stringbefore visible content)Show descriptionbefore visible text)Perhaps first ask whether Show and media belong in the name at all. A tab named Image already exposes its tab role and selected state. The tab semantics communicate that activation displays its associated panel. WAI-ARIA tabs pattern.
Category 2: aria-label diverges from the visible text
The element has an
aria-labelthat contains different text than what is visible. The accessible name (from aria-label) does not contain the visible label text at all, or starts with different text.Example:
The user says "Click Menu" but the accessible name is "Main Menu". Some speech engines
(notably iOS Voice Control)may require the accessible name to start with the visible text. "Main Menu" does start with "Menu" — but consider the reverse:The user says "Click Experimental" but the accessible name starts with "View information on". The visible text "(Experimental)" is not at the start of the accessible name.
Why this is a problem: The aria-label completely replaces the visible text as the accessible name. If the aria-label doesn't start with or contain the visible text, speech users cannot activate the control by speaking what they see.
Locations found:
aria-label="Main Menu"vs visible "Menu")aria-label="Reuse @field"vs visible "Re-use")aria-label="View information on..."vs visible "(Experimental)")aria-label=" about the status of..."vs visible text)aria-label="Details for @module"vs visible description)Patterns to watch out for:
The best default remains avoiding aria-label when visible content already produces a correct name. This keeps the visible label, accessible name, and translation source synchronized.
Category 3: Repeated identical text without disambiguation
The same visible text (e.g., "Edit", "Delete") appears in every row of a table or list with no
aria-labelor visually-hidden text to distinguish them.Example:
The user says "Click Edit". The software finds three matches and highlights them with numbers: 1, 2, 3. The user must then say "Choose 2" to select the second one. Every. Single. Time.
Compare with the correct pattern (already used in core's
EntityListBuilderbase class):The user can say "Click Configure Basic HTML" to directly activate the correct link without numbered disambiguation.
Why this is a problem: Every additional disambiguation step adds cognitive load and slows down the interaction. In a table with 20 rows, a speech user must speak two commands (number + confirm) for every action instead of one.
Locations found:
Unique accessible names can help:
But the feedback is correct: hidden specificity does not guarantee less work for a speech user who does not know that hidden name. Apple users may still need “Show names” or “Show numbers.”
Link text does not automatically fail WCAG 2.4.4. Link purpose can come from programmatically determined context such as the enclosing table cell, row headers, list item, or paragraph.
Proposed Child Issues
Each slice targets one component, with a minimal fix and one focused regression test.
Additional child issues can be created for:
References
Documentation from vendors: