videorc/ui

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 demo41:10
  • Friday Q&A1:02:45
  • Studio walkthrough12:31

Installation

$ pnpm dlx shadcn@latest add @videorc/kebab-menu

Usage

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.

Launch demo

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.

Studio wall
Night city
Soft gradient

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 attributeMeaning
Button ghost, size="icon"The 28 px trigger
group/kebab-hostThe row the trigger waits for with reveal="hover"
data-popup-openOn the trigger while its menu is open; keeps it visible
aria-busyOn the trigger while busy; keeps it visible

Keyboard

KeyAction
Enter SpaceOn the trigger, opens the menu
↑ ↓Moves through the items, disabled ones included
EnterRuns the highlighted item
EscCloses 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.