refactor: extract landmark-roles util (preserving deliberate per-rule region exclusion)#2758
Conversation
… region exclusion)
| // downstream callers can extend if needed). The exact size is intentionally | ||
| // not hard-coded: the derivation is what matters, so if aria-query adds a | ||
| // new non-abstract landmark upstream it will be picked up automatically. | ||
| const ALL_LANDMARK_ROLES = buildLandmarkRoleSet(); |
There was a problem hiding this comment.
why does this need a function now where it didn't before?
There was a problem hiding this comment.
The set is now computed by being derived by aria-query's role map rather than being hand-maintained. This aligns with other external-lib derivations and picks up any new landmark roles aria-query adds automatically.
There was a problem hiding this comment.
wouldn't importing it also do that? I don't understand
There was a problem hiding this comment.
aria-query doesn't expose a landmark-roles list as a top-level export. The landmark role only appears as a superClass entry inside the per-role definitions in roles. So to get "every non-abstract role that descends from landmark" you have to walk roles and filter on superClass, which is what buildLandmarkRoleSet() does. If aria-query ever adds a direct export we can swap to that.
Three a11y rules share a concept of "landmark ARIA role." Today they each maintain their own hand-list. The counts look like drift:
template-no-redundant-roleregiontemplate-no-nested-landmarkregiontemplate-no-duplicate-landmark-elementsBut the two 7-role lists aren't drift — the
regionexclusion is deliberate, documented, and was landed in #2694 after a "what's MDN say?" review. The reasoning:Source: WAI-ARIA APG HTML5 landmark examples.
The third rule (
template-no-duplicate-landmark-elements) DOES includeregionbecause that rule inspects aria-label / aria-labelledby before classifying a node as a landmark — it can safely handle the full 8-role set.What this PR does
Adds
lib/utils/landmark-roles.jsthat makes the distinction explicit by exporting two sets:ALL_LANDMARK_ROLES— the 8 WAI-ARIA 1.2 §5.3.4 landmark roles, derived from aria-query (non-abstract roles whose superClass chain includes 'landmark', minus DPub-ARIAdoc-*roles).LANDMARK_ROLES— the 7-role subset excludingregion. The safe default for static-linting rules that can't verify accessible names.Both sets are derived from aria-query's role metadata so future WAI-ARIA additions flow through on aria-query upgrade.
Rule migrations:
template-no-duplicate-landmark-elements→ALL_LANDMARK_ROLES(verifies accessible names).template-no-nested-landmark→LANDMARK_ROLES(preserves Post-merge-review: Fix template-no-nested-landmark: drop port-only section/region #2694 behavior).template-no-redundant-role→LANDMARK_ROLES(preserves existing landmark-only behavior).Also removes the
region: ['section']entry fromtemplate-no-redundant-role'sROLE_TO_ELEMENTSmapping, since #2694's reasoning applies to that rule too:<section role="region">shouldn't be flagged as redundant when we can't verify the section has an accessible name.Behavior vs. master
template-no-duplicate-landmark-elements: no change — same 8 roles.template-no-nested-landmark: no change — same 7 roles.template-no-redundant-role:<section role="region">is no longer flagged as redundant (default mode). This aligns the rule with Post-merge-review: Fix template-no-nested-landmark: drop port-only section/region #2694's reasoning.Spec references