Components / Overlays
Menu
A button that opens a list of commands: edit, copy, export, delete. Choosing one runs it and closes the menu. For picking a value that stays shown on the control, use Select.
When to useLink to "When to use"
- Overflow actions for an item (a row's "more" button) or a toolbar.
- Several related commands that don't each deserve a visible button.
- View options with checkable items (
kind: "checkbox"toggles,kind: "radio"picks one of a set).
Use something else when:
- the user picks a value for a form: Select
- there are only one or two actions: show Buttons
- the panel holds controls or a form: Popover
- the items navigate to pages: links in your navigation
UsageLink to "Usage"
import { IconButton, Menu } from "@arshad-shah/cynosure";
import { copy, edit, moreHorizontal, trash } from "@arshad-shah/cynosure/icons";
<Menu
label="File actions"
trigger={(props) => <IconButton {...props} icon={moreHorizontal} label="More actions" />}
items={[
{ value: "edit", label: "Edit", icon: edit, shortcut: ["Ctrl", "E"] },
{ value: "copy", label: "Copy path", icon: copy },
{ label: "Danger zone", items: [{ value: "delete", label: "Delete", icon: trash, tone: "danger" }] },
]}
onSelect={(value) => run(value)}
/>trigger is a render prop: spread its props onto a Button or IconButton. Sections ({ label, items }) render as a named group after a separator.
Sections without a labelLink to "Sections without a label"
A section with no label ({ items: [...] }) is set apart by separator lines before and after it, without a group name: undo/redo, then cut/copy/paste.
SubmenusLink to "Submenus"
An item with items opens a submenu beside it instead of being chosen, nested to any depth. The entries are plain data, so a plugin can contribute them. onSelect receives the value chosen at any level, and choosing closes the whole menu.
items={[
{ value: "open", label: "Open" },
{
value: "move",
label: "Move to",
icon: folder,
items: [
{ value: "inbox", label: "Inbox" },
{ value: "projects", label: "Projects", items: [{ value: "alpha", label: "Alpha" }] },
],
},
]}The submenu opens on the end side of its parent item, its first item level with the parent, and flips to the other side at the edge of the viewport.
Opening from elsewhereLink to "Opening from elsewhere"
open with onOpenChange controls the menu, so an app can open it from a keyboard shortcut; it opens on its first item. defaultOpen opens it on first render without controlling it.
Checkable itemsLink to "Checkable items"
Checkable items keep the menu open, so several options can be changed in one go. Keep checked in your state and update it in onSelect.
Coloured iconsLink to "Coloured icons"
Item icons are muted by default. iconTone and iconColor colour one item's icon and mean what Icon's tone and color mean: iconTone is a semantic colour (accent, neutral, danger, success, warning, info, muted), iconColor is any CSS colour and overrides it (brand draws a brand mark in its owner's colour). The highlighted item keeps the colour and a disabled item shows its icon muted.
PropsLink to "Props"
| Prop | What it does | Type | Default |
|---|---|---|---|
triggerRequired | Render the trigger: (props) => <Button {...props}>Actions</Button>. | (props: MenuTriggerProps) => ReactNode | |
labelRequired | Accessible name of the menu. | string | |
itemsRequired | The commands: items (an item with items opens a submenu), or sections of items, labelled or not. | MenuEntry[] | |
onSelectRequired | Called with the chosen item's value, from any level of submenu. | (value: string) => void | |
open | Whether the menu is open, to open it from elsewhere (a keyboard shortcut). Pair with onOpenChange. | boolean | |
defaultOpen | Open on first render, when open is not controlled. Default false. | boolean | false |
onOpenChange | Called when the menu opens or closes: the trigger, Escape, a choice, Tab or a click outside. | ((open: boolean) => void) | |
placement | Side of the trigger the menu opens on. Default "bottom". | Placement | "bottom" |
className | Added to the menu panel, for layout. | string | |
style | Layout only, applied to the panel. | CSSProperties |
Generated from the type definitions in @arshad-shah/cynosure 0.5.0.
Item fields: value, label, icon, iconTone, iconColor, shortcut (keys such as ["Ctrl", "E"]), disabled, tone (neutral or danger), kind (checkbox or radio), checked, keepOpen and items (a submenu). Section fields: items and an optional label.
Behaviour and accessibilityLink to "Behaviour and accessibility"
| Input | Result |
|---|---|
| Enter, Space, click or Down arrow arrow on the trigger | Opens on the first item |
| Up arrow arrow on the trigger | Opens on the last item |
| Down arrow / Up arrow arrows, Home, End | Move between items, wrapping and skipping disabled ones |
| A letter | Jumps to the next item starting with it |
| Enter, Space, click | Runs the item and closes the whole menu (checkable items stay open) |
| Right arrow arrow (Left arrow in right-to-left text), Enter, Space or click on a parent item | Opens its submenu on the first item |
| Left arrow arrow (Right arrow in right-to-left text) in a submenu | Closes it, back to its parent item |
| Resting the pointer on a parent item | Opens its submenu after 150 ms, without moving focus |
| Escape | Closes one level: a submenu back to its parent item, the menu back to the trigger |
| Tab | Closes the whole menu and moves on to the next control |
- Roles:
menunamed bylabel, items asmenuitem,menuitemcheckboxormenuitemradiowitharia-checked, sections as namedgroups, unlabelled sections set apart byseparators. A parent item hasaria-haspopup="menu",aria-expandedandaria-controlsnaming its submenu, amenunamed by the item's label. - Diagonal travel: while a submenu is open, another item must hold the pointer for 300 ms before the submenu closes or switches, so moving towards the submenu across other items keeps it open. Leaving the menu cancels a pending switch.
- Shortcuts are shown with Kbd and announced through
aria-keyshortcuts. The menu shows them; your app still has to handle the keys. - Disabled items stay visible (so people learn the command exists) but can't be focused or run.
- Layers: inside a Popover or dialog, Escape closes only the menu.
TouchLink to "Touch"
Rows are at least 44px tall on coarse pointers, and a tap runs the item. Touch has no hover: a tap on a parent item opens its submenu.
ThemingLink to "Theming"
Tokens: --cy-menu-min-width, --cy-menu-max-width, --cy-menu-radius, --cy-menu-row-height and --cy-menu-offset (the gap from the trigger, also under a Menubar title).
createTheme({ components: { menu: { minWidth: "240px", radius: "10px" } } });RelatedLink to "Related"
These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.