Skip to content

Commit 98d4eed

Browse files
Merge pull request #2714 from johanrd/fix/aria-activedescendant-accept-tabindex-neg1
BUGFIX: accept tabindex="-1" in template-require-aria-activedescendant-tabindex
2 parents 31887cd + 82e7652 commit 98d4eed

3 files changed

Lines changed: 13 additions & 15 deletions

File tree

docs/rules/template-require-aria-activedescendant-tabindex.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
<!-- end auto-generated rule header -->
88

9-
This rule requires all non-interactive HTML elements using the `aria-activedescendant` attribute to declare a `tabindex` of zero.
9+
This rule requires non-interactive HTML elements using the `aria-activedescendant` attribute to declare a `tabindex` of `0` or `-1`.
1010

1111
The `aria-activedescendant` attribute identifies the active descendant element of a composite widget, textbox, group, or application with document focus. This attribute is placed on the container element of the input control, and its value is set to the ID of the active child element. This allows screen readers to communicate information about the currently active element as if it has focus, while actual focus of the DOM remains on the container element.
1212

13-
Elements with `aria-activedescendant` must have a `tabindex` of zero in order to support keyboard navigation. Besides interactive elements, which are inherently keyboard-focusable, elements using the `aria-activedescendant` attribute must declare a `tabIndex` of zero with the `tabIndex` attribute.
13+
Elements with `aria-activedescendant` must be focusable to support keyboard navigation. `tabindex="0"` puts the element in the natural tab order; `tabindex="-1"` makes it focusable programmatically (e.g. via roving focus) but skips it in the tab order. Both are valid patterns for composite widgets — see the [W3C APG — Managing focus in composites using aria-activedescendant](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_focus_activedescendant).
1414

1515
## Examples
1616

@@ -19,8 +19,8 @@ This rule **forbids** the following:
1919
```gjs
2020
<template>
2121
<div aria-activedescendant='some-id'></div>
22-
<div aria-activedescendant='some-id' tabindex='-1'></div>
23-
<input aria-activedescendant={{some-id}} tabindex='-1' />
22+
<div aria-activedescendant='some-id' tabindex='-2'></div>
23+
<input aria-activedescendant={{some-id}} tabindex='-100' />
2424
</template>
2525
```
2626

@@ -32,9 +32,11 @@ This rule **allows** the following:
3232
<CustomComponent aria-activedescendant={{some-id}} />
3333
<CustomComponent aria-activedescendant={{some-id}} tabindex={{0}} />
3434
<div aria-activedescendant='some-id' tabindex='0'></div>
35+
<div aria-activedescendant='some-id' tabindex='-1'></div>
3536
<input />
3637
<input aria-activedescendant={{some-id}} />
3738
<input aria-activedescendant={{some-id}} tabindex={{0}} />
39+
<input aria-activedescendant={{some-id}} tabindex={{-1}} />
3840
</template>
3941
```
4042

lib/rules/template-require-aria-activedescendant-tabindex.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ module.exports = {
9191

9292
const tabindexValue = getTabindexNumericValue(tabindexAttr);
9393

94-
if (!Number.isFinite(tabindexValue) || tabindexValue < 0) {
94+
if (!Number.isFinite(tabindexValue) || tabindexValue < -1) {
9595
context.report({
9696
node,
9797
messageId: 'missingTabindex',

tests/lib/rules/template-require-aria-activedescendant-tabindex.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ const validHbs = [
1313
'<CustomComponent aria-activedescendant="choice1" />',
1414
'<CustomComponent aria-activedescendant="option1" tabIndex="-1" />',
1515
'<CustomComponent aria-activedescendant={{foo}} tabindex={{bar}} />',
16+
// tabindex="-1" is focusable-but-not-tabbable — the canonical pattern for
17+
// composite widgets that manage focus via roving focus / aria-activedescendant.
18+
// See W3C APG — https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_focus_activedescendant
19+
'<div aria-activedescendant="option0" tabindex="-1"></div>',
20+
'<div aria-activedescendant={{foo}} tabindex={{-1}}></div>',
21+
'<button aria-activedescendant="x" tabindex="-1"></button>',
1622
];
1723

1824
const invalidHbs = [
@@ -26,11 +32,6 @@ const invalidHbs = [
2632
output: '<div aria-activedescendant={{bar}} tabindex="0" />',
2733
errors: [{ message: ERROR_MESSAGE }],
2834
},
29-
{
30-
code: '<div aria-activedescendant={{foo}} tabindex={{-1}}></div>',
31-
output: '<div aria-activedescendant={{foo}} tabindex="0"></div>',
32-
errors: [{ message: ERROR_MESSAGE }],
33-
},
3435
{
3536
code: '<div aria-activedescendant="fixme" tabindex=-100></div>',
3637
output: '<div aria-activedescendant="fixme" tabindex="0"></div>',
@@ -41,11 +42,6 @@ const invalidHbs = [
4142
output: '<a aria-activedescendant="x" tabindex="0"></a>',
4243
errors: [{ message: ERROR_MESSAGE }],
4344
},
44-
{
45-
code: '<button aria-activedescendant="x" tabindex="-1"></button>',
46-
output: '<button aria-activedescendant="x" tabindex="0"></button>',
47-
errors: [{ message: ERROR_MESSAGE }],
48-
},
4945
];
5046

5147
function wrapTemplate(entry) {

0 commit comments

Comments
 (0)