Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/rules/template-require-aria-activedescendant-tabindex.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

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

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

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.

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.
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).

## Examples

Expand All @@ -19,8 +19,8 @@ This rule **forbids** the following:
```gjs
<template>
<div aria-activedescendant='some-id'></div>
<div aria-activedescendant='some-id' tabindex='-1'></div>
<input aria-activedescendant={{some-id}} tabindex='-1' />
<div aria-activedescendant='some-id' tabindex='-2'></div>
<input aria-activedescendant={{some-id}} tabindex='-100' />
</template>
```

Expand All @@ -32,9 +32,11 @@ This rule **allows** the following:
<CustomComponent aria-activedescendant={{some-id}} />
<CustomComponent aria-activedescendant={{some-id}} tabindex={{0}} />
<div aria-activedescendant='some-id' tabindex='0'></div>
<div aria-activedescendant='some-id' tabindex='-1'></div>
<input />
<input aria-activedescendant={{some-id}} />
<input aria-activedescendant={{some-id}} tabindex={{0}} />
<input aria-activedescendant={{some-id}} tabindex={{-1}} />
</template>
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ module.exports = {

const tabindexValue = getTabindexNumericValue(tabindexAttr);

if (!Number.isFinite(tabindexValue) || tabindexValue < 0) {
if (!Number.isFinite(tabindexValue) || tabindexValue < -1) {
context.report({
node,
messageId: 'missingTabindex',
Expand Down
16 changes: 6 additions & 10 deletions tests/lib/rules/template-require-aria-activedescendant-tabindex.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const validHbs = [
'<CustomComponent aria-activedescendant="choice1" />',
'<CustomComponent aria-activedescendant="option1" tabIndex="-1" />',
'<CustomComponent aria-activedescendant={{foo}} tabindex={{bar}} />',
// tabindex="-1" is focusable-but-not-tabbable — the canonical pattern for
// composite widgets that manage focus via roving focus / aria-activedescendant.
// See W3C APG — https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_focus_activedescendant
'<div aria-activedescendant="option0" tabindex="-1"></div>',
'<div aria-activedescendant={{foo}} tabindex={{-1}}></div>',
'<button aria-activedescendant="x" tabindex="-1"></button>',
];

const invalidHbs = [
Expand All @@ -26,11 +32,6 @@ const invalidHbs = [
output: '<div aria-activedescendant={{bar}} tabindex="0" />',
errors: [{ message: ERROR_MESSAGE }],
},
{
code: '<div aria-activedescendant={{foo}} tabindex={{-1}}></div>',
output: '<div aria-activedescendant={{foo}} tabindex="0"></div>',
errors: [{ message: ERROR_MESSAGE }],
},
{
code: '<div aria-activedescendant="fixme" tabindex=-100></div>',
output: '<div aria-activedescendant="fixme" tabindex="0"></div>',
Expand All @@ -41,11 +42,6 @@ const invalidHbs = [
output: '<a aria-activedescendant="x" tabindex="0"></a>',
errors: [{ message: ERROR_MESSAGE }],
},
{
code: '<button aria-activedescendant="x" tabindex="-1"></button>',
output: '<button aria-activedescendant="x" tabindex="0"></button>',
errors: [{ message: ERROR_MESSAGE }],
},
];

function wrapTemplate(entry) {
Expand Down
Loading