A menu of actions for a region, opened at the pointer with a right-click, Shift+F10 or a long press.
ContextMenu wraps a region (a row, a card, a file) and opens the shared menu list where the pointer is. The region is focusable, so keyboard users open it with Shift+F10 or the Menu key; on touch a long press (longPressMs, 500ms) opens it.
Items take an icon, a shortcut hint (display only), a danger tone and a disabled state; separators and headings group them. It is the same popover glass and item rhythm as Menu.
inline renders the open panel in the page flow: no portal, scrim, focus trap, scroll lock or outside-click listener. It exists for docs, previews and static mock-ups; never ship it as the live overlay.
Installation
npx shadcn@latest add KezLahd/rime/context-menuAlso installs: base.
Usage
import { ContextMenu } from "@/components/ui";
<ContextMenu label="Actions for Invoice INV-0042" items={[
{ id: "open", label: "Open", onSelect: open },
{ id: "rename", label: "Rename", shortcut: "F2", onSelect: rename },
{ id: "sep", separator: true },
{ id: "delete", label: "Move to bin", tone: "danger", onSelect: remove },
]}>
Invoice INV-0042.pdf
</ContextMenu>Examples
Open
On a region
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
ContextMenu
| Prop | Type | Default | Description |
|---|---|---|---|
| childrenRequired | ReactNode | The region that answers a right-click: a row, a card, a canvas. | |
| itemsRequired | ReadonlyArray<MenuListEntry> | The menu: actions, separators ({ id, separator: true }) and headings ({ id, heading }). | |
| labelRequired | string | The menu's accessible name, e.g. "Actions for Invoice INV-0042". | |
| longPressMs | number | 500 | ms a touch must be held to open it on phones and tablets. |
| inline | boolean | Render the menu open in the page flow, over the region (docs, previews). Nothing is interactive and no listeners are attached. | |
| highlightId | string | Inline only: the item drawn in its hover state. | |
| className | string |
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
| Token | Default | Live value |
|---|---|---|
| --context-region-height | 150px | … |
| --context-region-radius | var(--r-lg) | … |
| --context-region-width | 300px | … |
| --menu-list-min-width | 220px | … |
| --popover-item-active-bg | linear-gradient(to right, rgba(var(--rgb-brand), 0.14), rgba(var(--rgb-brand-deep), 0.07)) | … |
| --popover-item-press-bg | linear-gradient(to right, rgba(var(--rgb-brand), 0.2), rgba(var(--rgb-brand-deep), 0.1)) | … |
| --popover-item-radius | var(--r-xs) | … |
| --popover-pad | calc(6px * var(--density)) | … |
| --popover-radius | var(--r-lg) | … |
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --border-w | tokens.css | … |
| --danger-ink | tokens.css | … |
| --density | tokens.css | … |
| --dur | tokens.css | … |
| --dur-fast | tokens.css | … |
| --dur-slow | tokens.css | … |
| --ease | tokens.css | … |
| --ease-out | tokens.css | … |
| --focus-ring | tokens.css | … |
| --font-body | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-muted | tokens.css | … |
| --line-input | tokens.css | … |
| --line-soft | tokens.css | … |
| --popover-blur | tokens.css | … |
| --popover-border | tokens.css | … |
| --popover-shadow | tokens.css | … |
| --popover-surface | tokens.css | … |
| --r-lg | tokens.css | … |
| --r-xs | tokens.css | … |
| --rgb-brand | tokens.css | … |
| --rgb-brand-deep | tokens.css | … |
| --rgb-danger | tokens.css | … |
| --rgb-white | tokens.css | … |
| --text-caption | tokens.css | … |
| --text-micro | tokens.css | … |
| --text-small | tokens.css | … |
| --tracking-label | tokens.css | … |
| --wash-subtle | tokens.css | … |
| --z-popover | tokens.css | … |
Accessibility
- The menu is role="menu" with role="menuitem" items: arrows, Home, End and typeahead move; Enter or Space runs; Esc closes and returns focus to the region; Tab closes.
- The region is focusable and opens on Shift+F10 and the Menu key, so a mouse is never required.
- Every action in a context menu must also be reachable some other way (a row Menu, a toolbar).
Do and don't
Do
- Put destructive items last, after a separator.
- Keep it to the actions for that one thing.
Don't
- Don't make it the only route to an action.
- Don't override the browser's menu on text people need to copy.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open context-menu.md