Skip to content

BUGFIX: template-no-unsupported-role-attributes — honor aria-query attribute constraints#52

Closed
johanrd wants to merge 11 commits intomasterfrom
fix/implicit-role-attribute-constraints
Closed

BUGFIX: template-no-unsupported-role-attributes — honor aria-query attribute constraints#52
johanrd wants to merge 11 commits intomasterfrom
fix/implicit-role-attribute-constraints

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 22, 2026

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#2726 for Copilot review]

  • Premise 1: aria-query's elementRoles map encodes element → implicit-role pairings that depend on attribute state. Each entry lists attributes that must be set, undefined, or match a specific value.
  • Premise 2: Our getImplicitRole helper returned the first entry whose tag name matched, ignoring the attribute constraints on subsequent entries.
  • Conclusion: Several common elements got the wrong implicit role.

Concrete consequences of the old logic:

HTML Implicit role we returned Correct implicit role
<input type="text"> (no list) combobox textbox
`<input type="email tel url">(nolist`)
<input type="password"> button none (unmapped by aria-query, matches HTML-AAM)

The first two block valid usages involving attributes that textbox supports but combobox does not — notably aria-placeholder (covered by the new tests) and aria-multiline. Both textbox and combobox list aria-required in aria-query 5.3.2, so that attribute is not the concern here. The third row is arbitrarily wrong — there's no reason a password input should ever be treated as a button. <input type="password"> being left unmapped is consistent with HTML-AAM, which assigns it "No corresponding role."

Fix: walk every elementRoles entry whose name matches the tag; evaluate each attribute spec (checking constraints: ["set"], constraints: ["undefined"], and required values); pick the entry with the most attribute constraints satisfied.

Eight new valid tests cover the textbox/password paths. One existing test updated: <input type="email" aria-level={{this.level}} /> now maps to textbox in the error message; a sibling case with list="x" covers the combobox path.

Prior art

Plugin Rule Verified behavior
jsx-a11y role-supports-aria-props Computes implicit role via per-element lookup tables under src/util/implicitRoles/ (e.g. a.js checks for href before returning "link"); then validates that every aria-* attribute is in the role's supported-props list from aria-query.
vuejs-accessibility no equivalent rule vue-a11y's aria-props rule only validates that attribute names beginning with aria- are known ARIA attributes (via aria.has(lowered)); it does not compute implicit roles or check role↔prop compatibility.
@angular-eslint/template no equivalent rule angular-eslint's valid-aria validates ARIA attribute names and literal values against aria-query's definitions; it does not resolve implicit roles from element + attributes, so the role-supports-props check does not apply. role-has-required-aria is the inverse check (role → required props), not role → supported props.
lit-a11y role-supports-aria-attr Only reacts to an explicit role= attribute (Object.keys(element.attribs).includes('role')); does not compute an implicit role from tag + attributes, so the implicit-role false positive does not arise.

Audit fixture

Translated peer-plugin test fixture at tests/audit/role-supports-aria-props/peer-parity.js documenting our rule's behavior relative to jsx-a11y and lit-a11y. Runs as part of the default Vitest suite (included via the tests/**/*.js glob) and serves double-duty as an auditable peer-parity record and as regression coverage pinning current behavior.

johanrd added 5 commits April 21, 2026 07:36
…ute constraints in implicit-role lookup

The old getImplicitRole picked the first elementRoles entry whose name
matched the tag. For <input> it tried a type-attribute match but
ignored the rest of aria-query's constraint annotations.

Concrete consequences of the old logic:

- <input type="text"> without a `list` attribute returned "combobox"
  (because aria-query's {type=text, list=set}→combobox entry appears
  before {type=text, list=undefined}→textbox). Correct implicit role is
  textbox.
- <input type="email|tel|url"> behaved the same way.
- <input type="password"> isn't mapped by aria-query; the fallback
  branch returned the first input entry — "button".

The new implementation walks every elementRoles key whose tag matches,
checks each attribute spec (honoring `constraints: ["set"]` and
`constraints: ["undefined"]` as well as required values), and picks the
entry with the most attribute constraints satisfied.

Behavioral impact:

