Skip to content

BUGFIX: template-no-invalid-role — support DPUB/Graphics-ARIA and role-fallback lists#14

Closed
johanrd wants to merge 3 commits intomasterfrom
fix/invalid-role-aria-query
Closed

BUGFIX: template-no-invalid-role — support DPUB/Graphics-ARIA and role-fallback lists#14
johanrd wants to merge 3 commits intomasterfrom
fix/invalid-role-aria-query

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 21, 2026

Two related fixes in one rewrite. Both were false positives that rejected documented-valid ARIA patterns.

1. DPUB-ARIA and Graphics-ARIA roles

  • Premise: DPUB-ARIA and Graphics-ARIA are W3C Recommendations that extend WAI-ARIA with doc-* (40+ roles) and graphics-* (3 roles) tokens. aria-query indexes them under its roles map.
  • Problem: Our hand-maintained VALID_ROLES (~90 tokens) covered only WAI-ARIA base roles. role="doc-abstract", role="graphics-document", etc., were flagged as invalid.

Fix: derive VALID_ROLES from aria-query's concrete (non-abstract) role keys. Covers ~127 roles automatically.

A small ARIA 1.3 draft allowlist (associationlist, associationlistitemkey, associationlistitemvalue, comment, suggestion) is kept inline because aria-query doesn't yet ship those.

2. Whitespace-separated role fallback lists

  • Premise: ARIA 1.2 §4.1 WAI-ARIA Rolesrole is a list of tokens. The UA picks the first one it recognises; the others are fallbacks.
  • Problem: Our rule treated role="tabpanel row" as one opaque value — VALID_ROLES.has("tabpanel row") is false — and flagged the whole string as invalid.

Fix: split on whitespace, validate each token. The error message now names the first offending token (Invalid ARIA role 'foobar') rather than the whole string.

Before/after

HTML Before After
<div role="doc-abstract"> invalid valid
<svg role="graphics-document"> invalid valid
<div role="tabpanel row"> invalid (whole string) valid
<section role="doc-appendix doc-bibliography"> invalid (whole string) valid
<div role="tabpanel row foobar"> invalid (whole string as role name) invalid (specifically: 'foobar')

Three existing invalid tests updated to reflect the per-token error message; ten new valid tests cover the fixes.

Prior art

Plugin Rule Notes
jsx-a11y aria-role Uses aria-query's roles.keys() directly; .split(' ') the value and checks each token against the set of concrete roles.
vuejs-accessibility aria-role Same pattern as jsx-a11y.
lit-a11y aria-role No whitespace split; passes the full role string to isConcreteAriaRole, so multi-token fallback lists like role="tabpanel row" fail.

@angular-eslint/template's valid-aria rule validates aria-* attribute names and values rather than role tokens, so it has no direct analog for the fallback-list fix.

Upstream [email protected] has both gaps.

…pport DPUB-/Graphics-ARIA and role-fallback lists

Two related fixes, shared rewrite.

1. Replace the hand-maintained VALID_ROLES (~90 WAI-ARIA 1.2 tokens)
   with a derived list from aria-query (concrete — non-abstract — role
   keys), plus a small ARIA 1.3 draft-role allowlist that aria-query
   doesn't yet ship.

   Effect: DPUB-ARIA roles (doc-abstract, doc-chapter, …) and
   Graphics-ARIA roles (graphics-document, graphics-object,
   graphics-symbol) are no longer flagged as invalid.

2. Split the role value on whitespace before validating. A role
   attribute is a list of tokens per ARIA 1.2 §5.4 (role fallback).
   Each token must individually be valid.

   Effect: role="tabpanel row", role="doc-appendix doc-bibliography",
   and role="graphics-document document" now pass; role="tabpanel
   row foobar" flags the first invalid token ("foobar") instead of
   rejecting the whole string as one opaque role name.

Error message now names the specific offending token. Three existing
invalid tests updated accordingly (previously expected the whole
string; now the specific token).

