Skip to content

Commit 5362da7

Browse files
committed
fix(template-no-aria-hidden-on-focusable): clarify message and expand test coverage (Copilot review)
- Rephrase noAriaHiddenOnFocusable / noAriaHiddenOnAncestorOfFocusable messages to say 'can still receive focus (via Tab navigation or programmatically)' rather than 'reachable via Tab' — the latter was inaccurate for tabindex=-1 and programmatic focus paths. - Add test cases exercising contenteditable ('', 'true', 'plaintext-only', 'false') and <area href> / bare <area>. contenteditable='false' and bare <area> are asserted as valid; the other contenteditable values and <area href> flag.
1 parent fa83b32 commit 5362da7

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

lib/rules/template-no-aria-hidden-on-focusable.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ module.exports = {
191191
schema: [],
192192
messages: {
193193
noAriaHiddenOnFocusable:
194-
'aria-hidden="true" must not be set on focusable elements — it creates a keyboard trap (element reachable via Tab but hidden from assistive tech).',
194+
'aria-hidden="true" must not be set on focusable elements — the element can still receive focus (via Tab navigation or programmatically) while remaining hidden from assistive tech.',
195195
noAriaHiddenOnAncestorOfFocusable:
196-
'aria-hidden="true" must not be set on an element that contains focusable descendants — the descendants remain keyboard-reachable but are hidden from assistive tech.',
196+
'aria-hidden="true" must not be set on an element that contains focusable descendants — the descendants can still receive focus (via Tab navigation or programmatically) while remaining hidden from assistive tech.',
197197
},
198198
},
199199

tests/lib/rules/template-no-aria-hidden-on-focusable.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ ruleTester.run('template-no-aria-hidden-on-focusable', rule, {
4545
'<template><button disabled aria-hidden="true">Click me</button></template>',
4646
'<template><input disabled aria-hidden="true" /></template>',
4747

48+
// contenteditable="false" explicitly disables editing — not focusable on
49+
// its own, aria-hidden on it is fine.
50+
'<template><div contenteditable="false" aria-hidden="true"></div></template>',
51+
52+
// <area> without href isn't focusable.
53+
'<template><map name="m"><area aria-hidden="true" /></map></template>',
54+
4855
// Components — we don't know if they render a focusable element.
4956
'<template><CustomBtn aria-hidden="true" /></template>',
5057

@@ -105,6 +112,36 @@ ruleTester.run('template-no-aria-hidden-on-focusable', rule, {
105112
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
106113
},
107114

115+
// contenteditable makes a non-interactive element focusable.
116+
// Per HTML spec, "", "true", and "plaintext-only" all enable editing.
117+
{
118+
code: '<template><div contenteditable aria-hidden="true"></div></template>',
119+
output: null,
120+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
121+
},
122+
{
123+
code: '<template><div contenteditable="" aria-hidden="true"></div></template>',
124+
output: null,
125+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
126+
},
127+
{
128+
code: '<template><div contenteditable="true" aria-hidden="true"></div></template>',
129+
output: null,
130+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
131+
},
132+
{
133+
code: '<template><div contenteditable="plaintext-only" aria-hidden="true"></div></template>',
134+
output: null,
135+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
136+
},
137+
138+
// <area href> is a focusable link (HTML §6.6.3 "focusable area").
139+
{
140+
code: '<template><map name="m"><area href="/x" aria-hidden="true" /></map></template>',
141+
output: null,
142+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
143+
},
144+
108145
// Focusable descendants nested inside block helpers ({{#if}}, {{#each}},
109146
// {{#let}}) must be detected — aria-hidden cascades to every possible
110147
// render path.

0 commit comments

Comments
 (0)