Visitar URL original
fix(commons/text/visibleVirtual): separate the words around a visually hidden element by maricastroc · Pull Request #5476 · dequelabs/axe-core · GitHub
Skip to content

fix(commons/text/visibleVirtual): separate the words around a visually hidden element - #5476

Open
maricastroc wants to merge 1 commit into
dequelabs:developfrom
maricastroc:fix/5475-visible-text-hidden-separator
Open

maricastroc wants to merge 1 commit into
dequelabs:developfrom
maricastroc:fix/5475-visible-text-hidden-separator

Conversation

@maricastroc

@maricastroc maricastroc commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Closes issue #5475

Problem

visibleVirtual leaves out the text of an element that is not visible and joins the text around it with no separator at all. When the hidden element is a separator, the words on either side of it become one:

<style>
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
    white-space: nowrap;
    clip-path: inset(50%);
  }
  .status {
    display: block;
  }
  .group {
    display: flex;
  }
</style>

<label id="file-upload-label">Upload a file</label>
<button
  id="file-upload"
  type="button"
  aria-labelledby="file-upload-label file-upload-comma file-upload"
><span class="status">No file chosen</span><span class="visually-hidden" id="file-upload-comma">, </span><span class="group">Choose file</span></button>

The styles and the missing whitespace between the <span>s are part of the repro: they match what the NHS.UK page renders (its markup is generated by JavaScript, with no whitespace between the spans). Without the .visually-hidden styles the comma is visible text, and the button passes.

accessible name Upload a file , No file chosen, Choose file
visible text (develop) No file chosenChoose file
visible text (this PR) No file chosen Choose file

The accessible name keeps the hidden comma, and with it the word boundary, because visually hidden content is exposed to assistive technology. The visible text drops it and keeps no boundary, so label-content-name-mismatch tokenizes chosenchoose, finds no such word in the name, and reports a violation for a button that is labelled by its own content.

This is the NHS.UK frontend file upload component from the issue. Neither the self reference in aria-labelledby nor aria-live is involved: with the hidden separator removed, the same button passes on develop.

Fix

An element that is rendered but has no visible text now contributes a single space, which is what the ACT definition of visible inner text gives it:

Element not visible, rendered, with bounding box width greater than 0: its visible inner text is " " (U+0020 SPACE).

An element that is not rendered, or whose box has no width, still contributes nothing, so the words around a display: none or hidden element stay joined, as they are on screen. The space is only ever added where the text of an element was left out, so visible elements are untouched.

Testing

  • 5 unit tests in test/commons/text/visible-virtual.js cover the visually hidden and visibility: hidden separators, and the display: none, zero-width and visible cases that must stay joined.
  • 3 check tests in test/checks/label/label-content-name-mismatch.js cover the reported markup, the same pattern with aria-label, and a genuine mismatch that the new separator must not rescue.
  • Integration cases #pass20 and #fail10. #pass20 fails on develop.
  • Verified against the live NHS.UK page: the violation is gone, and the visible text is No file chosen Choose file or drop file.
  • Full suites pass: unit (core, commons, checks, rule-matches, virtual-rules, api, gather-internals, integration), locales, examples, jsdom, tsc, ESLint and Prettier. ACT rule 2ee8b8 passes all 16 passed and all 16 failed examples; the only ACT failure, 78fd32 Failed Example 2, fails the same way on develop.

Across an 18 case matrix of hidden separators, only the reported pattern changed outcome, plus a visibility: hidden element between two words, which the ACT definition separates for the same reason. Nothing went from pass to violation.

Note on #5460

#5460 fixes the related case where the text of two adjacent block elements is joined. It does not fix this issue: here the separated elements are <span>s, which are phrasing content, so #5460 keeps them joined — verified by building that branch against the NHS.UK page. The two patches touch the same statement and will conflict textually, but they compose: #5460 handles a child whose text is non-empty, this one a child whose text is empty.

…y hidden element

visibleVirtual leaves out the text of an element that is not visible and
joins the text around it with no separator, so a visually hidden separator
turns "No file chosen" and "Choose file" into the single word
"chosenChoose". The accessible name keeps the hidden text, and with it that
word boundary, so label-content-name-mismatch reports a violation for a
button labelled by its own content.

Return a space for an element that is rendered but has no visible text, as
in the ACT definition of visible inner text, which gives such an element a
single space. An element that is not rendered, or that has no width, still
contributes nothing, so the words around a display: none element stay
joined as they are on screen.

Closes: dequelabs#5475
@maricastroc
maricastroc requested a review from a team as a code owner October 16, 2026 12:22
@straker

straker commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for the pr, however I'm not able to reproduce the problem. Testing the provided code passes label-content-name-mismatch on develop.

Accessible name = "upload a file , no file chosen, choose file"
Visible text = "no file chosen, choose file"

@maricastroc

maricastroc commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

@straker Thanks for taking a look! In your results visibleText still has the comma ("no file chosen, choose file"), so the .visually-hidden span was visible in your test. That's my fault: the snippet in the description had no CSS, and without it the comma is just visible text, so nothing mismatches. The HTML in the issue has the same gap. It was also reformatted with line breaks between the spans, which adds whitespace the real DOM doesn't have (the NHS.UK component builds it with JavaScript).

Here is a self-contained repro with the styles the NHS.UK page applies:

<style>
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
    white-space: nowrap;
    clip-path: inset(50%);
  }
  .status {
    display: block;
  }
  .group {
    display: flex;
  }
</style>

<label id="file-upload-label">Upload a file</label>
<button
  id="file-upload"
  type="button"
  aria-labelledby="file-upload-label file-upload-comma file-upload"
><span class="status">No file chosen</span><span class="visually-hidden" id="file-upload-comma">, </span><span class="group">Choose file</span></button>
accessible name visible text label-content-name-mismatch
develop Upload a file , No file chosen, Choose file No file chosenChoose file violation
this PR Upload a file , No file chosen, Choose file No file chosen Choose file pass

The live NHS.UK file upload example gives the same result: a violation on develop, a pass with this PR. I've updated the PR description with the styles.

@straker

straker commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Thanks for the additional information. We have a lot of open prs at the moment so we'll try to get to this as soon as we can.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants