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:

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

andrewmacpherson created an issue. See original summary.

andrewmacpherson’s picture

I wonder if we can crowd-share this work, say as an activity in the Global Sprint Weekend.

mgifford’s picture

That 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.

andrewmacpherson’s picture

Issue summary: View changes

There'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:

  • PASS: t("Manage fields <span class="visually-hidden">for @bundle</span>", ["@bundle" => $entity->bundle()])
  • FAIL: t("Manage <span class="visually-hidden">@bundle</span> fields", ["@bundle" => $entity->bundle()])

How can we address this?

  • Add guidance in d.o handbook pages about Label in name, with pass/fail examples, including how it might be broken by translation.
  • Encourage localization contributors to review their translations for WCAG Label in Name. How to promote this?

Added a pass/fail example to the issue summary.

andrewmacpherson’s picture

Issue tags: +multilingual

I'll let the interface translation maintainer (Gabor) know about this.

andrewmacpherson’s picture

#3 - @mgifford - Epic spreadsheet FTW! I don't relish the idea, but I can't suggest anything better :-)

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

andrewmacpherson’s picture

Issue summary: View changes

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mgifford’s picture

Issue tags: +wcag253

I think this is more of a search for issues, rather than "there are issues"

prabuela’s picture

Assigned: Unassigned » prabuela
prabuela’s picture

The best is as discussed earlier, split the module or theme sub issue, will help to fix the issue.

prabuela’s picture

Assigned: prabuela » Unassigned

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

mgifford’s picture

With 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:

User says: "Click Save"

Software searches for a control whose accessible name contains "Save"

Match found → control activated

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 cannot may not be able to match because the accessible name begins with different text.

Example:

<!-- User sees: "Image" -->
<!-- Accessible name: "Show Image media" -->
<span class="visually-hidden">Show </span>Image<span class="visually-hidden"> media</span>

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:

  • present in full;
  • in the same order;
  • contiguous, without hidden words inserted between them.

Locations found:

  • Media Library tab buttons (Show @title media)
  • Navigation admin menu (Extend before menu item title)
  • Locale translation form (Source string / Translated string before visible content)
  • Content translation table headers (bundle name before field/column label)
  • Locale translation update info (Show description before visible text)
  • Status report counter (severity text before "Details")

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-label that 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:

<!-- User sees: "Menu" -->
<!-- Accessible name: "Main Menu" -->
<button aria-label="Main Menu">Menu</button>

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:

<!-- User sees: "(Experimental)" -->
<!-- Accessible name: "View information on the Experimental status of the module Node" -->
<a href="..." aria-label="View information on the Experimental status of the module Node">(Experimental)</a>

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:

  • Olivero mobile nav button (aria-label="Main Menu" vs visible "Menu")
  • Field re-use button (aria-label="Reuse @field" vs visible "Re-use")
  • Module lifecycle links (aria-label="View information on..." vs visible "(Experimental)")
  • Non-stable module confirm form (aria-label=" about the status of..." vs visible text)
  • System modules details summary (aria-label="Details for @module" vs visible description)

Patterns to watch out for:

  • Save versus Confirm changes: WCAG failure.
  • Re-use versus Reuse: avoid the discrepancy, but do not declare it a confirmed failure without applying the relevant language and punctuation rules.

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-label or visually-hidden text to distinguish them.

Example:

<!-- Row 1 -->
<a href="/edit/state/1">Edit</a>  <a href="/delete/state/1">Delete</a>
<!-- Row 2 -->
<a href="/edit/state/2">Edit</a>  <a href="/delete/state/2">Delete</a>
<!-- Row 3 -->
<a href="/edit/state/3">Edit</a>  <a href="/delete/state/3">Delete</a>

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 EntityListBuilder base class):

<a href="/edit/format/basic_html" aria-label="Configure Basic HTML">Configure</a>
<a href="/edit/format/restricted_html" aria-label="Configure Restricted HTML">Configure</a>

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:

  • Workflow edit form: "Edit" and "Delete" repeated per state and transition
  • Comment admin: "Edit" repeated per comment
  • Shortcut set customize: "Edit" and "Delete" repeated per shortcut
  • Image style edit form: "Edit" and "Delete" repeated per effect
  • Config translation: "Edit", "Delete", "Add" repeated per language
  • Block list builder: visible text changed to "Configure"/"Remove" but aria-label still says "Edit"
  • Field config list builder: "Edit" and "Delete" with no aria-label at all
  • Node revisions: "Delete" repeated per revision
  • Content translation: "Delete" repeated per language
  • Workspace list: "Manage" repeated per workspace

Unique accessible names can help:

  • screen-reader users navigating a list of controls;
  • Dragon and Windows Voice Access users who know or can infer the additional words;
  • other users when the contextual name is exposed through an overlay.

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.

Slice Component Category Fix approach
2 Media Library tabs Cat 1 Move visible type label to start of accessible name
3 Lifecycle links (4 files) Cat 2 Change aria-label to start with visible text
4 Olivero mobile nav Cat 2 Align aria-label with visible "Menu" text
5 FieldStorageReuseForm Cat 2 Fix "Re-use" vs "Reuse" spelling in aria-label
6 Status report counter Cat 1 Move severity text after "Details"
7 Navigation "Extend" button Cat 1 Move visually-hidden text after menu item title
8 WorkflowEditForm operations Cat 3 Add aria-label with state/transition name
9 Comment admin Edit links Cat 3 Add aria-label with comment identifier
10 Config translation operations Cat 3 Add aria-label with language name

Additional child issues can be created for:

  • Locale translation form (Cat 1, slices for source/plural textareas)
  • Content translation theme hooks (Cat 1, bundle/field labels)
  • Block list builder (Cat 3, parent aria-label mismatch)
  • Field config list builder (Cat 3, no aria-label at all)
  • Node revisions (Cat 3, Delete per revision)
  • Remaining list builders with inherited mismatched aria-labels

References

Documentation from vendors: