Focus management
useArrowNavigationGroup and useFocusableGroup manage keyboard focus for groups of elements, like Fluent's Tabster.
useArrowNavigationGroup uses a roving tabindex: Tab enters or leaves the group in one step, the arrow keys move between items (axis horizontal / vertical / both / grid), Home/End jump to the first/last item, circular wraps around and memorizeCurrent remembers the position. ←/→ flip in RTL. useFocusableGroup turns the container into a single Tab stop: Enter moves inside, Esc moves back out. Combine the two hooks to navigate between cards. Spread the whole hook result onto the container.
Import
Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.
TSX
// npm install yugen-uiimport { useArrowNavigationGroup, useFocusableGroup } from "yugen-ui";Variants
Each tile is a standalone example: hover to copy, or press Code to see the source.
Grid & memorizeCurrent
↑/↓ follow the visual columns, ←/→ follow the order. Tabbing out and back in returns to the item you left.
Vertical, circular
axis="vertical" + circular: ↓ on the last item goes back to the first.
Introducing Yugen UI
The Sakura palette
useFocusableGroup inside an arrow group
←/→ move between cards; Enter goes into a card, Tab cycles through its buttons (limited-trap-focus), Esc returns to the card.
Usage
Do
- Use arrow navigation for toolbars, lists and tile grids: any group of many similar items.
- Set a suitable role and aria-label on the container (toolbar, group, list).
- Add useFocusableGroup for cards that contain several buttons.
Don't
- Don't put text inputs inside an arrow group (the arrow keys move the caret).
- Don't set tabIndex on the children yourself. The hook manages the roving tabindex.
Props
| Prop · Type | Default | Description |
|---|---|---|
| axis"horizontal" | "vertical" | "both" | "grid" | Default: "vertical" | Which arrow keys are used. |
| circularboolean | Default: false | Wraps around at both ends. |
| memorizeCurrentboolean | Default: false | Tabbing back into the group focuses the most recently focused item. |
| tabbableboolean | Default: false | Keeps every item in the Tab order (turns off the roving tabindex). |
| roleAriaRole | Default: — | Returned in the props object as well. |
| useFocusableGroup | Default: "limited" | limited: Enter to go in, Esc to go out · trap: Tab cycles inside · unlimited: Tab moves through the items. |
| (returns){ ref, onKeyDown, onFocus, onBlur, … } | Default: — | Spread it onto the container. If you need your own handlers, merge them with the returned ones. |