- Text-like inputs without a list attribute now correctly resolve to
  textbox and accept aria-required / aria-readonly / aria-placeholder.
- <input type="password"> now resolves to no implicit role, so global
  ARIA attrs don't trip the rule (matching jsx-a11y's treatment).
- One existing test updated: <input type="email"> now maps to textbox
  in the error message; added a sibling case with `list="x"` to cover
  the combobox path.
- Eight new valid tests cover the textbox/password paths.
…peer cases

Translates 33 cases from peer-plugin rules:
  - jsx-a11y role-supports-aria-props
  - lit-a11y role-supports-aria-attr
  - vuejs-accessibility (no direct equivalent; divergence noted)

The fixture documents where our rule now matches jsx-a11y (notably
<input type="email"> without `list` mapping to "textbox" per the
aria-query attribute constraints) plus the sibling `list=…` case
mapping to "combobox".
…euristic

The "pick entry with most attribute constraints satisfied" resolution is a
heuristic, not a spec-documented approach. aria-query's elementRoles is an
unordered Map; aria-query does not publish a resolution order for
multi-match cases. Name the inference, cite the motivating bug cases, and
note that if aria-query ever publishes resolution semantics we should
switch to those.
@johanrd johanrd requested a review from Copilot April 22, 2026 10:41
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🟠 js small 14.09 ms 14.42 ms +2.4%
🟢 js medium 7.28 ms 7.03 ms -3.4%
🟢 js large 2.90 ms 2.84 ms -2.3%
gjs small 1.24 ms 1.24 ms -0.0%
gjs medium 619.78 µs 613.28 µs -1.0%
gjs large 242.88 µs 243.66 µs +0.3%
gts small 1.22 ms 1.23 ms +0.3%
gts medium 619.94 µs 619.15 µs -0.1%
gts large 243.18 µs 243.99 µs +0.3%

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

Full mitata output
clk: ~3.06 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.05 ms/iter  18.74 ms █                    
                      (12.64 ms … 28.68 ms)  27.54 ms █▇▇ ▅                
                    (  6.10 mb …  10.16 mb)   7.30 mb ███▄█▄▄▄▄▁▄▁▁▁▄▁▇▁▄▄▇

js small (experiment)         14.96 ms/iter  15.73 ms    ██     ▄          
                      (13.13 ms … 20.05 ms)  18.17 ms   ███▅▅   █▅         
                    (  6.14 mb …   8.21 mb)   6.84 mb █▅██████▁▁██▁▁█▁▁▁▁▅█

                             ┌                                            ┐
                             ╷ ┌──────────┬────┐                          ╷
          js small (control) ├─┤          │    ├──────────────────────────┤
                             ╵ └──────────┴────┘                          ╵
                              ╷  ┌──┬─┐       ╷
       js small (experiment)  ├──┤  │ ├───────┤
                              ╵  └──┴─┘       ╵
                             └                                            ┘
                             12.64 ms           20.09 ms           27.54 ms

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

------------------------------------------- -------------------------------
js medium (control)            7.86 ms/iter   8.12 ms  █                   
                       (6.81 ms … 13.06 ms)  12.94 ms ██▇                  
                    (  2.64 mb …   4.38 mb)   3.53 mb ███▄▅▅▅▃▂▂▁▁▃▂▁▃▁▁▂▁▂

js medium (experiment)         7.56 ms/iter   7.68 ms  █                   
                       (6.69 ms … 13.92 ms)  12.91 ms ▄█                   
                    (  3.02 mb …   4.42 mb)   3.52 mb ██▄▅▃▂▃▃▂▂▂▁▁▂▁▂▁▁▁▁▂

                             ┌                                            ┐
                              ╷┌─────┬─┐                                  ╷
         js medium (control)  ├┤     │ ├──────────────────────────────────┤
                              ╵└─────┴─┘                                  ╵
                             ╷┌────┬┐                                     ╷
      js medium (experiment) ├┤    │├─────────────────────────────────────┤
                             ╵└────┴┘                                     ╵
                             └                                            ┘
                             6.69 ms            9.82 ms            12.94 ms

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