Ten new valid tests cover DPUB/Graphics and the fallback-list shape.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 21, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🔴 js small 14.04 ms 14.83 ms +5.6%
🟢 js medium 7.20 ms 7.00 ms -2.8%
js large 2.84 ms 2.80 ms -1.6%
gjs small 1.26 ms 1.25 ms -0.4%
gjs medium 618.63 µs 614.66 µs -0.6%
🟢 gjs large 263.81 µs 247.42 µs -6.2%
gts small 1.23 ms 1.23 ms +0.1%
gts medium 617.92 µs 615.76 µs -0.4%
gts large 247.92 µs 246.36 µs -0.6%

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

Full mitata output
clk: ~3.04 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)            17.15 ms/iter  17.96 ms █                    
                      (12.40 ms … 29.86 ms)  29.72 ms █▆▃                  
                    (  5.67 mb …  10.62 mb)   7.31 mb ███▆█▄█▄▁▁▁▁▁▄▁▄█▁▁▄▄

js small (experiment)         15.56 ms/iter  15.74 ms   █ ▄ █              
                      (13.24 ms … 22.77 ms)  21.09 ms ▅▅███ █              
                    (  6.72 mb …   8.26 mb)   6.84 mb ████████▁▅▅▅▁▅▁▁▁▅▅▁▅

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

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

------------------------------------------- -------------------------------
js medium (control)            7.93 ms/iter   8.10 ms  █                   
                       (6.78 ms … 14.48 ms)  13.85 ms ██                   
                    (  2.62 mb …   4.37 mb)   3.55 mb ██▅▅▃▅▂▃▁▁▂▁▄▁▁▁▃▁▂▁▂

js medium (experiment)         7.57 ms/iter   7.96 ms █▇                   
                       (6.61 ms … 13.23 ms)  13.05 ms ██                   
                    (  2.60 mb …   4.37 mb)   3.54 mb ███▄▄▇▅▃▃▂▂▁▂▁▁▂▁▁▂▁▂

                             ┌                                            ┐
                              ╷┌─────┬┐                                   ╷
         js medium (control)  ├┤     │├───────────────────────────────────┤
                              ╵└─────┴┘                                   ╵
                             ╷┌────┬─┐                               ╷
      js medium (experiment) ├┤    │ ├───────────────────────────────┤
                             ╵└────┴─┘                               ╵
                             └                                            ┘
                             6.61 ms           10.23 ms            13.85 ms

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

------------------------------------------- -------------------------------
js large (control)             3.34 ms/iter   3.22 ms  █                   
                       (2.66 ms … 10.03 ms)   8.14 ms ▇█                   
                    (279.96 kb …   2.71 mb)   1.44 mb ██▃▄▃▃▁▂▁▂▁▁▃▂▂▁▁▁▁▁▁

js large (experiment)          3.10 ms/iter   2.88 ms  █                   
                        (2.58 ms … 9.35 ms)   6.69 ms  █                   
                    (826.05 kb …   2.40 mb)   1.43 mb ██▃▁▂▁▂▁▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                              ╷┌───┬                                      ╷
          js large (control)  ├┤   │──────────────────────────────────────┤
                              ╵└───┴                                      ╵
                             ╷┌──┬                            ╷
       js large (experiment) ├┤  │────────────────────────────┤
                             ╵└──┴                            ╵
                             └                                            ┘
                             2.58 ms            5.36 ms             8.14 ms

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

------------------------------------------- -------------------------------
gjs small (control)            1.42 ms/iter   1.38 ms █                    
                        (1.21 ms … 6.39 ms)   5.82 ms █▃                   
                    (164.80 kb …   1.66 mb)   1.06 mb ██▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.38 ms/iter   1.29 ms █                    
                        (1.21 ms … 6.46 ms)   5.58 ms █                    
                    ( 15.21 kb …   2.11 mb)   1.06 mb █▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

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

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

