Context Menu
A shortcut menu on power-user app surfaces (table rows, editors, canvases), opened by right-click, Shift+F10 or the Menu key.
import {
ContextMenu,
ContextMenuItem,
ContextMenuPopup,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@fairgarden-private/design/components/ContextMenu'
The popup is the Menu's build: a portaled white scope that follows the page mode, a --border-size-2 --primary12 edge, 44 px rows and the highlight with its start-edge bar. It anchors to the pointer, or to the focused object when opened from the keyboard. The trigger area has no visual part of its own. Its items are the Menu's parts, re-exported as ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuSubmenu and so on. It is hidden in print.
Every item must also exist in a visible "…" Menu on the same row or selection: the context menu is a shortcut, never the only route. Keep it off editorial and marketing pages.
Rows with a context menu
Rows with a context menu
Each row carries the same actions in its "…" Menu, which is also the route on touch screens.
- Ridge Loop
- Alder Creek
- Meadow Spur
Right-click a row, press Shift+F10, or use its “…” menu.
'use client'
import * as React from 'react'
import {
ContextMenu,
ContextMenuItem,
ContextMenuPopup,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@fairgarden-private/design/components/ContextMenu'
import {
Menu,
MenuItem,
MenuPopup,
MenuSeparator,
MenuTrigger,
} from '@fairgarden-private/design/components/Menu'
import styles from './basic.module.css'
const trails = ['Ridge Loop', 'Alder Creek', 'Meadow Spur'] as const
/** One set of actions, shown in both the context menu and the visible "…" Menu. */
function RowActions({
trail,
onAction,
Item,
Separator,
}: {
trail: string
onAction: (message: string) => void
Item: typeof MenuItem
Separator: typeof MenuSeparator
}) {
return (
<>
<Item icon="zoom_in" onClick={() => onAction(`Opened ${trail}.`)}>
Open trail
</Item>
<Item icon="download" onClick={() => onAction(`Downloaded the ${trail} map.`)}>
Download map
</Item>
<Separator />
<Item destructive onClick={() => onAction(`Removed ${trail} from the list.`)}>
Remove from list
</Item>
</>
)
}
export function ContextMenuBasic() {
const [last, setLast] = React.useState('Right-click a row, press Shift+F10, or use its “…” menu.')
return (
<div className={styles.stack}>
<ul className={styles.list} role="list">
{trails.map((trail) => (
<li key={trail} className={styles.row}>
<ContextMenu>
<ContextMenuTrigger className={styles.area} tabIndex={0}>
<span className={styles.name}>{trail}</span>
</ContextMenuTrigger>
<ContextMenuPopup>
<RowActions
trail={trail}
onAction={setLast}
Item={ContextMenuItem}
Separator={ContextMenuSeparator}
/>
</ContextMenuPopup>
</ContextMenu>
<Menu>
<MenuTrigger iconOnly icon="more_horiz" size="sm">
{`${trail} actions`}
</MenuTrigger>
<MenuPopup align="end">
<RowActions trail={trail} onAction={setLast} Item={MenuItem} Separator={MenuSeparator} />
</MenuPopup>
</Menu>
</li>
))}
</ul>
<p className={styles.status} aria-live="polite">
{last}
</p>
</div>
)
}
API Reference
ContextMenu and ContextMenuTrigger
Groups the parts of a context menu (Base UI ContextMenu.Root). It opens by right-click, Shift+F10 or the Menu key on the trigger area.
The area that opens the menu: a row, an editor or a canvas. It has no visual part of its own; it writes its scope attributes like every root.
ContextMenuPopup
The popup: the Menu’s build (a white scope, --border-size-2 --primary12
edge, --ds-size-hit rows, the highlight with its start-edge bar),
anchored to the pointer, or to the focused object for keyboard
invocation. Compose the Menu item parts inside it, re-exported here as
ContextMenuItem, ContextMenuCheckboxItem and so on.
| Prop | Type | Description |
|---|---|---|
primary | | Primary Radix scale inside the popup’s |
secondary | | Secondary Radix scale inside the popup. It drives nothing but a destructive item’s fallback. |
container | | The element the portal renders into. Default: |
align | | Alignment to the trigger. Default |
alignOffset | | Offset along the alignment axis in px. |
side | | Side of the trigger. Default |
sideOffset | | Distance from the trigger in px. Default 8 ( |
collisionPadding | | Clearance from the viewport edge in px before the popup shifts or flips. Default 16. |
keepMounted | | Keeps the portal mounted while closed. |
Additional types
ContextMenuPopupProps
ContextMenuProps
ContextMenuTriggerProps
Specification: DESIGN-SYSTEM.md §9.7 (menu, and its Context Menu sub-spec).