------------------------------------------- -------------------------------
js large (control)             3.39 ms/iter   3.40 ms  █                   
                       (2.54 ms … 10.53 ms)   7.65 ms  █▃                  
                    (299.95 kb …   3.06 mb)   1.43 mb ▄██▃▄▂▃▃▁▂▁▂▂▂▂▂▁▁▁▂▂

js large (experiment)          3.05 ms/iter   2.94 ms  █                   
                        (2.60 ms … 6.43 ms)   6.01 ms  █                   
                    (190.33 kb …   2.68 mb)   1.43 mb ███▃▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────┬                                    ╷
          js large (control) ├─┤     │────────────────────────────────────┤
                             ╵ └─────┴                                    ╵
                              ╷┌──┬                         ╷
       js large (experiment)  ├┤  │─────────────────────────┤
                              ╵└──┴                         ╵
                             └                                            ┘
                             2.54 ms            5.09 ms             7.65 ms

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

------------------------------------------- -------------------------------
gjs small (control)            1.35 ms/iter   1.31 ms █                    
                        (1.19 ms … 5.40 ms)   4.45 ms █▃                   
                    (164.51 kb …   1.66 mb)   1.06 mb ██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.34 ms/iter   1.26 ms █                    
                        (1.20 ms … 6.26 ms)   4.53 ms █                    
                    (223.98 kb …   1.91 mb)   1.06 mb █▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                         ╷
         gjs small (control) │ │─────────────────────────────────────────┤
                             └─┴                                         ╵
                             ┌─┬                                          ╷
      gjs small (experiment) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             └                                            ┘
                             1.19 ms            2.86 ms             4.53 ms

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

------------------------------------------- -------------------------------
gjs medium (control)         660.74 µs/iter 633.13 µs  █                   
                      (590.26 µs … 4.53 ms)   1.33 ms ▂█                   
                    ( 28.48 kb …   1.31 mb) 541.77 kb ██▃▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      657.98 µs/iter 626.72 µs █                    
                      (583.63 µs … 4.68 ms)   1.98 ms █▄                   
                    (115.60 kb … 985.40 kb) 540.88 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌┬                     ╷
        gjs medium (control) ├┤│─────────────────────┤
                             ╵└┴                     ╵
                             ╷┌┬                                          ╷
     gjs medium (experiment) ├┤│──────────────────────────────────────────┤
                             ╵└┴                                          ╵
                             └                                            ┘
                             583.63 µs           1.28 ms            1.98 ms

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

------------------------------------------- -------------------------------
gjs large (control)          265.72 µs/iter 258.51 µs  █                   
                      (231.86 µs … 4.56 ms) 384.17 µs  █▄                  
                    ( 17.38 kb … 925.80 kb) 217.33 kb ███▄▇▆▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       267.91 µs/iter 261.65 µs  █                   
                      (235.16 µs … 4.59 ms) 361.85 µs  █                   
                    (170.18 kb … 776.48 kb) 216.51 kb ▆██▂▅▆▃▁▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───────┬                                  ╷
         gjs large (control) ├─┤       │──────────────────────────────────┤
                             ╵ └───────┴                                  ╵
                              ╷┌────────┬                          ╷
      gjs large (experiment)  ├┤        │──────────────────────────┤
                              ╵└────────┴                          ╵
                             └                                            ┘
                             231.86 µs         308.01 µs          384.17 µs

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

