fix(label-content-name-mismatch): match visible text with aria-label and exclude invisible text - #5096
Conversation
| } | ||
| if ( | ||
| ignoreIconLigature && | ||
| isIconLigature(child, pixelThreshold, occurrenceThreshold) |
There was a problem hiding this comment.
we should judge isIconLigature here because it needs child node as its first param
There was a problem hiding this comment.
Pull request overview
This PR updates label-content-name-mismatch so it compares accessible names against visually rendered text instead of subtree accessible text, addressing false positives like img[alt] and SVG <title> content being treated as visible label text.
Changes:
- Swapped the check’s visible-text computation from
subtreeText()to avisibleVirtual()-based traversal. - Added integration and unit coverage for
img[alt], SVG title,<br>, and icon-ligature scenarios. - Extended
visibleVirtual()with options for ignoring icon ligatures during visible-text extraction.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.json |
Updates expected pass/fail fixtures for new label/content cases. |
test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.html |
Adds HTML fixtures covering img[alt] and SVG title visible-label scenarios. |
test/commons/text/visible-virtual.js |
Adds tests for the new visibleVirtual ligature-filtering options. |
test/checks/label/label-content-name-mismatch.js |
Adds unit coverage for the img[alt] false-positive regression. |
lib/commons/text/visible-virtual.js |
Adds optional icon-ligature filtering to visible-text extraction. |
lib/checks/label/label-content-name-mismatch-evaluate.js |
Reworks the check to derive visible text from rendered content instead of subtree accessible text. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
964edbb to
3370354
Compare
| if (nodeName === 'br') { | ||
| return ' '; | ||
| } |
There was a problem hiding this comment.
we should replace <br> with ' ' to pass 'returns true when text contains <br/>' test
WilcoFiers
left a comment
There was a problem hiding this comment.
Looks good. I think this is missing one test.
| var actual = check.evaluate(vNode.actualNode, options, vNode); | ||
| assert.isTrue(actual); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Would you mind adding a ligature font unit test here as well?
There was a problem hiding this comment.
added more test cases!
WilcoFiers
left a comment
There was a problem hiding this comment.
Should have made that a change request.
|
@copilot resolve the merge conflicts in this pull request |
…ntents instead of `subtreeText` Use `visibleVirtual` instead of `subtreeText` in order to match visible text with aria-label and exclude invisible text. closes issue dequelabs#5063
… font Add more test cases using ligature font and normal invalid case. closes issue dequelabs#5063
aed738e to
21c2d6e
Compare
|
Copilot didn't seem to be working, so I resolved the conflict manually |
|
@mehm8128 would you mind running Prettier? the fmt_check is failing because of it. |
Run prettier. closes issue dequelabs#5063
|
@WilcoFiers done |
WilcoFiers
left a comment
There was a problem hiding this comment.
Looks good to me. Thank you @mehm8128!
For the books, I reviewed this PR for security.
## [4.12.0](v4.11.4...v4.12.0) (2026-06-01) ### Features - add gather-internals.js external script ([#5099](#5099)) ([c61d58b](c61d58b)), closes [#5080](#5080) - **aria-allowed/prohibited-attr, aria-required-parent/children:** partially support element internals role ([#5080](#5080)) ([417b48a](417b48a)), closes [#5039](#5039) [#4259](#4259) - **axe.externalAPIs:** add public api for setting elementInternal data ([#5105](#5105)) ([63bab8f](63bab8f)) - **core:** expose normalizeRunOptions ([#4998](#4998)) ([b8e6a59](b8e6a59)) - expose axe.resetLocale() to restore the default locale ([#5108](#5108)) ([c2b5292](c2b5292)), closes [#5107](#5107) - **getRules:** include rule enabled state in returned objects ([#5118](#5118)) ([75bf772](75bf772)), closes [#5116](#5116) - **list,listitem:** support element internals role ([#5119](#5119)) ([7d9d696](7d9d696)) - **new-rule:** check that aria-tab have an accessible name ([#5001](#5001)) ([0d4e4e7](0d4e4e7)), closes [#4842](#4842) - **rules:** deprecate landmark-complementary-is-top-level rules ([#4992](#4992)) ([9e09139](9e09139)), closes [#4950](#4950) - **utils:** add `getElementInternals` function ([#5077](#5077)) ([1c15f82](1c15f82)) ### Bug Fixes - **aria-allowed-attr:** restrict br and wbr elements to aria-hidden only ([#4974](#4974)) ([c6245e7](c6245e7)) - **aria-conditional-attr:** add support for radio ([#5100](#5100)) ([8223c98](8223c98)) - **aria-valid-attr-value:** handle multiple aria-errormessage IDs ([#4973](#4973)) ([0489e30](0489e30)) - **aria:** prevent getOwnedVirtual from returning duplicate nodes ([#4987](#4987)) ([48ca955](48ca955)), closes [#4840](#4840) - **commons/text:** exclude natively hidden elements from aria-labelledby accessible name ([#5076](#5076)) ([ea7202c](ea7202c)), closes [#4704](#4704) - **DqElement:** avoid calling constructors with cloneNode ([#5013](#5013)) ([0281fa1](0281fa1)) - **existing-rule:** aria-busy now shows an error message for a use with unallowed children ([#5017](#5017)) ([2067b87](2067b87)) - **helpUrl:** ensure axe.configure always updates the help URLs ([#5114](#5114)) ([c4f60ff](c4f60ff)) - **label-content-name-mismatch:** match visible text with aria-label and exclude invisible text ([#5096](#5096)) ([3a012a1](3a012a1)) - **locale:** ensure all subtags are correctly set ([#5112](#5112)) ([13005ed](13005ed)) - **scrollable-region-focusable:** clarify the issue is in safari ([#4995](#4995)) ([4ec5211](4ec5211)), closes [WebKit#190870](https://github.com/dequelabs/WebKit/issues/190870) [WebKit#277290](https://github.com/dequelabs/WebKit/issues/277290) - **scrollable-region-focusable:** do not fail scroll areas when all content is visible without scrolling ([#4993](#4993)) ([838707a](838707a)) - **target-size:** determine offset using clientRects if target is display:inline ([#5012](#5012)) ([a4b8091](a4b8091)) - **target-size:** ignore position: fixed elements that are offscreen when page is scrolled ([#5066](#5066)) ([1229a6e](1229a6e)), closes [#5065](#5065) - **target-size:** ignore widgets that are inline with other inline elements ([#5000](#5000)) ([a8dd81b](a8dd81b)) - **utils/getAncestry:** escape node name ([#5079](#5079)) ([d1fabaa](d1fabaa)), closes [#5078](#5078) - **utils:** Add null check to parseCrossOriginStylesheet, closes [#5074](#5074) ([#5075](#5075)) ([f12ef32](f12ef32)) - **utils:** update isShadowRoot to use spec-compliant custom element regex ([#5059](#5059)) ([edc6ce2](edc6ce2)), closes [#5030](#5030) This PR was opened by a robot 🤖 🎉
Using
visibleVirtualinstead ofsubtreeText, I resolved issue.Because if I simple replace it and use
isIconLigatureexisting two tests failed ('returns true when visible text excluding ligature icon is part of accessible name'and'returns true when text contains <br/>'), I changedvisible-virtual.js, but I'm not sure this is proper changes (this is the first PR for me to axe-core).Closes: #5063