Visible Label In The Accessible Name

ID

html.visible_label_in_accessible_name

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

Html

Tags

accessibility, code_smell

Description

Flags a control whose accessible name does not contain the label the user can see on it.

Rationale

A speech-input user operates a control by saying what is written on it. The words they say are matched against the control’s accessible name, not against what is on the screen, so a name that does not contain the visible label leaves the control unreachable by voice: the button says "Discard" and answers only to "Save changes to the document". The same mismatch strands a screen-reader user who can partially see the screen, since they hear one label and read another. This is WCAG 2.5.3, Label in Name.

<button type="submit" aria-label="Save changes to the document">Discard</button> <!-- FLAW -->
<a href="/docs" aria-label="Documentation">Docs</a>              <!-- FLAW — "Docs" is not in the name -->
<div role="button" aria-label="Submit form">Send</div>           <!-- FLAW -->

<button type="submit" aria-label="Save changes">Save</button>    <!-- OK — the visible text is inside -->
<button type="button" aria-label="Delete item">Delete...</button> <!-- OK — punctuation is not compared -->
<button type="button" aria-label="Close">&times;</button>        <!-- OK — an icon has no visible label -->
<button type="button" aria-label="Close dialog">X</button>       <!-- OK — one character is a glyph -->
<button type="button" aria-label="Search"><svg></svg></button>   <!-- OK — same, drawn instead -->

An aria-label that extends the visible text is the intended pattern and never reported — aria-label="Save changes" on a button reading "Save" is correct, and so is "Read more about pricing" on a "Read more" link. The comparison is therefore containment, not equality, and it runs on both strings normalised the same way: lower-cased, with everything that is not a letter or a digit reduced to a single space. That keeps trailing punctuation, an ellipsis or a colon out of the comparison.

The rule stays silent wherever either side cannot be read as written, which is most of the markup it walks:

  • An element with no visible text of its own. An icon-only button is the case aria-label exists for, and it is never reported — whether the icon is drawn by a nested element, written as a character reference, or is a symbol such as × that normalises to nothing. A single remaining character counts as a glyph too: an X for close is not a label anyone says out loud.

  • An element whose text is not a single text node: nested markup carries part of the label, and which part is visible is not decidable from the markup.

  • A template expression on either side, in the text or in the attribute, and a directive spelling such as th:aria-label.

  • An aria-labelledby whose targets cannot all be resolved to elements with readable text of their own. The reference is followed — that is the name the browser computes, and it takes precedence over aria-label — but a target in a host page, a target carrying nested markup, or one id of several going unresolved leaves the name unknown, and an unknown name is not a finding.

Only controls are judged: the interactive elements plus any element an interactive ARIA role turns into a control. A div carrying an aria-label for a region is not one, and neither is an input, whose visible label comes from a <label> or a value rather than from text of its own.

Remediation

Make the accessible name start with the visible text, then add whatever context the name needs after it. Where the extra context is only there to disambiguate two identical labels, prefer making the visible text itself specific — then the control needs no aria-label at all.

<button type="submit" aria-label="Discard changes to the document">Discard</button>
<a href="/docs" aria-label="Docs and API reference">Docs</a>
<div role="button" aria-label="Send form">Send</div>

© 2026 Xygeni. All rights reserved