Conversation
…or semantic-role exemptions
Replaces the 3-entry hand-list (`{input type}:{role}` pairings) with a
lookup against axobject-query's `elementAXObjects` + `AXObjectRoles`
maps. Mirrors the approach used by eslint-plugin-jsx-a11y (its
`isSemanticRoleElement` util) and @angular-eslint/template.
## Why
The hand-list covered 3 pairings: `checkbox:checkbox`, `checkbox:switch`,
`radio:radio`. axobject-query encodes substantially more — including
`input[type=range]:slider`, `input[type=number]:spinbutton`,
`input[type=text]:textbox`, `input[type=search]:searchbox`. Each of
these is a case where the native element already provides the role's
required ARIA state (e.g., `<input type=range>` provides value via its
native `value` attribute, satisfying `role=slider`'s `aria-valuenow`
requirement).
Using axobject-query directly:
- Gives us strict superset coverage of the hand-list.
- Stays in sync when axobject-query updates (the hand-list had already
drifted — the earlier revision incorrectly claimed menuitemcheckbox
/ menuitemradio pairings were in axobject-query when they aren't).
- Matches jsx-a11y / angular-eslint behavior, closing a documented
parity gap.
Adds `axobject-query@^4.1.0` as a direct dep. It's already a transitive
dep via other ecosystem packages; this elevates it to first-class.
## Changes
- `lib/rules/template-require-mandatory-role-attributes.js` — replace
`NATIVELY_CHECKED_INPUT_ROLE_PAIRS` + `isNativelyChecked` with
`isSemanticRoleElement` that walks `elementAXObjects` and checks
`AXObjectRoles`. Handles both `GlimmerElementNode` (angle-bracket
syntax) and `GlimmerMustacheStatement` (classic `{{input}}` helper).
- `package.json` — add `axobject-query@^4.1.0`.
- `tests/lib/rules/template-require-mandatory-role-attributes.js` —
add tests for the broadened coverage (`<input type=range role=slider>`
now valid in both gts and hbs forms).
- `docs/rules/template-require-mandatory-role-attributes.md` — rewrite
the exemption section to describe the axobject-query-backed lookup
with a table of known pairings.
…imary source axobject-query is the data package the rule queries, but the normative source for "native <input type=checkbox> exposes aria-checked via the checked IDL attribute" is HTML-AAM §el-input-checkbox. Adding the HTML-AAM link makes the exemption's spec grounding explicit instead of leaving axobject-query as a free-floating data source.
🏎️ Benchmark Comparison
Full mitata output |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates template-require-mandatory-role-attributes to avoid false positives when a native HTML element already supplies a role’s required ARIA state, by consulting axobject-query for semantic element↔role pairings.
Changes:
- Add an
axobject-query-backed semantic-role exemption (mirroring jsx-a11y / angular-eslint behavior). - Expand rule tests to cover exempt vs non-exempt
<input type=... role=...>pairings (including{{input}}helper cases). - Document the semantic-role exemption behavior and add
axobject-queryas a direct dependency.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/rules/template-require-mandatory-role-attributes.js | Adds axobject-query lookup to skip required-ARIA enforcement when the node is a semantic role element. |
| tests/lib/rules/template-require-mandatory-role-attributes.js | Adds valid/invalid cases for semantic input-role exemptions plus non-exempt pairings. |
| tests/audit/role-has-required-aria/peer-parity.js | Introduces an audit-only parity fixture (not in CI) documenting peer behaviors. |
| package.json | Adds axobject-query@^4.1.0 as a direct dependency. |
| docs/rules/template-require-mandatory-role-attributes.md | Documents semantic-role exemptions and adds references. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… perf (Copilot review)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f735a42 to
48ef43c
Compare
…e {{input}} assumption (Copilot review)
In strict GJS/GTS, {{input}} could resolve to any imported identifier — not
just Ember's classic helper. We assume the classic helper (renders native
input); false-positive risk is low in practice because strict-mode authors
rarely use the mustache form at all. Document the assumption inline so
future readers don't need to re-derive the tradeoff.
…Objects by tag (Copilot review) Benchmarked ~12.5x speedup on isSemanticRoleElement. The naive impl walked the full elementAXObjects map per call (O(concepts × axObjects × roles)); pre-indexing resolves each concept's exposed-role set once at module load and buckets concepts by tag, reducing the per-call hot path to a handful of entries per tag. Benchmark: 200k calls on a realistic tag/role mix — current 154 ms, indexed 12 ms. Behavior-preserving (140/140 parity combos verified before landing; 84/84 rule-test suite passes).
…havior Rebased onto #51's axobject-query semantic-role-element exemption. The audit fixture previously captured pre-#54 divergences (our rule accepted space-separated and case-insensitive role values) as VALID-for-us. After rebase, the rule splits whitespace + lowercases before lookup, so `<div role="combobox listbox">` and `<div role="COMBOBOX">` now flag — matching jsx-a11y. Move those cases from valid → invalid and rewrite the divergence comments as parity.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… by tag (Copilot review) Benchmarked ~2.6× speedup on getImplicitRole — bucketing the static elementRoles Map by tag turns the per-call scan of ~80 keys into a 1-5 key lookup per tag. Parity verified across 15 representative tag/attr combinations before landing. Matches the Q29 optimization pattern on #51's isSemanticRoleElement.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…e cases, drop audit fixture Upstream maintainers don't want the per-PR `tests/audit/peer-parity` pattern. Port the documented divergences from peer plugins (jsx-a11y, vue-a11y, angular-eslint) into the regular suite as VALID cases that pin our current behavior: - `role="combobox listbox"` (space-separated tokens — peers split, we don't) - `role="COMBOBOX"` / `role="SLIDER"` (case-insensitivity — peers lowercase before lookup, we don't) - `role="foobar"` (unknown — parity) Other audit cases were already covered by the regular tests or were pure peer-plugin restatements.
- Replace broken `html-aam-1.1` URL with `html-aam-1.0`. There is no HTML-AAM 1.1 published recommendation; 1.0 is the published TR and contains the cited `#el-input-checkbox` anchor. - Soften the description of how the native element supplies aria-checked. HTML-AAM 1.0 §3.5.59 derives the value from the element's checkedness (plus `indeterminate` for the `mixed` value), not directly from the `checked` IDL attribute.
… by tag (Copilot review) Benchmarked ~2.6× speedup on getImplicitRole — bucketing the static elementRoles Map by tag turns the per-call scan of ~80 keys into a 1-5 key lookup per tag. Parity verified across 15 representative tag/attr combinations before landing. Matches the Q29 optimization pattern on #51's isSemanticRoleElement.
[Mirror of ember-cli#2725 for Copilot review]
Note
This is part of a series where Claude has audited
eslint-plugin-emberagainst jsx-a11y, vuejs-accessibility, angular-eslint, lit-a11y and html-validate,ember-template-lint, and the HTML and WCAG specs.Premise
For some
{element, role}pairs, the native HTML element already supplies the role's required ARIA state. Classic example:<input type="checkbox" role="switch">. The nativecheckedstate coversaria-checked, which is the required ARIA state for bothrole="checkbox"androle="switch"(per WAI-ARIA 1.2 §switch,switchis a subclass ofcheckboxwhose required state isaria-checked; per HTML-AAM 1.0 §3.5.59, the native checkbox maps to thecheckboxrole witharia-checkedderived from its checkedness). Authors shouldn't need to addaria-checkedredundantly.Problem
The rule flags any element with a role missing required ARIA attributes, including patterns where the native element already provides the state.
<input type="checkbox" role="switch">is flagged for missingaria-checked— a documented false positive.Fix
Consult
axobject-query'selementAXObjects+AXObjectRolesmaps to determine when a native element satisfies a given role, mirroringeslint-plugin-jsx-a11y'sisSemanticRoleElement. When the pair matches, skip the missing-attribute check for that role.Adds
axobject-query@^4.1.0as a direct dependency. It's already installed as a transitive dep across the ecosystem; elevates to first-class.Coverage (non-exhaustive)
<input type="checkbox">checkbox,switchcheckedstate<input type="radio">radiocheckedstate<input type="range">slidervalue/min/max<input type="number">spinbuttonvalue(no required ARIA)<input type="text">textbox<input type="search">searchboxBoth angle-bracket syntax (
<input type="checkbox" role="switch">) and the classic{{input type="checkbox" role="switch"}}helper are handled.Undocumented pairings still flag
<input type="checkbox" role="menuitemcheckbox">/<input type="radio" role="menuitemradio">etc. — axobject-query doesn't list these, so they remain flagged for missingaria-checked. Matches jsx-a11y and angular-eslint behavior.Why not keep the hand-list?
An earlier revision of this PR hand-maintained a 3-entry
{input type}:{role}whitelist. Audit-time review showed:menuitemcheckbox/menuitemradiopairings that axobject-query doesn't actually encode. A hand-list encodes the maintainer's (imperfect) understanding of the library's data; using the library directly eliminates the drift class.Prior art
role-has-required-aria-propsisSemanticRoleElement(type, attributes)via axobject-query'selementAXObjectsrole-has-required-ariarole-has-required-aria-props{role: switch, type: checkbox}only — much narrower than axobject-queryrole-has-required-aria-attrsThis PR aligns with jsx-a11y / angular-eslint on the axobject-query-backed approach. vue-a11y's narrower carve-out and lit-a11y's no-exemption behavior are documented divergences.
What this PR does not fix
Role-token case-insensitivity at the rule level —
<input type="checkbox" role="SWITCH">is silently ignored (aria-query'sroles.get(role)is case-sensitive, so the rule short-circuits to null before reaching theisSemanticRoleElementcheck). That rule-wide gap is addressed in ember-cli#2728.