Menu
Terminal-styled selection list with keyboard navigation, highlighting and optional hotkey indicators.
Installation
dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.
The hosted registry is not public yet because r.b4r7.dev does not resolve. Use this source checkout or a local registry preview until the endpoint returns 200.
Diffgazer packages are not yet published to npm. Until the first release, pack @diffgazer/ui and @diffgazer/keys from the repository and install those tarballs.
UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.
Usage
Examples
Detail Variant
Grouped with Labels
Checkbox and Radio Items
Custom Icons
Disabled Items
Submenu
Submenu (drill-down stack)
Controlled Keyboard Navigation
API Reference
Menu
MenuItem
MenuDivider
MenuGroup
MenuLabel
MenuItemCheckbox
MenuItemRadio
MenuSub
MenuSubTrigger
MenuSubContent
Data attributes
Accessibility
Keyboard Navigation
Keyboard navigation is built-in. The Controlled Keyboard Navigation example above demonstrates controlled mode with explicit state management. Arrow keys and their vim aliases j/k move the highlight, Enter activates selection.
| Key | Action |
|---|---|
| ArrowUp / k | Moves highlight to the previous item. Disabled items remain navigable and announced but cannot be activated. |
| ArrowDown / j | Moves highlight to the next item. Disabled items remain navigable and announced but cannot be activated. |
| Home / End | Moves highlight to the first or last item, including disabled items that remain nonactivatable. |
| Enter / Space | Activates the highlighted item or toggles checkbox/radio items. |
| Printable character | Starts or extends a typeahead query and moves the highlight to the matching item. j/k still move the highlight on an empty query buffer and only extend a query already in progress. |
| Escape / Tab | Calls onClose on the root menu. |
| ArrowRight | Opens the highlighted nested submenu. On a leaf item it does nothing and stays within the current submenu. |
| ArrowLeft / Escape | Closes submenu content and returns focus to its trigger. |
Notes
Composition Contract
Use Menu.Item and Menu.Divider as explicit children in the Menu JSX tree. Custom item UI belongs inside Menu.Item. Components that create items internally from an opaque wrapper are not part of the current public contract.
Row Columns
A default row is icon, label, accelerator. The label column starts immediately after the icon and the [n] accelerator is pushed to the row end, so rows with and without an accelerator keep their labels aligned. Detail rows keep the same shape with a right-aligned value instead of an accelerator.
Submenu presentation
Menu.Sub takes mode="flyout" | "stack" | "auto" (default "auto"). A flyout is a side-anchored panel. A stack is a drill-down: the submenu replaces the item list inside the SAME panel, at the same width and left edge, with a sticky back row that is both the breadcrumb and the pop control. "auto" resolves to stack at coarse pointer or below 640px, resolved before the submenu ever opens so touch never sees a flyout frame — a side flyout has nowhere to go on a narrow screen and ends up shifted back over the rows it came from, hiding them. The key model is identical in both: ArrowRight/Enter pushes, ArrowLeft/Escape pops, and Escape at the root level still closes the menu. While drilled in, the parent items are removed from the DOM tree that navigation reads, so arrow keys and typeahead never reach a row the user cannot see.
Built-in Keyboard Navigation
Menu includes keyboard navigation via useListbox (Arrow keys and their vim aliases j/k, Home/End, Enter/Space). For custom key bindings or cross-component navigation, use the highlighted, onHighlightChange, and onKeyDown props to add external handlers alongside the built-in behavior.
Source
Install via CLI: pnpm exec dgadd add ui/menu. Keyboard hooks are included as standalone copies. For the full experience, use --integration keys.
Highlighted source loads after this disclosure opens. Browse the source repository.