------------------------------------------- -------------------------------
gts small (control)            1.31 ms/iter   1.24 ms █                    
                        (1.19 ms … 5.56 ms)   4.66 ms █                    
                    (460.60 kb …   1.68 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.31 ms/iter   1.25 ms █                    
                        (1.20 ms … 5.29 ms)   4.45 ms █                    
                    (652.60 kb …   1.72 mb)   1.05 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gts small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌─┬                                       ╷
      gts small (experiment) │ │───────────────────────────────────────┤
                             └─┴                                       ╵
                             └                                            ┘
                             1.19 ms            2.92 ms             4.66 ms

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

------------------------------------------- -------------------------------
gts medium (control)         656.14 µs/iter 630.98 µs  █                   
                      (586.45 µs … 4.92 ms)   1.28 ms  █                   
                    (125.27 kb …   1.25 mb) 541.44 kb ██▄▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      658.43 µs/iter 627.99 µs  █                   
                      (588.17 µs … 4.61 ms)   1.63 ms ▇█                   
                    (279.00 kb …   1.29 mb) 541.27 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌─┬                          ╷
        gts medium (control) ├┤ │──────────────────────────┤
                             ╵└─┴                          ╵
                             ╷┌─┬                                         ╷
     gts medium (experiment) ├┤ │─────────────────────────────────────────┤
                             ╵└─┴                                         ╵
                             └                                            ┘
                             586.45 µs           1.11 ms            1.63 ms

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

------------------------------------------- -------------------------------
gts large (control)          264.15 µs/iter 260.16 µs  █                   
                      (233.13 µs … 4.39 ms) 354.13 µs  █▆                  
                    ( 71.36 kb …   1.21 mb) 217.05 kb ▆██▄▃▇▅▂▁▂▁▂▁▁▁▁▁▁▁▁▁

gts large (experiment)       265.90 µs/iter 260.90 µs  █▆▃                 
                      (234.34 µs … 4.77 ms) 309.04 µs  ███                 
                    (143.42 kb … 992.64 kb) 216.58 kb ▄███▅▂▂▆█▇▅▂▂▁▁▂▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────────┬                                ╷
         gts large (control) ├─┤         │────────────────────────────────┤
                             ╵ └─────────┴                                ╵
                             ╷ ┌─────────┬               ╷
      gts large (experiment) ├─┤         │───────────────┤
                             ╵ └─────────┴               ╵
                             └                                            ┘
                             233.13 µs         293.63 µs          354.13 µs

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

Copy link
Copy Markdown

Copilot AI left a comment

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-no-unsupported-role-attributes implicit-role resolution to honor aria-query’s per-element attribute constraints, preventing incorrect implicit roles (notably for various <input> types) and the resulting false positives/negatives in aria-* supported-props validation.

Changes:

  • Reworked implicit-role lookup to evaluate aria-query elementRoles entries against the element’s static attribute state and choose the most specific match.
  • Added/updated rule tests covering <input type="text|email|tel|url"> vs combobox paths and ensuring <input type="password"> yields “no implicit role”.
  • Added an “audit” RuleTester fixture for peer-plugin parity comparisons.

Reviewed changes

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

File Description
lib/rules/template-no-unsupported-role-attributes.js Implements attribute-constraint-aware implicit role selection.
tests/lib/rules/template-no-unsupported-role-attributes.js Adds/updates test cases for textbox/password and email+list behaviors.
tests/audit/role-supports-aria-props/peer-parity.js Adds parity/audit fixture documenting behavior vs jsx-a11y/lit-a11y.

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

Comment thread lib/rules/template-no-unsupported-role-attributes.js Outdated
Comment thread lib/rules/template-no-unsupported-role-attributes.js Outdated
Comment thread tests/audit/role-supports-aria-props/peer-parity.js Outdated
johanrd added 2 commits April 22, 2026 13:56
- getStaticAttrValue() now trims chars so callers don't handle whitespace.
- Lowercase the node value when comparing against aria-query's attrSpec.value
  (HTML enumerated attribute values are ASCII case-insensitive per HTML
  common-microsyntaxes §2.3.3).
- Remove unused createUnsupportedAttributeErrorMessage() dead code; the rule
  uses messageId/data exclusively.
Copy link
Copy Markdown

Copilot AI left a comment

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 2 comments.


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

Comment thread tests/audit/role-supports-aria-props/peer-parity.js Outdated
Comment thread tests/lib/rules/template-no-unsupported-role-attributes.js
@johanrd johanrd force-pushed the fix/implicit-role-attribute-constraints branch from 5ad45c5 to 3de05b9 Compare April 22, 2026 17:11
Copy link
Copy Markdown

Copilot AI left a comment

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 2 comments.


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

Comment thread lib/rules/template-no-unsupported-role-attributes.js
Comment thread tests/audit/role-supports-aria-props/peer-parity.js Outdated
… 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.
Copy link
Copy Markdown

Copilot AI left a comment

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 no new comments.


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

johanrd added a commit that referenced this pull request Apr 24, 2026
…ementRoles, valueless-label, fixable meta (Copilot review)

- scoreMatch now uses three tiers: exact value (3) > set (2) > undefined (1).
  Previously set and undefined both added +1, so a stricter 'set' constraint
  could lose to a looser 'undefined' when both matched. Exact-value entries
  still win over 'set' (e.g. <img alt=''> → presentation still beats the
  generic alt-set entry).
- Pre-index elementRoles by tag name at module load (same pattern as PR #52's
  template-no-unsupported-role-attributes). Turns a ~80-key scan per call
  into a 1–5-key lookup.
- hasNonEmptyLabelAttr returns false when attr.value == null — a valueless
  aria-label / aria-labelledby carries no accessible name, so it shouldn't
  be treated as a non-empty (author-declared) label.
- Add meta.fixable: null — rule is not autofixable, be explicit.
@johanrd johanrd requested a review from Copilot April 24, 2026 17:47
Copy link
Copy Markdown

Copilot AI left a comment

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.

Copy link
Copy Markdown

Copilot AI left a comment

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.

Copy link
Copy Markdown

Copilot AI left a comment

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 added 2 commits April 25, 2026 06:35
…ases, drop audit fixture

Upstream maintainers don't want the per-PR `tests/audit/peer-parity`
pattern. Port two unique audit cases into the regular suite:
- `<body aria-expanded=...>` — pins our implicit-role resolution
  ("generic" via aria-query, vs jsx-a11y's "document"); both flag,
  message differs.
- `<a aria-checked />` — acknowledged false-positive vs jsx-a11y; we
  resolve href-less <a> to implicit role "generic" where aria-checked
  is unsupported, jsx-a11y treats it as having no role and accepts.

All other audit cases were already covered by the regular tests.
…n <a> divergence note

Researched the href-less <a> divergence: per HTML-AAM 1.2 §3.5.3 the
implicit role IS `generic` (which is what we resolve via aria-query).
jsx-a11y's `getImplicitRoleForAnchor` returns `''` for href-less <a>,
and its role-supports-aria-props rule then early-returns on no-role and
silently allows any aria-* — jsx-a11y's own source comments this as
"This actually isn't true - should fix in future release". vue-a11y
walks aria-query the same way we do and reaches the same conclusion.
We're spec-current; jsx-a11y is spec-stale. Updated the test comment
to reflect this rather than calling our behavior an "acknowledged
false-positive".
@johanrd johanrd closed this Apr 25, 2026
johanrd added a commit that referenced this pull request Apr 26, 2026
…ementRoles, valueless-label, fixable meta (Copilot review)

- scoreMatch now uses three tiers: exact value (3) > set (2) > undefined (1).
  Previously set and undefined both added +1, so a stricter 'set' constraint
  could lose to a looser 'undefined' when both matched. Exact-value entries
  still win over 'set' (e.g. <img alt=''> → presentation still beats the
  generic alt-set entry).
- Pre-index elementRoles by tag name at module load (same pattern as PR #52's
  template-no-unsupported-role-attributes). Turns a ~80-key scan per call
  into a 1–5-key lookup.
- hasNonEmptyLabelAttr returns false when attr.value == null — a valueless
  aria-label / aria-labelledby carries no accessible name, so it shouldn't
  be treated as a non-empty (author-declared) label.
- Add meta.fixable: null — rule is not autofixable, be explicit.
johanrd added a commit that referenced this pull request Apr 27, 2026
…ementRoles, valueless-label, fixable meta (Copilot review)

- scoreMatch now uses three tiers: exact value (3) > set (2) > undefined (1).
  Previously set and undefined both added +1, so a stricter 'set' constraint
  could lose to a looser 'undefined' when both matched. Exact-value entries
  still win over 'set' (e.g. <img alt=''> → presentation still beats the
  generic alt-set entry).
- Pre-index elementRoles by tag name at module load (same pattern as PR #52's
  template-no-unsupported-role-attributes). Turns a ~80-key scan per call
  into a 1–5-key lookup.
- hasNonEmptyLabelAttr returns false when attr.value == null — a valueless
  aria-label / aria-labelledby carries no accessible name, so it shouldn't
  be treated as a non-empty (author-declared) label.
- Add meta.fixable: null — rule is not autofixable, be explicit.
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