Skip to content

BUGFIX: template-require-mandatory-role-attributes — lowercase role + split whitespace role lists#54

Closed
johanrd wants to merge 18 commits into
masterfrom
fix/role-required-aria-case-and-space-split
Closed

BUGFIX: template-require-mandatory-role-attributes — lowercase role + split whitespace role lists#54
johanrd wants to merge 18 commits into
masterfrom
fix/role-required-aria-case-and-space-split

Conversation

@johanrd

@johanrd johanrd commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Note

This is part of a series where Claude has audited eslint-plugin-ember against jsx-a11y, vuejs-accessibility, angular-eslint, lit-a11y and html-validate, ember-template-lint, and the HTML and WCAG specs.

[Mirror of ember-cli#2728 for Copilot review]

Depends on ember-cli#2725 — merge ember-cli#2725 first. Without it, the <input type="checkbox" role="switch"> exemption from ember-cli#2725's axobject-query-driven isSemanticRoleElement helper is missing, and this PR's existing test fixtures (plus the audit-parity file) would flag those shapes as missing aria-checked. ember-cli#2725 + ember-cli#2728 together produce the peer-aligned behavior; alone, ember-cli#2728 shifts some tests out of sync until ember-cli#2725 lands.

Two related fixes (shared root cause — both deal with role-token normalisation).

1. Case-insensitive role comparison

  • Premise: ARIA role token comparison inherits case-insensitivity from HTML's enumerated-attribute rules — role="COMBOBOX" is the same token as role="combobox". (WAI-ARIA 1.2 §4.1 explicitly defers case-sensitivity to the host language: "Case-sensitivity of the comparison inherits from the case-sensitivity of the host language.")
  • Problem: <div role="COMBOBOX"> was silently accepted — roles.get("COMBOBOX") returns undefined in aria-query, so the rule skipped the check entirely.

Fix: lowercase the role value before the lookup.

2. Whitespace-separated role fallback lists

  • Premise: Per WAI-ARIA 1.2 §4.1, a role attribute may list multiple tokens and "User agents MUST use the first token in the sequence of tokens in the role attribute value that matches the name of any non-abstract WAI-ARIA role."
  • Problem: role="combobox listbox" was treated as one opaque string. aria-query returned undefined, the rule skipped silently, and <div role="combobox listbox"> without aria-expanded + aria-controls wasn't flagged.

Fix: split on whitespace, validate the first recognised role per ARIA §4.1's first-token semantics. This diverges from jsx-a11y and vue-a11y, which validate every recognised token — our approach is closer to the UA behavior the spec prescribes. Noted as a deliberate divergence.

Helpers renamed to plural forms (getStaticRolesFromElement, getStaticRolesFromMustache). getMissingRequiredAttributes now returns { role, missing } so the reporter can cite the actually-checked role in the error message.

Four new tests cover both fixes (valid + invalid of each).

Prior art

Verified each peer in source:

Plugin Rule Verified behavior
jsx-a11y role-has-required-aria-props String(roleAttrValue).toLowerCase().split(' ').filter(recognised).forEach(validate). Validates EVERY recognised token.
vuejs-accessibility role-has-required-aria-props roleValue.toLowerCase().split(" ").forEach(role => { if (isAriaRoleDefinitionKey(role)) validate(role) }). Same pattern.
@angular-eslint/template role-has-required-aria Raw role string passed directly to roles.get(role). No .toLowerCase(), no .split().
lit-a11y role-has-required-aria-attrs Raw role string passed to isAriaRole / roles.get. No splitting, no lowercasing.

johanrd added 2 commits April 22, 2026 00:31
…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.
@johanrd johanrd requested a review from Copilot April 22, 2026 10:41
@github-actions

github-actions Bot commented Apr 22, 2026

Copy link
Copy Markdown

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
js small 13.95 ms 14.05 ms +0.7%
🟢 js medium 7.23 ms 7.08 ms -2.0%
🟢 js large 2.87 ms 2.77 ms -3.4%
gjs small 1.27 ms 1.25 ms -1.7%
gjs medium 629.07 µs 617.31 µs -1.9%
gjs large 248.41 µs 247.95 µs -0.2%
gts small 1.24 ms 1.25 ms +0.4%
gts medium 624.77 µs 622.24 µs -0.4%
gts large 246.50 µs 244.68 µs -0.7%

🟢 faster · 🔴 slower · 🟠 slightly slower · ⚪ within 2%

Full mitata output
clk: ~2.69 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.14.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            16.72 ms/iter  17.56 ms █                    
                      (12.40 ms … 30.93 ms)  28.04 ms █ ▇                  
                    (  5.62 mb …  10.65 mb)   7.24 mb ███▆█▆▃▃▁▃▁▁▃▁▃▁▃▃▆▁▃

js small (experiment)         14.66 ms/iter  15.47 ms   █                  
                      (12.95 ms … 19.98 ms)  18.85 ms   █▆█                
                    (  6.22 mb …   8.33 mb)   6.84 mb ▆████▄▆▄▄██▁█▁▄▁▁▁▁▁▄

                             ┌                                            ┐
                             ╷┌──────────┬──┐                             ╷
          js small (control) ├┤          │  ├─────────────────────────────┤
                             ╵└──────────┴──┘                             ╵
                               ╷┌──┬──┐         ╷
       js small (experiment)   ├┤  │  ├─────────┤
                               ╵└──┴──┘         ╵
                             └                                            ┘
                             12.40 ms           20.22 ms           28.04 ms

summary
  js small (experiment)
   1.14x faster than js small (control)

------------------------------------------- -------------------------------
js medium (control)            7.92 ms/iter   8.27 ms █▄                   
                       (6.70 ms … 13.92 ms)  13.32 ms ███                  
                    (  1.93 mb …   4.61 mb)   3.53 mb ███▄▅▄▄▃▂▃▄▃▂▁▂▁▁▂▁▁▃

js medium (experiment)         7.68 ms/iter   8.00 ms ▂█                   
                       (6.59 ms … 14.65 ms)  14.24 ms ██▂                  
                    (  3.20 mb …   3.96 mb)   3.52 mb ███▆█▆▅▂▃▂▁▂▁▁▁▁▁▂▁▁▂

                             ┌                                            ┐
                              ╷┌─────┬─┐                             ╷
         js medium (control)  ├┤     │ ├─────────────────────────────┤
                              ╵└─────┴─┘                             ╵
                             ╷┌────┬─┐                                    ╷
      js medium (experiment) ├┤    │ ├────────────────────────────────────┤
                             ╵└────┴─┘                                    ╵
                             └                                            ┘
                             6.59 ms           10.41 ms            14.24 ms

summary
  js medium (experiment)
   1.03x faster than js medium (control)

------------------------------------------- -------------------------------
js large (control)             3.34 ms/iter   3.14 ms  █                   
                        (2.48 ms … 8.46 ms)   7.61 ms  ██                  
                    (400.44 kb …   2.79 mb)   1.42 mb ▅██▃▂▃▂▁▂▂▁▂▂▂▁▂▂▁▁▁▁

js large (experiment)          3.01 ms/iter   2.87 ms  █▅                  
                        (2.54 ms … 6.18 ms)   5.52 ms  ██                  
                    (322.11 kb …   2.55 mb)   1.43 mb ███▂▃▂▃▂▁▂▂▂▁▁▁▂▁▁▁▂▂

                             ┌                                            ┐
                             ╷  ┌────┬                                    ╷
          js large (control) ├──┤    │────────────────────────────────────┤
                             ╵  └────┴                                    ╵
                              ╷┌──┬                     ╷
       js large (experiment)  ├┤  │─────────────────────┤
                              ╵└──┴                     ╵
                             └                                            ┘
                             2.48 ms            5.05 ms             7.61 ms

summary
  js large (experiment)
   1.11x faster than js large (control)

------------------------------------------- -------------------------------
gjs small (control)            1.40 ms/iter   1.35 ms █                    
                        (1.21 ms … 6.49 ms)   5.52 ms █▂                   
                    (243.97 kb …   1.66 mb)   1.06 mb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.39 ms/iter   1.29 ms █                    
                        (1.21 ms … 6.09 ms)   5.27 ms █                    
                    (196.60 kb …   1.56 mb)   1.06 mb █▅▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gjs small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌─┬                                       ╷
      gjs small (experiment) │ │───────────────────────────────────────┤
                             └─┴                                       ╵
                             └                                            ┘
                             1.21 ms            3.36 ms             5.52 ms

summary
  gjs small (experiment)
   1.01x faster than gjs small (control)

------------------------------------------- -------------------------------
gjs medium (control)         694.02 µs/iter 644.16 µs █                    
                      (595.71 µs … 6.31 ms)   3.76 ms █                    
                    (298.17 kb …   1.65 mb) 543.27 kb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      662.44 µs/iter 633.71 µs  █                   
                      (587.64 µs … 5.87 ms)   1.26 ms ▂█                   
                    ( 96.88 kb … 997.52 kb) 540.35 kb ██▆▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
        gjs medium (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬        ╷
     gjs medium (experiment) ││────────┤
                             └┴        ╵
                             └                                            ┘
                             587.64 µs           2.17 ms            3.76 ms

summary
  gjs medium (experiment)
   1.05x faster than gjs medium (control)

------------------------------------------- -------------------------------
gjs large (control)          270.95 µs/iter 264.06 µs  █▇                  
                      (237.20 µs … 5.08 ms) 344.38 µs  ██                  
                    (216.45 kb … 802.70 kb) 217.24 kb ▅██▅▃▇▆▇▃▁▂▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       275.58 µs/iter 264.86 µs  █▃                  
                      (237.29 µs … 5.71 ms) 345.07 µs  ██                  
                    (215.70 kb … 739.03 kb) 216.84 kb ▄██▅▃█▇▅▂▂▂▁▂▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───────────┬                              ╷
         gjs large (control) ├─┤           │──────────────────────────────┤
                             ╵ └───────────┴                              ╵
                             ╷ ┌─────────────┬                            ╷
      gjs large (experiment) ├─┤             │────────────────────────────┤
                             ╵ └─────────────┴                            ╵
                             └                                            ┘
                             237.20 µs         291.13 µs          345.07 µs

summary
  gjs large (control)
   1.02x faster than gjs large (experiment)

------------------------------------------- -------------------------------
gts small (control)            1.34 ms/iter   1.27 ms █                    
                        (1.20 ms … 6.82 ms)   5.42 ms █                    
                    (534.98 kb …   1.60 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.35 ms/iter   1.27 ms █                    
                        (1.20 ms … 6.66 ms)   5.53 ms █                    
                    (196.07 kb …   1.76 mb)   1.05 mb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                         ╷
         gts small (control) │ │─────────────────────────────────────────┤
                             └─┴                                         ╵
                             ┌─┬                                          ╷
      gts small (experiment) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             └                                            ┘
                             1.20 ms            3.36 ms             5.53 ms

summary
  gts small (control)
   1x faster than gts small (experiment)

------------------------------------------- -------------------------------
gts medium (control)         675.13 µs/iter 638.69 µs  █                   
                      (592.59 µs … 5.66 ms)   1.29 ms  █                   
                    (177.77 kb … 985.96 kb) 541.77 kb ██▅▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      666.13 µs/iter 634.38 µs █▆                   
                      (590.20 µs … 5.23 ms)   1.87 ms ██                   
                    ( 26.94 kb …   1.03 mb) 540.72 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌─┬                     ╷
        gts medium (control) ├┤ │─────────────────────┤
                             ╵└─┴                     ╵
                             ╷┌─┬                                         ╷
     gts medium (experiment) ├┤ │─────────────────────────────────────────┤
                             ╵└─┴                                         ╵
                             └                                            ┘
                             590.20 µs           1.23 ms            1.87 ms

summary
  gts medium (experiment)
   1.01x faster than gts medium (control)

------------------------------------------- -------------------------------
gts large (control)          270.63 µs/iter 262.63 µs  █                   
                      (235.64 µs … 5.34 ms) 365.55 µs  ██                  
                    (170.66 kb …   1.03 mb) 216.96 kb ███▄█▇▅▂▂▂▁▁▁▁▁▁▁▁▁▁▁

gts large (experiment)       268.14 µs/iter 261.60 µs  █                   
                      (235.24 µs … 5.22 ms) 346.33 µs  █▇                  
                    ( 45.02 kb …   1.00 mb) 216.62 kb ▅██▄▃▇▇▃▂▂▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────────┬                                ╷
         gts large (control) ├─┤         │────────────────────────────────┤
                             ╵ └─────────┴                                ╵
                             ╷ ┌────────┬                          ╷
      gts large (experiment) ├─┤        │──────────────────────────┤
                             ╵ └────────┴                          ╵
                             └                                            ┘
                             235.24 µs         300.40 µs          365.55 µs

summary
  gts large (experiment)
   1.01x faster than gts large (control)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes template-require-mandatory-role-attributes role-token normalization so the rule correctly enforces required ARIA attributes when role values are uppercase and/or contain whitespace-separated fallback role lists (per ARIA §4.1 host-language case-insensitivity + first-recognized-token semantics).

Changes:

  • Normalize static role values by lowercasing and splitting on whitespace into tokens.
  • Update the rule to validate required attributes against the first recognized role token (and report that role in the error).
  • Add/extend test coverage for uppercase roles and fallback role lists, plus an audit parity fixture.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
lib/rules/template-require-mandatory-role-attributes.js Implements role token normalization (lowercase + whitespace split) and validates the first recognized role token.
tests/lib/rules/template-require-mandatory-role-attributes.js Adds new valid/invalid cases for uppercase roles and whitespace-separated fallback role lists.
tests/audit/role-has-required-aria/peer-parity.js Adds an audit fixture intended to document peer-plugin parity and divergences.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/lib/rules/template-require-mandatory-role-attributes.js
Comment thread lib/rules/template-require-mandatory-role-attributes.js
Comment thread tests/audit/role-has-required-aria/peer-parity.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/audit/role-has-required-aria/peer-parity.js Outdated
Comment thread lib/rules/template-require-mandatory-role-attributes.js Outdated
Comment thread lib/rules/template-require-mandatory-role-attributes.js Outdated
@johanrd johanrd force-pushed the fix/role-required-aria-case-and-space-split branch from 88ab040 to 3d69b55 Compare April 22, 2026 17:10
johanrd added 7 commits April 23, 2026 21:39
…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).
…t whitespace-separated role lists

Two changes, shared root cause (both deal with role-token normalisation).

1. Case-insensitive role matching. Per HTML-AAM, ARIA role tokens
   compare as ASCII-case-insensitive. Before: <div role="COMBOBOX">
   was silently accepted because "COMBOBOX" didn't match "combobox" in
   aria-query. Now: lowercase before lookup.

2. Whitespace-separated role fallback lists. Per ARIA 1.2 §5.4, a role
   attribute may list multiple tokens to express a fallback; a UA picks
   the first one it recognises. Before: role="combobox listbox" was
   treated as one opaque string, aria-query returned undefined, and
   the rule skipped silently. Now: split on whitespace, check against
   the first recognised role's required attributes (matching jsx-a11y).

Helpers renamed to plural forms (getStaticRolesFromElement,
getStaticRolesFromMustache) and getMissingRequiredAttributes now
returns { role, missing } so the reporter can use the actually-checked
role in the error message.

Four new tests cover the two cases (valid + invalid of each).
…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.
@johanrd johanrd force-pushed the fix/role-required-aria-case-and-space-split branch from dc20a4b to d3d5abc Compare April 24, 2026 08:27
@johanrd johanrd requested a review from Copilot April 24, 2026 08:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 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.

Comment thread lib/rules/template-require-mandatory-role-attributes.js
Comment thread lib/rules/template-require-mandatory-role-attributes.js Outdated
Comment thread tests/lib/rules/template-require-mandatory-role-attributes.js

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread lib/rules/template-require-mandatory-role-attributes.js
Comment thread docs/rules/template-require-mandatory-role-attributes.md Outdated
johanrd added 2 commits April 24, 2026 19:03
…ired-ARIA description (Copilot review)

Per WAI-ARIA, role=spinbutton requires aria-valuenow. The docs table
previously said 'no required ARIA'. Update to reflect that <input
type='number'> supplies the required value state via native value/min/max,
which accessibility mappings expose as aria-valuenow — the role IS required,
the native element provides it.
…tic-role exemption in strict mode (Copilot review)

In classic Handlebars (.hbs), `{{input}}` globally resolves to Ember's
built-in input helper, which renders a native `<input>` — so the
semantic-role exemption (e.g., `role="switch"` on `{{input
type="checkbox"}}` is satisfied by the native `checked` property) is
correct.

In strict-mode GJS/GTS there is no lowercase `input` export from
`@ember/component` (only the PascalCase `<Input>` component), so
`{{input}}` in strict mode is always a user-bound identifier. We can't
prove it renders a native `<input>`, so applying the exemption risks
silently skipping required-ARIA checks on arbitrary components. Detect
strict mode by filename (matching the convention already established by
template-builtin-component-arguments) and return null for `{{input}}`
there; the regular required-ARIA flow then reports missing attributes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@johanrd johanrd requested review from Copilot and removed request for Copilot April 24, 2026 19:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…e cases, drop audit fixture

Upstream maintainers don't want the per-PR `tests/audit/peer-parity`
pattern. Port two basic widget-role-missing-required-attrs cases that
peer plugins also flag (slider missing aria-valuenow, checkbox missing
aria-checked) into the regular suite.

Other audit cases were already covered by the regular tests on this
branch — including the previous DIVERGENCE cases (role="COMBOBOX",
role="combobox listbox") which this PR's lowercase + split-whitespace
fix turns into INVALID assertions in the regular suite.
@johanrd johanrd closed this Apr 25, 2026
johanrd added a commit that referenced this pull request Apr 25, 2026
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