Skip to content

fix(landmark-unique): exclude section/form with non-landmark roles from landmark match#5085

Open
nami8824 wants to merge 2 commits into
dequelabs:developfrom
nami8824:fix/landmark-unique-matches-role-check
Open

fix(landmark-unique): exclude section/form with non-landmark roles from landmark match#5085
nami8824 wants to merge 2 commits into
dequelabs:developfrom
nami8824:fix/landmark-unique-matches-role-check

Conversation

@nami8824

@nami8824 nami8824 commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Closes #4722, #5064

Updated landmark-unique-matches to change the matching logic for section / form elements as follows:

before

No aria-label Has aria-label
No explicit role
Explicit landmark role
Non-landmark role ✅ ← bug

after

No aria-label Has aria-label
No explicit role
Explicit landmark role
Non-landmark role

Unrelated to this issue, but I think section / form with an explicit landmark role and no accessible name might also need to match, opened a separate issue for it: #5064

…om landmark match

Previously, section and form elements with an explicit non-landmark role
(e.g. role=tabpanel) were still matched by landmark-unique as long as
they had an accessible name. Now only elements with a landmark role
are considered.

Closes issue dequelabs#4722

@WilcoFiers WilcoFiers left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for raising this. There's already a solution built into axe-core for doing the thing isLandmarkVirtual is trying to do. We should use that instead.

Comment thread lib/rules/landmark-unique-matches.js Outdated
Comment on lines +22 to +23
if (nodeName === 'section' || nodeName === 'form') {
const accessibleText = accessibleTextVirtual(vNode);
return !!accessibleText;
return isLandmarkRole && !!accessibleTextVirtual(vNode);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the real bug here is that section / form elements are always treated as landmarks if they have an accessible name, even if they have a role that doesn't map to your name. We should add a test for that:

<form role="tabpanel" aria-label="foo" id="pass-form-tabpanel1"></form>
<form role="tabpanel" aria-label="foo" id="pass-form-tabpanel1"></form>

Comment thread lib/rules/landmark-unique-matches.js Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Axe-core already has code for checking which element is a landmark. The real bug here is that this matches function wasn't updated to use getRoleType. You can remove isLandmarkVirtual from this file.

Suggested change
getRoleType(virtualNode) === 'landmark' &&
isVisibleToScreenReaders(virtualNode)

This fix also addresses the other issue you raised.

<div id="violation-role-search-2" role="search"></div>
<form id="violation-role-search" role="search"></form>
<form id="violation-role-search-2" role="search"></form>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, I wanted to add new test cases for <section>/<form> with a landmark role but no aria-label. However, all landmark roles were already used in the no-label section of this file, so there was no available role to use for new elements. Instead, I replaced the existing <div role="search"> with <form role="search">.

@nami8824

Copy link
Copy Markdown
Contributor Author

I've made the requested changes. Could you take another look?

0afa4bd

@nami8824 nami8824 requested a review from WilcoFiers April 30, 2026 00:02

@WilcoFiers WilcoFiers left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic. One more suggestion.

Comment on lines +8 to +17
<form
id="pass-form-search-aria-label-1"
role="search"
aria-label="form-search-label-1"
></form>
<form
id="pass-form-search-aria-label-2"
role="search"
aria-label="form-search-label-2"
></form>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the false positive of the original issue. Good to have that in the E2E suite.

Suggested change
<form
id="pass-form-search-aria-label-1"
role="search"
aria-label="form-search-label-1"
></form>
<form
id="pass-form-search-aria-label-2"
role="search"
aria-label="form-search-label-2"
></form>
<section
id="pass-section-tabpanel-aria-label-repeat-1"
role="tabpanel"
aria-label="My tab panel"
></section>
<section
id="pass-section-tabpanel-aria-label-repeat-2"
role="tabpanel"
aria-label="My tab panel"
></section>

Comment on lines +9 to +10
["#pass-form-search-aria-label-1"],
["#pass-form-search-aria-label-2"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
["#pass-form-search-aria-label-1"],
["#pass-form-search-aria-label-2"],
["#pass-section-tabpanel-aria-label-repeat-1"],
["#pass-section-tabpanel-aria-label-repeat-2"],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I tried applying the suggested JSON change by adding these selectors, but the integration test fails with "Element not found".
If I add the suggested elements only to landmark-unique-pass.html, without listing them in landmark-unique-pass.json, the test passes. Would you prefer that approach?
If the rule-matches test already verifies that these elements are not matched, I think we may not need additional integration coverage for this case.

Also, I originally added the form + role="search" + aria-label cases because I did not see existing integration coverage for form/section + accessible name + explicit landmark role passing. I also added similar coverage in test/rule-matches/landmark-unique-matches.js. If that coverage is not useful here, I can remove those cases.

@nami8824 nami8824 requested a review from WilcoFiers April 30, 2026 23:47
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.

Mis-identification of landmark role, false positive for unique AccName

2 participants