Kebab Menu
The ⋯ menu for an item's low-frequency actions. One item list grouped by hairlines, the destructive item last in red, keys on the right, and a trigger that can wait for its row's hover.
- Launch demo
- Friday Q&A
- Studio walkthrough
Installation
$ pnpm dlx shadcn@latest add @videorc/kebab-menuUsage
import { KebabMenu } from "@/components/kebab-menu";<KebabMenu
items={[
{ id: "rename", label: "Rename…", icon: EditIcon, onSelect: rename },
{
id: "delete",
label: "Delete…",
destructive: true,
onSelect: askToDelete,
},
]}
label="Actions for Launch demo"
/>Use it for per-item actions in every list and card: recordings, assets, scenes, scheduled livestreams, chat rows. The row's primary action stays a click on the row; the menu holds what you do less often.
Don't build a menu by hand for a row, and don't put the row's one main action in it.
Examples
Groups, keys and a busy item
Items with the same group sit together, a hairline between groups, in the order each group first appears. Destructive items always go last, in a group of their own. shortcut takes a Shortcut Keys. While an item's work runs, busy swaps the dots for a spinner, and a disabled item says why on hover.
Revealed on hover
reveal="hover" hides the trigger until its host row is hovered or holds focus. Mark the row with group/kebab-host. The trigger stays visible while its menu is open, while it has keyboard focus and while it's busy.
With a confirm dialog
A destructive item that can't be undone opens a Confirm Dialog, and its label ends in "…". The recordings list above does this for Delete.
Theming
| Token or attribute | Meaning |
|---|---|
Button ghost, size="icon" | The 28 px trigger |
group/kebab-host | The row the trigger waits for with reveal="hover" |
data-popup-open | On the trigger while its menu is open; keeps it visible |
aria-busy | On the trigger while busy; keeps it visible |
Keyboard
| Key | Action |
|---|---|
| Enter Space | On the trigger, opens the menu |
| ↑ ↓ | Moves through the items, disabled ones included |
| Enter | Runs the highlighted item |
| Esc | Closes the menu and returns focus to the trigger |
Accessibility
The trigger is a button named by label: name the item it acts on ("Actions for Launch demo"), never just "More". A click on the trigger or in the menu never reaches a clickable row behind it. A disabled item stays in the arrow-key order and is described by its disabledReason, through Disabled Reason.
API reference
Prop
Type
KebabMenuItem
Prop
Type
kebabMenuGroups(items) returns the items in their groups, destructive last, for a menu of your own that should agree with this one.
Record Button
Record and its stop button as one control. Metallic red, lit from the click, with a glow that breathes like a camera's tally light while the session runs. One width through every state.
Kebab Context Menu
The right-click twin of a row's ⋯ menu, built from the same item list, so the two menus can never disagree.