jsx-a11y/interactive-supports-focus
Require elements with an interactive role and an event handler to be focusable
Also called jsx_a11y/interactive-supports-focus
role="button" makes a screen reader say "button", but it doesn't make a div behave like one. Without a tabIndex, the element never gets focus, so nobody can reach it with the keyboard.
The rule reports an element such as div or span that has an interactive role, a mouse or keyboard handler, and no tabIndex. In an editor, it suggests adding tabIndex={0}, which puts the element in the Tab order. For roles that a parent widget moves focus to, such as tab, option and menuitem, it also suggests tabIndex={-1}.
The rule skips components, controls such as button and input, elements with disabled, aria-disabled="true" or aria-hidden, and elements that already have their own meaning, such as li, p and h1.
Configure#
Off by default. To turn it on:
[lint.rules]
"jsx-a11y/interactive-supports-focus" = "error" # or "warn"Every option, set to its default:
[lint.rules]
"jsx-a11y/interactive-supports-focus" = ["error", {
tabbable = [
"button",
"checkbox",
"link",
"searchbox",
"spinbutton",
"switch",
"textbox",
],
}]Examples#
const save = <div role="button" onClick={saveDraft}>Elements with the button interactive role must be tabbable.Save</div>;
const tab = <span role="tab" onClick={showBilling}>Elements with the tab interactive role must be focusable.Billing</span>;const save = <button onClick={saveDraft}>Save</button>;
const publish = <div role="button" tabIndex={0} onClick={publishPost}>Publish</div>;
const tab = <span role="tab" tabIndex={-1} onClick={showBilling}>Billing</span>;Options#
| Option | Default | What it does |
|---|---|---|
tabbable | ["button", "checkbox", "link", "searchbox", "spinbutton", "switch", "textbox"] | Roles that must be in the Tab order. Your list replaces the default. |
The option only changes the message and the suggestions. A role in the list gets "must be tabbable" and only the tabIndex={0} suggestion. Any other role gets "must be focusable". The same elements are reported either way, and any tabIndex is enough.
{ "tabbable": [] }2 problemsconst save = <div role="button" onClick={saveDraft}>Elements with the button interactive role must be focusable.Save</div>;
const tab = <span role="tab" onClick={showBilling}>Elements with the tab interactive role must be focusable.Billing</span>;