Menus
Examples
Section titled “Examples”<details class="menu"> <summary class="menu-trigger">Open menu</summary> <div class="menu-popup" role="menu"> <button class="menu-item" type="button"> <i class="ti ti-pencil" aria-hidden="true"></i> Edit </button> <button class="menu-item" type="button"> <i class="ti ti-copy" aria-hidden="true"></i> Duplicate </button> <hr class="menu-separator" /> <button class="menu-item" type="button"> <i class="ti ti-trash" aria-hidden="true"></i> Delete </button> </div></details><Menu> <Menu.Trigger>Open menu</Menu.Trigger> <Menu.Popup> <Menu.Item icon={IconPencil}>Edit</Menu.Item> <Menu.Item icon={IconCopy}>Duplicate</Menu.Item> <Menu.Separator /> <Menu.Item icon={IconTrash}>Delete</Menu.Item> </Menu.Popup></Menu>Disabled
Section titled “Disabled”<details class="menu"> <summary class="menu-trigger">Actions</summary> <div class="menu-popup" role="menu"> <button class="menu-item" type="button"> <i class="ti ti-pencil" aria-hidden="true"></i> Edit </button> <button class="menu-item" type="button" disabled> <i class="ti ti-copy" aria-hidden="true"></i> Duplicate </button> <a class="menu-item" href="#changelog" aria-disabled="true"> <i class="ti ti-history" aria-hidden="true"></i> Changelog </a> </div></details><Menu> <Menu.Trigger>Actions</Menu.Trigger> <Menu.Popup> <Menu.Item icon={IconPencil}>Edit</Menu.Item> <Menu.Item icon={IconCopy} disabled> Duplicate </Menu.Item> <Menu.Item href="#changelog" icon={IconHistory} aria-disabled="true"> Changelog </Menu.Item> </Menu.Popup></Menu>Checkbox and radio items
Section titled “Checkbox and radio items”<details class="menu"> <summary class="menu-trigger">View</summary> <div class="menu-popup" role="menu"> <button class="menu-item" type="button" role="menuitemcheckbox" aria-checked="true" > <span class="menu-item-indicator" ><i class="ti ti-check" aria-hidden="true"></i ></span> Show grid </button> <button class="menu-item" type="button" role="menuitemcheckbox" aria-checked="false" > <span class="menu-item-indicator" ><i class="ti ti-check" aria-hidden="true"></i ></span> Show ruler </button> <hr class="menu-separator" /> <div class="menu-group" role="group"> <div class="menu-group-label">Density</div> <button class="menu-item" type="button" role="menuitemradio" aria-checked="true" > <span class="menu-item-indicator" ><i class="ti ti-check" aria-hidden="true"></i ></span> Comfortable </button> <button class="menu-item" type="button" role="menuitemradio" aria-checked="false" > <span class="menu-item-indicator" ><i class="ti ti-check" aria-hidden="true"></i ></span> Compact </button> </div> </div></details><Menu> <Menu.Trigger>View</Menu.Trigger> <Menu.Popup> <Menu.Item checked>Show grid</Menu.Item> <Menu.Item checked={false}>Show ruler</Menu.Item> <Menu.Separator /> <Menu.Group> <Menu.GroupLabel>Density</Menu.GroupLabel> <Menu.Item role="menuitemradio" checked> Comfortable </Menu.Item> <Menu.Item role="menuitemradio" checked={false}> Compact </Menu.Item> </Menu.Group> </Menu.Popup></Menu>Hotkey React only
Section titled “Hotkey ”<Menu> <Menu.Trigger>Actions</Menu.Trigger> <Menu.Popup> <Menu.Item icon={IconPlus} hotkey="mod+n"> New </Menu.Item> <Menu.Item icon={IconDeviceFloppy} hotkey="mod+s"> Save </Menu.Item> <Menu.Separator /> <Menu.Item icon={IconSearch} hotkey="/"> Search </Menu.Item> </Menu.Popup></Menu>Grouped items
Section titled “Grouped items”<details class="menu"> <summary class="menu-trigger">Resources</summary> <div class="menu-popup" role="menu"> <div class="menu-group"> <div class="menu-group-label">Internal</div> <a class="menu-item" href="#docs"> <i class="ti ti-book" aria-hidden="true"></i> Docs </a> <a class="menu-item" href="#changelog"> <i class="ti ti-history" aria-hidden="true"></i> Changelog </a> </div> <hr class="menu-separator" /> <div class="menu-group"> <div class="menu-group-label">External</div> <a class="menu-item" href="#support"> <i class="ti ti-lifebuoy" aria-hidden="true"></i> Support </a> </div> </div></details><Menu> <Menu.Trigger>Resources</Menu.Trigger> <Menu.Popup> <Menu.Group> <Menu.GroupLabel>Internal</Menu.GroupLabel> <Menu.Item href="#docs" icon={IconBook}> Docs </Menu.Item> <Menu.Item href="#changelog" icon={IconHistory}> Changelog </Menu.Item> </Menu.Group> <Menu.Separator /> <Menu.Group> <Menu.GroupLabel>External</Menu.GroupLabel> <Menu.Item href="#support" icon={IconLifebuoy}> Support </Menu.Item> </Menu.Group> </Menu.Popup></Menu>Button-styled trigger
Section titled “Button-styled trigger”<details class="menu"> <summary class="menu-trigger btn btn-primary">Actions</summary> <div class="menu-popup" role="menu"> <button class="menu-item" type="button">Approve</button> <button class="menu-item" type="button">Reject</button> <hr class="menu-separator" /> <button class="menu-item" type="button">Send to review</button> </div></details><Menu> <Menu.Trigger className="btn btn-primary">Actions</Menu.Trigger> <Menu.Popup> <Menu.Item>Approve</Menu.Item> <Menu.Item>Reject</Menu.Item> <Menu.Separator /> <Menu.Item>Send to review</Menu.Item> </Menu.Popup></Menu>Split button
Section titled “Split button”<div class="btn-group"> <button class="btn btn-primary" type="button">Save</button> <details class="menu"> <summary class="menu-trigger btn btn-primary btn-square" aria-label="More save options" ></summary> <div class="menu-popup" role="menu"> <button class="menu-item" type="button">Save as draft</button> <button class="menu-item" type="button">Save and publish</button> <hr class="menu-separator" /> <button class="menu-item" type="button">Save a copy…</button> </div> </details></div><ButtonGroup> <Button variant="primary">Save</Button> <Menu> <Menu.Trigger className="btn btn-primary btn-square" aria-label="More save options" /> <Menu.Popup> <Menu.Item>Save as draft</Menu.Item> <Menu.Item>Save and publish</Menu.Item> <Menu.Separator /> <Menu.Item>Save a copy…</Menu.Item> </Menu.Popup> </Menu></ButtonGroup>A <details> menu has no light dismiss: clicking outside leaves it open. Closing means clicking the trigger again or moving focus away.
Reference
Section titled “Reference”| Part | Renders | Class |
|---|---|---|
Menu |
<details> |
menu |
Menu.Trigger |
<summary> |
menu-trigger |
Menu.Popup |
<div> |
menu-popup |
Menu.Item |
<button>, or <a> with an href |
menu-item |
Menu.Separator |
<hr> |
menu-separator |
Menu.Group |
<div> |
menu-group |
Menu.GroupLabel |
<div> |
menu-group-label |
| Part | Prop | Type | Default |
|---|---|---|---|
Menu.Item |
icon |
IconProp |
— |
Menu.Item |
hotkey |
string | readonly string[] |
— |
Menu.Item |
checked |
boolean |
— |
Open state is the <details> element’s, so there is no open prop to control and no JavaScript involved — pass the native open attribute for a menu that starts expanded. Each part takes the native attributes of its element.
href on an item renders a real <a>, so right-click “open in new tab” and copy-URL work; without it the item is a <button>. Disable a button item with the native disabled attribute and an anchor item with aria-disabled="true", since an <a> has no disabled — both dim, both stop activating, hotkeys included.
checked turns an item into a checkable one and renders the leading check; checked={false} still reserves the gutter, so labels stay aligned down the group. Set role="menuitemradio" for single-select. You hold the state itself.
hotkey dispatches a click on the item and right-pins a Kbd chip, on both the button and anchor branches — see Conventions › Hotkeys.
Vanilla
Section titled “Vanilla”| Class | Effect |
|---|---|
menu |
Positioning context on a <details> |
menu-trigger |
<summary> behaviour: pointer cursor, no marker, trailing chevron that rotates when open |
menu-popup |
Popup panel: 11rem min-width, 18rem max-height, scrolls, bordered surface with a shadow |
menu-item |
Row: full width, 0.75rem/0.375rem padding, text-sm, hover tint, 0.5rem gap for an icon |
menu-item-indicator |
1rem check gutter, always reserved; its glyph shows only when aria-checked="true" |
menu-separator |
1px divider with 0.25rem of margin |
menu-group |
Column wrapper for a labelled set |
menu-group-label |
Group heading: text-xs uppercase, muted |
Open and close is the browser’s, via <details>/<summary> — no JavaScript in either bundle. Write role="menu" on the popup and, for checkable items, role="menuitemcheckbox" or "menuitemradio" plus aria-checked yourself.
menu-trigger only styles behaviour and bows out of appearance when a btn class is present, so adding btn btn-primary to the <summary> gives a button-styled trigger with no override needed. An empty trigger centres its chevron, as the split-button pattern does — pair it with an aria-label, since the chevron is the only visible content. A menu inside a btn-group joins the strip’s rounding and seams.
Where anchor positioning is available the popup is position: fixed and anchored to the trigger, so it escapes an ancestor’s overflow: hidden — inside a <dialog>, for instance — and flips above the trigger when there’s no room below. Without it, the popup falls back to absolute positioning and clips as usual.