# jsx-a11y/interactive-supports-focus

> Require elements with an interactive role and an event handler to be focusable

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

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[lint.rules]
"jsx-a11y/interactive-supports-focus" = "error" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"jsx-a11y/interactive-supports-focus" = ["error", {
  tabbable = [
    "button",
    "checkbox",
    "link",
    "searchbox",
    "spinbutton",
    "switch",
    "textbox",
  ],
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad {"count": 2} */}
```jsx
const save = <div role="button" onClick={saveDraft}>Save</div>;
const tab = <span role="tab" onClick={showBilling}>Billing</span>;
```

Not reported:

{/* good */}
```jsx
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.

{/* bad {"options": [{"tabbable": []}], "count": 2} */}
```jsx
const save = <div role="button" onClick={saveDraft}>Save</div>;
const tab = <span role="tab" onClick={showBilling}>Billing</span>;
```