------------------------------------------- -------------------------------
gjs medium (control)         682.90 µs/iter 645.08 µs █                    
                      (582.09 µs … 5.49 ms)   3.18 ms █                    
                    ( 70.95 kb …   1.12 mb) 540.95 kb █▆▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      659.85 µs/iter 631.36 µs █▅                   
                      (582.32 µs … 5.75 ms)   1.90 ms ██                   
                    ( 46.63 kb …   1.11 mb) 540.46 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
        gjs medium (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                     ╷
     gjs medium (experiment) ││─────────────────────┤
                             └┴                     ╵
                             └                                            ┘
                             582.09 µs           1.88 ms            3.18 ms

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

------------------------------------------- -------------------------------
gjs large (control)          359.34 µs/iter 436.87 µs █                    
                      (237.58 µs … 6.74 ms) 649.00 µs ██                   
                    ( 43.14 kb … 921.91 kb) 217.71 kb ███▃▂▂▂▁▁▂▂▂▂▂▂▄▄▅▂▁▁

gjs large (experiment)       268.85 µs/iter 262.88 µs  █▃                  
                      (235.67 µs … 5.45 ms) 348.16 µs  ██                  
                    ( 16.11 kb …   1.31 mb) 216.81 kb ▅██▅▃█▆▄▂▁▂▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌───────────┬────────┐                      ╷
         gjs large (control) ├┤           │        ├──────────────────────┤
                             ╵└───────────┴────────┘                      ╵
                             ╷┌──┬       ╷
      gjs large (experiment) ├┤  │───────┤
                             ╵└──┴       ╵
                             └                                            ┘
                             235.67 µs         442.34 µs          649.00 µs

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

------------------------------------------- -------------------------------
gts small (control)            1.33 ms/iter   1.26 ms █                    
                        (1.20 ms … 6.36 ms)   4.38 ms █                    
                    (312.39 kb …   1.82 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.33 ms/iter   1.25 ms █                    
                        (1.20 ms … 6.61 ms)   4.41 ms █                    
                    (504.66 kb …   1.63 mb)   1.05 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

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

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

------------------------------------------- -------------------------------
gts medium (control)         663.79 µs/iter 637.18 µs  █                   
                      (585.72 µs … 5.59 ms)   1.48 ms  █                   
                    ( 97.36 kb …   1.35 mb) 541.83 kb ██▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      661.54 µs/iter 633.37 µs █                    
                      (586.34 µs … 5.86 ms)   1.91 ms █▆                   
                    (124.56 kb …   1.08 mb) 540.05 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌─┬                           ╷
        gts medium (control) ├┤ │───────────────────────────┤
                             ╵└─┴                           ╵
                             ╷┌─┬                                         ╷
     gts medium (experiment) ├┤ │─────────────────────────────────────────┤
                             ╵└─┴                                         ╵
                             └                                            ┘
                             585.72 µs           1.25 ms            1.91 ms

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

------------------------------------------- -------------------------------
gts large (control)          273.20 µs/iter 263.96 µs  █                   
                      (236.65 µs … 5.64 ms) 376.70 µs  ██                  
                    (216.09 kb … 953.48 kb) 216.98 kb ███▃█▇▃▂▂▁▁▁▁▁▁▁▁▁▁▁▁

gts large (experiment)       270.32 µs/iter 262.35 µs  █                   
                      (235.87 µs … 5.53 ms) 334.54 µs  ██                  
                    (215.70 kb … 675.82 kb) 216.80 kb ▆███▄▅█▆▃▂▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────────┬                                ╷
         gts large (control) ├─┤         │────────────────────────────────┤
                             ╵ └─────────┴                                ╵
                             ╷┌─────────┬                    ╷
      gts large (experiment) ├┤         │────────────────────┤
                             ╵└─────────┴                    ╵
                             └                                            ┘
                             235.87 µs         306.29 µs          376.70 µs

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

johanrd and others added 2 commits April 21, 2026 16:24
Translates 32 cases from peer-plugin rules:
  - jsx-a11y aria-role
  - vuejs-accessibility aria-role
  - lit-a11y aria-role

Fixture documents parity after this fix:
  - DPUB-ARIA and Graphics-ARIA roles accepted (via aria-query).
  - Space-separated role tokens accepted when all are valid, and the
    invalid-token variant names the specific offending token.

Remaining divergences (case-insensitive comparison, empty-string role
not flagged) are annotated inline.
@johanrd
Copy link
Copy Markdown
Owner Author

johanrd commented Apr 21, 2026

Moved upstream to ember-cli#2729. See that PR.

@johanrd johanrd closed this Apr 21, 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.

1 participant