Skip to content

Commit ce6e2dd

Browse files
committed
fix(#22): drop details/option/datalist from focusable set (Copilot review)
Per HTML §6.6.3 'Sequential focus navigation', none of <details>, <option>, or <datalist> are focusable by default: - <details>: the focusable control is its <summary> child - <option>: not in default tab order; <select> is focused and arrow keys navigate options within it - <datalist>: no user-facing UI; the paired <input list> is focused Including them in UNCONDITIONAL_FOCUSABLE_TAGS was over-aggressive and caused false positives on this rule. Tests updated to pin the now-allowed cases.
1 parent 32a0eec commit ce6e2dd

2 files changed

Lines changed: 16 additions & 8 deletions

File tree

lib/rules/template-no-role-presentation-on-focusable.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,19 @@ function hasPresentationRole(node) {
5959
// focusable by default — clicks on a label forward to its associated control,
6060
// but the label itself isn't in the tab order. So it's excluded here even
6161
// though `isHtmlInteractiveContent` would return true for it.
62+
// Per HTML §6.6.3 "Sequential focus navigation", tags in the default tab
63+
// order without further qualification. Explicitly excluded:
64+
// - <details>: the focusable control is its <summary> child, not details.
65+
// - <option>: not in the default focus order; <select> is focused and
66+
// arrow keys navigate options within it (not Tab).
67+
// - <datalist>: no user-facing UI; the paired <input list> is focused.
6268
const UNCONDITIONAL_FOCUSABLE_TAGS = new Set([
6369
'button',
6470
'select',
6571
'textarea',
6672
'iframe',
6773
'embed',
6874
'summary',
69-
'details',
70-
'option',
71-
'datalist',
7275
]);
7376

7477
// Form-control tags whose `disabled` attribute removes them from the tab order

tests/lib/rules/template-no-role-presentation-on-focusable.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ ruleTester.run('template-no-role-presentation-on-focusable', rule, {
5555
'<template><button disabled role="presentation">Click</button></template>',
5656
'<template><input type="text" disabled role="presentation" /></template>',
5757

58+
// HTML §6.6.3 sequential-focus-navigation elements that are NOT
59+
// focusable themselves (focus lands on a child or a sibling):
60+
// - <details> — focusable control is its <summary>, not details itself
61+
// - <option> — not in default tab order; <select> receives focus
62+
// - <datalist> — no user-facing UI; paired <input list> is focused
63+
// So role="presentation" on these is allowed.
64+
'<template><details role="presentation"><summary>Hi</summary></details></template>',
65+
'<template><option role="presentation">X</option></template>',
66+
'<template><datalist role="presentation"><option>X</option></datalist></template>',
67+
5868
// contenteditable falsy values — not focusable, presentation is allowed.
5969
'<template><div contenteditable="false" role="presentation"></div></template>',
6070

@@ -161,11 +171,6 @@ ruleTester.run('template-no-role-presentation-on-focusable', rule, {
161171
output: null,
162172
errors: [{ messageId: 'invalidPresentation' }],
163173
},
164-
{
165-
code: '<template><details role="presentation"><summary>Hi</summary></details></template>',
166-
output: null,
167-
errors: [{ messageId: 'invalidPresentation' }],
168-
},
169174
{
170175
code: '<template><summary role="none">Hi</summary></template>',
171176
output: null,

0 commit comments

Comments
 (0)