keyboard-interactionlisted
Install: claude install-skill soumit-kaz/lazysitter
# Keyboard interaction patterns
## Name the pattern before writing the widget
Users already know these contracts. A "dropdown" that is really a listbox but behaves like a menu is worse than either, because it defeats knowledge the user already has. Decide which of the standard patterns you are implementing, then implement **that** contract.
## The contracts
### Button
`Enter` and `Space` activate. That is the whole contract — and it is why `<button>` beats `<div role="button">`, which must re-implement it (and usually forgets `Space`, or forgets that `Space` should activate on keyup and not scroll the page).
### Disclosure (show/hide)
`Enter`/`Space` toggles. `aria-expanded` on the trigger, `aria-controls` pointing at the region. No trap, no arrow keys. The simplest pattern and the right answer far more often than a menu.
### Modal dialog
`Escape` closes. Focus is trapped inside. `Tab`/`Shift+Tab` cycle within. `role="dialog"` + `aria-modal="true"` + an accessible name via `aria-labelledby`. Content behind is `inert` or `aria-hidden`. Focus returns to the trigger on close.
### Menu / menubar (a list of *actions*)
`Enter`/`Space`/`ArrowDown` opens from the trigger. `ArrowUp`/`ArrowDown` move between items (wrapping). `Home`/`End` jump to first/last. Type-ahead jumps to a matching item. `Escape` closes and returns focus. `Tab` closes and moves on.
**Roving tabindex**: exactly one item is `tabIndex={0}`, the rest `-1`.
Use this for actions. Do **not** use `role="menu"` fo