Components · Actions
Context menu
BetaA right-click menu for power users, such as in the program builder.
- Source
components/ui/context-menu.tsx - shadcn/ui Context menu
- Figma: not linked yet (Q13)
When to use
- As a shortcut for people who work in a dense area all day: right-click a session in the program builder, a cell in the meal-plan grid, a row in a long list.
- Only for actions that are also available somewhere visible: a Dropdown menu on the same item, or buttons.
When not to use
- As the only way to reach an action. Many people never right-click, touch users have to long-press, and screen reader users won't find it. Put the same actions in a "More" dropdown menu.
- On ordinary links, text and form fields, where people expect the browser's own menu (copy, open in new tab, spell-check).
- For one or two actions: show them as buttons.
Anatomy
- 1. Trigger area: the item it acts on; opens the menu at the pointer
- 2. Label (optional): names the item
- 3. Item: optional 16px icon, then a verb
- 4. Destructive item: last, after a separator
Right-click or long-press the area in the example to see parts 2 to 4.
Variants
A context menu has the same item types as the Dropdown menu: actions, variant="destructive", checkbox items, a radio group, one level of submenu, labels, separators and shortcuts. The example shows view options for the program calendar. Only show a shortcut if it really works in that view.
States
| State | What changes | Token |
|---|---|---|
| Open | Menu panel at the pointer, on the dropdown layer | bg-popover, shadow-md, z-(--z-dropdown) |
| Highlighted item | Accent fill and text | bg-accent, text-accent-foreground |
| Open submenu trigger | Stays highlighted while its submenu is open | data-[state=open]:bg-accent |
| Checked | Check mark (checkbox) or dot (radio) at the start | ItemIndicator |
| Disabled item | Half opacity, skipped by arrow keys. "Paste" in the first example is disabled because nothing has been copied | opacity-(--opacity-disabled) |
| Destructive item | Red text and icon; a red tint when highlighted | text-destructive-text, bg-destructive/10 |
As in the dropdown menu, destructive items use text-destructive-text, which stays above 4.5:1 on their 10% tint when highlighted. Upstream's text-destructive dropped to about 4.1:1 there.
The trigger area has no style of its own. Give it a visible focus ring if it is focusable, as the examples do with ring-ring/50.
Usage guidelines
- Keep the items and their order the same as the item's dropdown menu, so both are learned once.
- Put the most frequent actions first and destructive actions last, after a separator.
- Don't replace the browser's menu on text people may want to copy.
Content guidelines
- Same rules as the Dropdown menu: verb first, sentence case, "…" when a dialog follows.
- Keep labels short; the menu opens at the pointer and covers the content around it.
- Arabic: "تعديل الحصة"، "تكرار إلى"، "حذف الحصة".
Accessibility
| Key | Does |
|---|---|
| Shift+F10 or the Menu key | Opens the menu when the trigger area has focus (the browser fires the contextmenu event) |
| Arrow up / down | Moves between items, skipping disabled ones |
| Arrow right (left in RTL), Enter or Space | On a submenu trigger: opens the submenu |
| Arrow left (right in RTL), Escape in a submenu | Closes the submenu |
| Enter or Space | Chooses the item. Checkbox and radio items toggle |
| Escape | Closes the menu |
- Long-press opens it on touch screens (after 700ms); the trigger blocks the browser's own long-press menu.
- The trigger area is a plain
<span>and isn't focusable by default. AddtabIndex={0}and a focus style, or wrap an element that already takes focus, so Shift+F10 works. - The panel is a
menuwithmenuitem,menuitemcheckboxandmenuitemradioitems. A screen reader announces "menu" and the first item when it opens, but nothing announces that a context menu exists. That is why the actions must also be reachable another way.
RTL
- The menu opens at the pointer and is kept inside the viewport, so placement doesn't depend on direction.
- Icons and check marks sit at the start of each item and move to the right. Shortcuts sit at the end. Submenus open toward the left.
- Arrow keys follow the direction because the site provides Radix's
DirectionProvider. - The submenu chevron mirrors, so it points left in RTL, toward where the submenu opens.
Tokens used
Read from components/ui/context-menu.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --accent | bg-accent | brand-50 | orange-50 |
| --accent-foreground | text-accent-foreground | brand-700 | orange-700 |
| --border | bg-border | neutral-200 | neutral-200 |
| --destructive | bg-destructive/10 text-destructive | red-600 | red-600 |
| --destructive-text | text-destructive-text | red-700 | red-700 |
| --foreground | text-foreground | neutral-950 | neutral-950 |
| --muted-foreground | text-muted-foreground | neutral-600 | neutral-600 |
| --popover | bg-popover | white | white |
| --popover-foreground | text-popover-foreground | neutral-950 | neutral-950 |
- Layer
z-(--z-dropdown) - Opacity
opacity-(--opacity-disabled) - Radius
rounded-md - Radius
rounded-sm - Shadow
shadow-lg - Shadow
shadow-md - Type
font-medium - Type
text-sm - Type
tracking-wide - Type
text-xs
Props
<ContextMenu>
Also takes every prop of Radix ContextMenu.Root
No props of its own.
<ContextMenuTrigger>
Also takes every prop of Radix ContextMenu.Trigger
No props of its own.
<ContextMenuSubTrigger>
Also takes every prop of Radix ContextMenu.SubTrigger
| Prop | Type | Default |
|---|---|---|
| inset? | boolean | — |
<ContextMenuContent>
Also takes every prop of Radix ContextMenu.Content
No props of its own.
<ContextMenuItem>
Also takes every prop of Radix ContextMenu.Item
| Prop | Type | Default |
|---|---|---|
| inset? | boolean | — |
| variant? | "default" | "destructive" | "default" |
<ContextMenuCheckboxItem>
Also takes every prop of Radix ContextMenu.CheckboxItem
No props of its own.
<ContextMenuRadioItem>
Also takes every prop of Radix ContextMenu.RadioItem
No props of its own.
Replaces in coach-admin
From the read-only audit of coach-admin: 5 style values, 5 uses.
The only context menu in coach-admin is ReactGrid's, restyled in src/reusableComponents/sheet/ReactGridCustom.scss for the spreadsheet view. It is drawn by the grid library, so it changes only if the grid is replaced; these classes show which values it should match.
Classes and selectors
.rg-context-menu3.rg-context-menu-option2
SCSS variables in these styles
$shadow-colornot a color: spacing, size or layout$opacity-20not a color: spacing, size or layout$dropdown-menu-bg-colornot a color: spacing, size or layout$context-menu-font-sizenot a color: spacing, size or layout$context-menu-option-paddingnot a color: spacing, size or layout$context-menu-shadownot a color: spacing, size or layout