# Focus keys
> Assign keyboard shortcuts that focus an item or panel from anywhere under the listener, across groups and even from inside a text field.
Set `data-keyrove-focus-key` on an element to focus it with a shortcut from
anywhere under the listener.
Ctrl+Shift+1, 2 or 3 focuses a panel in this demo. Try a shortcut from the text
area: focus keys with Ctrl, Alt or Meta also work inside editable fields.
```html
Explorer
Editor
Terminal
```
```ts
import { keyRove } from '@mixedrays/keyrove';
document
.querySelector('#editor-panes')
.addEventListener('keydown', (e) => keyRove(e));
```
The panels are not navigation items, so arrows do not move between them.
Each panel has its own [combo](/docs/api#combos). A bare code also works:
`data-keyrove-focus-key="KeyE"` focuses an element with E outside editable
fields.
The move reports `action: 'focus'` to `onMove` and in the
[return value](/docs/api#return-value).
## As far as the listener hears
keyrove looks for focus keys on every element under the listener's element,
not just in the nearest root, so where you attach the listener is the only
scope control:
- On a panel, the keys work inside that panel.
- On `document`, they work page-wide.
- From inside a [nested root](/docs/examples/nested-roots), a focus key
outside it still fires. Focus keys are the third way out of a nested group,
after Tab and a key of your own.
## A key and an order
An element can have both `data-keyrove-item` and a focus key. This palette
supports arrow navigation and direct shortcuts to tools.
```html
```
```ts
document.querySelector('#tools').addEventListener('keydown', (e) => keyRove(e));
```
Press P for the pen, then ↓: the
arrow steps on from wherever the key landed, because the pen is a stop in the
same order the arrows walk. The readout tells the two apart, `focus` for the jump
and `next` for the step. Then Tab out of the palette and
Shift+Tab back: focus returns to
the tool you last reached, whichever way you reached it, because the
[roving tab stop](/docs/examples/roving-tabindex) follows a jump as it follows
an arrow.
Focus keys use the bindings you choose: V for Move, O for Ellipse, I for
Eyedropper. [Typeahead](/docs/examples/typeahead) instead matches labels.
Each tool also has `aria-keyshortcuts`. The demo uses it to display the shortcut
beside the label; see [telling users about it](#telling-users-about-it).
A focus key only moves focus. To select or activate the tool, add your own
logic, for example in `onMove` using the destination `to`.
## Item or not
A focus key needs no `data-keyrove-item`, and whether its element has one
decides what else reaches it:
- **An item**, like a tool in the palette, stays in its group's order, so the
arrows reach it too, and the key is a shortcut to a place they already go.
The jump is a move in that group: `from` is the item focus left, or `null`
from outside the group, and a
[roving tab stop](/docs/examples/roving-tabindex) moves when focus leaves a
roving item. Attach `followFocus` to `focusin` to update the stop when the
shortcut enters from outside the group.
- **Any other element**, like the panels, is reached by its key alone. No arrow
lands on it. A jump to it reports `from: null` and moves no group tab stop.
If focus is already inside it, `from` is that element and `to` is `null`.
Either way, pressing the key while focus is already inside its element is a
consumed no-op: the key is claimed, and focus stays where it is. The
[API reference](/docs/api#focus-keys) has the exact rules.
A panel with a list inside is best made that list's root: put
`data-keyrove-root` on the panel itself. The jump leaves focus on the panel,
and an arrow pressed there then enters the panel's own list. Without the root,
the arrow belongs to the group around the panels and lands on the first item
under the listener, whichever panel that is in.
```html
index.ts
README.md
```
The panel's `tabindex="-1"` is what lets it take focus while keeping it out of
the Tab order. Without it, or on any element that cannot
take focus, the key is still claimed, but focus stays where it was and
`onMove` does not fire.
## From inside a text field
Movement bindings leave [editable targets](/docs/examples/editable-targets)
their editing keys. Focus shortcuts are the exception:
- A combo holding Ctrl, Alt or
Meta fires from inside a field.
- A bare combo, or one holding only Shift, does not.
In the demo, `ctrl+shift+Digit1` reaches out of the text area; a bare `Digit1`
would type a "1" there and focus the panel from everywhere else.
Choose shortcuts that do not conflict with editing commands or text input.
For example, Ctrl+B can mean bold; Alt+letter can type accented characters on
macOS; and Windows can report AltGr as Ctrl+Alt. A `ctrl+alt+` focus shortcut
can therefore fire during text entry. No focus key runs while `isComposing`
is true.
## Precedence and ties
Focus keys take precedence over explicit movement bindings and defaults.
For example, an element's `Home` focus key overrides the usual Home action.
When two elements declare the same combo, the first in DOM order wins.
The attribute scan excludes skipped and disabled targets. An explicit
`focusKeys` map replaces that scan and bypasses skip checks, but still excludes
disabled targets; see
[configuration differences](/docs/attributes-and-options#configuration-differences).
If a target is excluded, another binding may handle the key. If none matches,
the browser keeps it. Consider the listener's scope when assigning shortcuts:
a bare letter on a document listener applies across the page.
## Telling users about it
Add
[`aria-keyshortcuts`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-keyshortcuts)
to expose a shortcut to assistive technology. keyrove does not set it for you.
ARIA uses key names, while keyrove uses physical codes:
`data-keyrove-focus-key="ctrl+shift+KeyE"` pairs with
`aria-keyshortcuts="Control+Shift+E"`.