Skip to content

BUGFIX: template-no-empty-headings — recognize boolean aria-hidden#48

Merged
johanrd merged 14 commits intomasterfrom
fix/heading-accept-boolean-aria-hidden
Apr 25, 2026
Merged

BUGFIX: template-no-empty-headings — recognize boolean aria-hidden#48
johanrd merged 14 commits intomasterfrom
fix/heading-accept-boolean-aria-hidden

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 22, 2026

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

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.

This PR is part of a Phase 3 a11y-parity audit against jsx-a11y / vue-a11y / angular-eslint-template / lit-a11y.

  • Premise: A heading that's marked as decorative via aria-hidden is intentionally invisible to assistive technology — requiring text content in that heading is a false positive.
  • Problem: Our isHidden check only matched aria-hidden="true" as a case-sensitive string literal, so <h1 aria-hidden>, <h1 aria-hidden="">, <h1 aria-hidden="TRUE">, <h1 aria-hidden={{true}}>, etc. were treated as visible and flagged for missing content.

Fix: extract isAriaHiddenTruthy() that recognizes every plausible "hide-this" form (valueless, empty-string, "true" case-insensitive, mustache boolean/string-literal true/case-variants).

Prior art

Plugin Rule Verified behavior
jsx-a11y heading-has-content Flags empty <h1><h6>; skips hidden headings via isHiddenFromAT (jsx-a11y's aria-hidden interpretation — see Contested-semantics table below).
vuejs-accessibility heading-has-content Flags empty <h1><h6>; uses isHiddenFromScreenReader (vue's (value || "").toString() !== "false" — see Contested-semantics table below).
@angular-eslint/template elements-content Covers <h1><h6> together with <a> / <button> via a single regex matcher; skips hidden via isHiddenFromScreenReader. Not a dedicated heading rule.
lit-a11y no direct equivalent lit-a11y ships heading-hidden (headings must not be hidden) but no "heading has content" rule. Inverse concern.

Four ecosystem positions on valueless aria-hidden

The question "what does <el aria-hidden> (bare), aria-hidden="" (empty), or aria-hidden={{false}} mean?" has no single authoritative answer. Four defensible positions exist:

# Source Interpretation Evidence
1 jsx-a11y Valueless → hidden Side effect of jsx-ast-utils coercing valueless JSX attrs to boolean true, combined with rule check ariaHidden === true. Quirk: string aria-hidden="true" is NOT recognized because "true" !== true. Not a deliberate ARIA interpretation.
2 vue-a11y Anything not literal "false" → hidden isHiddenFromScreenReader.ts: (value || "").toString() !== "false". Catches valueless, empty, "TRUE", "anything". Non-spec shortcut.
3 axe-core / W3C ACT Rules Valueless/empty → INCOMPLETE, needs author review axe-core PR #3635: empty ARIA values reported as incomplete, "There is no real difference between an empty ARIA attribute, a null attribute, and not having the attribute at all." W3C ACT Rule 6a7281 explicitly scopes out empty values as inapplicable.
4 WAI-ARIA 1.2 spec Valueless/empty → default undefined → not hidden The aria-hidden value table lists exactly true / false / undefined (default). A missing attribute resolves to that default; an empty-string value isn't enumerated, so it isn't interpreted as true. aria-hidden is also not in HTML's boolean attributes list, so the "presence-implies-true" rule that applies to e.g. disabled does not extend here.

Browser testing shows disagreement even on the explicit aria-hidden="true" case (see Steve Faulkner's post and Mozilla bug 948540); no documented browser testing on valueless specifically — most likely a no-op matching the spec's undefined-default.

Design choice for this rule

We lean toward fewer false positives. A linter that flags a heading the author intentionally marked decorative (via bare aria-hidden) creates friction and loss of trust; a linter that silently accepts some genuinely-empty unhidden headings is the smaller cost when the signal is ambiguous. So any aria-hidden form that could plausibly mean "hide this" exempts the heading from the empty-content check.

Exempt (don't flag empty heading):

  • <h1 aria-hidden>, <h1 aria-hidden="">, <h1 aria-hidden="true">, "TRUE", "True"
  • <h1 aria-hidden={{true}}>, {{"true"}}, {{"TRUE"}}, {{"True"}}

Still flag (explicit opt-in to the content check):

  • <h1 aria-hidden="false">, {{false}}, {{"false"}}

Tests

Valid (heading exempted):

  • Valueless, empty, "true" / "TRUE" / "True", {{true}}, {{"true"}} / case-variants

Invalid (falsy explicitly → flagged):

  • <h1 aria-hidden="false"></h1>, {{false}}, {{"false"}}

johanrd added 6 commits April 21, 2026 07:27
Before: isHidden only matched aria-hidden="true" as a string literal.
Boolean / valueless / empty / mustache forms (<h1 aria-hidden />,
<h1 aria-hidden="" />, <h1 aria-hidden={{true}} />) slipped past as
"not hidden", so empty headings in those forms were flagged as empty
even when the author had intentionally hidden them from AT.

Fix: extract isAriaHiddenTruthy(). Recognize:
- valueless attribute (HBS AST has value=null or empty-string TextNode)
- "true" string literal (preserved)
- "" empty string
- {{true}} boolean mustache literal
- {{"true"}} string mustache literal

Per HTML boolean-attribute semantics (and jsx-a11y/vue-a11y convention),
presence of aria-hidden without an explicit "false" value is treated as
truthy. The strict ARIA spec treats bare aria-hidden as "undefined"
rather than "true", but every major linter in the ecosystem (and most
screen readers) treats it as true.

Four new test cases covering each of the recognized forms.
…itively

HTML attribute value comparison is ASCII case-insensitive per spec, so
`aria-hidden="TRUE"` and `aria-hidden="True"` (and their mustache-string
equivalents) should be recognised as truthy. Mirrors the same case-
handling choice made in ember-cli#2718 for `kind="captions"`.

Tests cover `"TRUE"`, `"True"`, `{{"TRUE"}}`, `{{"True"}}`.
Adds invalid tests for `aria-hidden={{false}}` and `aria-hidden={{"false"}}`
to lock down that falsy mustache values do not exempt an otherwise-empty
heading.
…ARIA spec

Per WAI-ARIA 1.2 §6.6, `aria-hidden` has value type true/false/undefined
with default `undefined`. Per §8.5, missing or empty-string attribute
values resolve to the default. So a valueless `aria-hidden` is NOT
hidden per spec — only an explicit `"true"` (ASCII case-insensitive per
HTML enumerated-attribute rules) hides the element.

The earlier direction of this PR borrowed the HTML boolean-attribute
intuition (presence = truthy) from jsx-a11y. That's a peer-plugin
convention, not a spec mandate — aria-hidden is an enumerated ARIA
attribute, not a boolean HTML one. vue-a11y's heading-has-content
doesn't exempt aria-hidden headings at all; lit-a11y has the inverse
rule.

Behaviour now:
- Exempt (hidden): `aria-hidden="true"` / "TRUE" / "True", `{{true}}`,
  `{{"true"}}` / case-variants.
- Flag (NOT hidden per spec): valueless `<h1 aria-hidden>`, empty
  `<h1 aria-hidden="">`, `{{false}}`, `{{"false"}}`, `"false"`.
…less aria-hidden

The valueless / empty-string aria-hidden case is genuinely contested in
the ecosystem — four positions exist (jsx-a11y / vue-a11y / axe-core /
WAI-ARIA spec), and no single authoritative source is decisive. Rather
than pick one interpretation and live with its false positives, this
rule leans toward fewer-false-positives: any aria-hidden form that could
plausibly mean "hide this" exempts the heading from the empty-content
check.

Truthy (exempt heading):
- valueless `<h1 aria-hidden>` — undefined-default per spec, but
  authors who write bare aria-hidden plausibly intend to hide.
- empty `<h1 aria-hidden="">` — same.
- `aria-hidden="true"` (ASCII case-insensitive) — unambiguous.
- `aria-hidden={{true}}` / `{{"true"}}` (case-insensitive) — unambiguous.

Falsy (still flag empty heading):
- `aria-hidden="false"`, `{{false}}`, `{{"false"}}` — explicit opt-out.

This reverses the previous spec-first direction on the valueless/empty
case. Rationale: a linter that flags intentional decorative markup
creates friction and loss of trust; a linter that misses some genuinely-
empty headings is preferable when the signal is ambiguous. The explicit
`aria-hidden="true"` cases, which ARE clearly hidden per spec, remain
exempt.
Move the explanation of valueless / empty-string aria-hidden handling
from the PR body into the published rule docs. The rule deviates from
WAI-ARIA 1.2 §aria-hidden (which resolves valueless aria-hidden to the
default 'undefined', not 'true') in order to favor fewer false
positives for this specific check.

Also document the 'opposite-direction' split with
template-no-aria-hidden-on-focusable / template-anchor-has-content
(where spec-literal interpretation applies), and the unambiguous cases
that always follow the spec.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🟢 js small 14.03 ms 13.41 ms -4.5%
🟢 js medium 6.57 ms 6.34 ms -3.5%
🟢 js large 2.61 ms 2.55 ms -2.2%
gjs small 1.11 ms 1.10 ms -1.0%
gjs medium 550.67 µs 549.08 µs -0.3%
gjs large 217.85 µs 217.45 µs -0.2%
gts small 1.09 ms 1.10 ms +0.1%
gts medium 549.99 µs 549.25 µs -0.1%
gts large 218.67 µs 218.74 µs +0.0%

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

Full mitata output
clk: ~2.74 GHz
cpu: AMD EPYC 9V74 80-Core Processor
runtime: node 24.14.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            16.29 ms/iter  17.69 ms █ ▂                  
                      (11.35 ms … 31.25 ms)  28.74 ms █▅█ ▂                
                    (  4.94 mb …  10.65 mb)   7.23 mb ███▇█▇▇▄▄▄▁▄▁▁▄▄▁▇▁▁▇

js small (experiment)         13.92 ms/iter  14.94 ms   █                  
                      (11.90 ms … 18.98 ms)  18.58 ms █ █ ▃▃▃              
                    (  6.18 mb …   8.30 mb)   6.84 mb █▄█▄████▁▆▆▁▄▆▆▄▁▁▁▁▄

                             ┌                                            ┐
                             ╷┌───────────┬──┐                            ╷
          js small (control) ├┤           │  ├────────────────────────────┤
                             ╵└───────────┴──┘                            ╵
                              ╷ ┌───┬─┐         ╷
       js small (experiment)  ├─┤   │ ├─────────┤
                              ╵ └───┴─┘         ╵
                             └                                            ┘
                             11.35 ms           20.05 ms           28.74 ms

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

------------------------------------------- -------------------------------
js medium (control)            7.29 ms/iter   7.62 ms  █                   
                       (6.03 ms … 16.73 ms)  13.80 ms ▂█▄                  
                    (  2.86 mb …   4.60 mb)   3.53 mb ███▂▅▅▃▁▅▂▂▁▁▁▁▁▁▂▁▁▂

js medium (experiment)         7.09 ms/iter   7.09 ms  █                   
                       (5.93 ms … 14.70 ms)  14.34 ms ▃█                   
                    (  2.37 mb …   3.96 mb)   3.51 mb ██▅▃▃▂▄▂▂▁▁▁▂▁▁▂▁▁▁▂▂

                             ┌                                            ┐
                              ╷┌────┬─┐                                ╷
         js medium (control)  ├┤    │ ├────────────────────────────────┤
                              ╵└────┴─┘                                ╵
                             ╷┌────┬                                      ╷
      js medium (experiment) ├┤    │──────────────────────────────────────┤
                             ╵└────┴                                      ╵
                             └                                            ┘
                             5.93 ms           10.14 ms            14.34 ms

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

------------------------------------------- -------------------------------
js large (control)             3.07 ms/iter   2.98 ms  █                   
                        (2.22 ms … 9.12 ms)   8.59 ms ▇█                   
                    (218.79 kb …   3.25 mb)   1.44 mb ██▆▃▄▂▂▂▂▂▂▁▂▁▂▁▁▁▂▁▁

js large (experiment)          2.81 ms/iter   2.66 ms  █                   
                        (2.34 ms … 7.64 ms)   5.94 ms ▄█                   
                    (303.23 kb …   2.22 mb)   1.42 mb ███▁▂▂▂▁▁▂▁▁▁▁▁▁▁▂▁▁▂

                             ┌                                            ┐
                             ╷┌────┬                                      ╷
          js large (control) ├┤    │──────────────────────────────────────┤
                             ╵└────┴                                      ╵
                              ╷┌─┬                     ╷
       js large (experiment)  ├┤ │─────────────────────┤
                              ╵└─┴                     ╵
                             └                                            ┘
                             2.22 ms            5.41 ms             8.59 ms

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

------------------------------------------- -------------------------------
gjs small (control)            1.21 ms/iter   1.14 ms █                    
                        (1.08 ms … 6.57 ms)   5.13 ms █                    
                    (374.75 kb …   1.82 mb)   1.06 mb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.20 ms/iter   1.11 ms █                    
                        (1.07 ms … 5.74 ms)   4.88 ms █                    
                    (351.55 kb …   1.78 mb)   1.06 mb █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gjs small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                        ╷
      gjs small (experiment) ││────────────────────────────────────────┤
                             └┴                                        ╵
                             └                                            ┘
                             1.07 ms            3.10 ms             5.13 ms

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

------------------------------------------- -------------------------------
gjs medium (control)         609.83 µs/iter 559.05 µs █                    
                      (532.50 µs … 5.17 ms)   2.64 ms █                    
                    (539.67 kb …   1.28 mb) 542.75 kb █▂▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      599.64 µs/iter 557.63 µs █                    
                      (531.74 µs … 5.91 ms)   2.87 ms █                    
                    (  8.05 kb …   1.26 mb) 541.00 kb █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                      ╷
        gjs medium (control) │ │──────────────────────────────────────┤
                             └─┴                                      ╵
                             ┌┬                                           ╷
     gjs medium (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             531.74 µs           1.70 ms            2.87 ms

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

------------------------------------------- -------------------------------
gjs large (control)          237.30 µs/iter 224.80 µs  █                   
                      (211.74 µs … 4.44 ms) 305.31 µs  █                   
                    (216.09 kb … 836.73 kb) 217.12 kb ▅██▆▅▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       238.30 µs/iter 224.47 µs  █                   
                      (211.33 µs … 4.64 ms) 295.40 µs  █▃                  
                    (106.08 kb … 901.30 kb) 216.61 kb ▄██▃▇▄▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────────┬                                ╷
         gjs large (control) ├─┤         │────────────────────────────────┤
                             ╵ └─────────┴                                ╵
                             ╷ ┌──────────┬                          ╷
      gjs large (experiment) ├─┤          │──────────────────────────┤
                             ╵ └──────────┴                          ╵
                             └                                            ┘
                             211.33 µs         258.32 µs          305.31 µs

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

------------------------------------------- -------------------------------
gts small (control)            1.18 ms/iter   1.11 ms █                    
                        (1.07 ms … 6.30 ms)   5.12 ms █                    
                    (666.57 kb …   1.48 mb)   1.06 mb █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.18 ms/iter   1.11 ms █                    
                        (1.08 ms … 5.97 ms)   5.11 ms █                    
                    (656.59 kb …   1.57 mb)   1.05 mb █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬                                           ╷
         gts small (control) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             ┌┬                                           ╷
      gts small (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             1.07 ms            3.10 ms             5.12 ms

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

------------------------------------------- -------------------------------
gts medium (control)         593.55 µs/iter 556.78 µs █                    
                      (531.74 µs … 5.26 ms)   2.04 ms █                    
                    ( 52.32 kb …   1.26 mb) 540.65 kb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      600.86 µs/iter 556.46 µs █                    
                      (530.66 µs … 5.83 ms)   3.12 ms █                    
                    (106.61 kb …   1.04 mb) 539.92 kb █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬                        ╷
        gts medium (control) ││────────────────────────┤
                             └┴                        ╵
                             ┌┬                                           ╷
     gts medium (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             530.66 µs           1.83 ms            3.12 ms

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

------------------------------------------- -------------------------------
gts large (control)          253.69 µs/iter 227.54 µs █                    
                      (211.36 µs … 5.12 ms) 649.01 µs █                    
                    ( 17.26 kb … 738.61 kb) 216.91 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts large (experiment)       239.70 µs/iter 225.02 µs  █                   
                      (212.40 µs … 4.56 ms) 288.43 µs  █▇                  
                    ( 80.11 kb … 769.83 kb) 216.79 kb ▃██▄▇▅▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌───┬                                        ╷
         gts large (control) │   │────────────────────────────────────────┤
                             └───┴                                        ╵
                             ╷┌─┬    ╷
      gts large (experiment) ├┤ │────┤
                             ╵└─┴    ╵
                             └                                            ┘
                             211.36 µs         430.19 µs          649.01 µs

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

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

Updates template-no-empty-headings to avoid false positives by treating more aria-hidden “truthy” forms (including valueless/empty and case-insensitive "true", plus mustache boolean/string literals) as hidden, thereby exempting decorative headings from the empty-content check.

Changes:

  • Add isAriaHiddenTruthy() and use it in isHidden() to broaden recognized aria-hidden truthy forms.
  • Extend rule tests with additional valid/invalid aria-hidden variants.
  • Document the rule’s intentional deviation from spec-literal semantics for valueless/empty aria-hidden.

Reviewed changes

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

File Description
tests/lib/rules/template-no-empty-headings.js Adds coverage for additional aria-hidden variants (truthy exemptions and explicit falsy cases).
lib/rules/template-no-empty-headings.js Implements isAriaHiddenTruthy() and wires it into hidden detection for headings.
docs/rules/template-no-empty-headings.md Explains the rule’s aria-hidden interpretation and its rationale.

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

Comment thread docs/rules/template-no-empty-headings.md Outdated
Comment thread lib/rules/template-no-empty-headings.js Outdated
Comment thread tests/lib/rules/template-no-empty-headings.js Outdated
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 4 comments.


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

Comment thread tests/lib/rules/template-no-empty-headings.js Outdated
Comment thread lib/rules/template-no-empty-headings.js Outdated
Comment thread lib/rules/template-no-empty-headings.js Outdated
Comment thread lib/rules/template-no-empty-headings.js Outdated
`isAriaHiddenTruthy` previously only handled raw TextNode and bare
MustacheStatement attribute values. The quoted-mustache form
`aria-hidden="{{true}}"` produces a `GlimmerConcatStatement` with a
single mustache part — resolve that case by descending into the single
static-literal part, mirroring the pattern established in
template-no-aria-hidden-focusable.

Leans toward "truthy" only on literal true / empty / bare-valueless to
match the rule's doc-stated ethos of fewer false positives.
@johanrd johanrd force-pushed the fix/heading-accept-boolean-aria-hidden branch from 0bcced1 to a02b3e9 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 3 comments.


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

Comment thread lib/rules/template-no-empty-headings.js Outdated
Comment thread lib/rules/template-no-empty-headings.js Outdated
Comment thread tests/lib/rules/template-no-empty-headings.js
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-empty-headings.js Outdated
Comment thread tests/lib/rules/template-no-empty-headings.js Outdated
… via shared helper (Copilot review)

Extract a new `getStaticAttrValue` util that resolves literal-valued
mustaches (`{{"foo"}}`, `{{true}}`, `{{-1}}`) and single-part concat
statements (`"{{true}}"`) to their static string value. `isAriaHiddenTruthy`
now delegates to the helper and compares the resolved string to `'true'`
(case-insensitive, whitespace-trimmed).

Behavior change: valueless `<h1 aria-hidden>`, `aria-hidden=""`, and the
mustache-empty-string equivalents (`aria-hidden={{""}}`, `aria-hidden="{{""}}"`,
`aria-hidden={{" "}}`) are no longer treated as hidden. Per WAI-ARIA 1.2
§6.6 value table, those shapes resolve to the default `undefined` — NOT
`true` — so the empty-content check still applies. Drops the previous
"fewer false positives" deviation rationale in favour of spec-literal
consistency with sibling rules (#19, #35, #41) that share the same
aria-hidden resolution.

Byte-identical carrier of lib/utils/static-attr-value.js across all PRs
that land it.
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 johanrd requested a review from Copilot April 24, 2026 18:14
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.

The rule and test comments cited "WAI-ARIA 1.2 §6.6 aria-hidden value
table". §6.6 is "Taxonomy of WAI-ARIA States and Properties" and only
categorizes attributes; the aria-hidden value table lives in §6.7
"Definitions of States and Properties". Use the bare #aria-hidden
anchor, which is unambiguous.
@johanrd johanrd merged commit b6e78e8 into master Apr 25, 2026
10 checks passed
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