Skip to content

Dropdown menu · Menu item · Context menu

Dropdown menu — a list of actions that opens from a button, a pill or a right-click.

Beta · Navigation and overlay · packages/css/src/components/dropdown-menu.css · React on Base UI: @pepperui/dropdown-menu

.pui-dropdown-menu (__header __name __role __group-label __separator) · .pui-menu-item (--submenu --check --destructive; __icon __label __shortcut __chevron __check; data-highlighted, aria-disabled, aria-checked).

Dropdown menu · Menu item · Context menu — every size and every stateOpen in the workshop

Sizes across, states down.

  • Has header + Name / Role (the user menu’s head); Has group label + Group label (a heading over a group of rows); Has separator; Has item 4 / 5 / 6
  • Items 1–6 are exposed Menu items — label, icon, shortcut, Type (submenu · check · destructive) and State on each
  • 220 wide; the height follows the rows; a submenu is a second Dropdown menu to the right, top-aligned to its row

Use when three or more actions hang off one control — a row’s ⋯, a File or Export button, the user pill, a right-click on a table row (Context menu: the same menu at the pointer).

Do not use when the list picks a value (Select, Combobox); one or two actions (Buttons, an Icon button); the content is a form (Popover); it is site navigation (Navigation menu).

Relates to Menu item, Context menu, Select menu, Popover, Kbd, Icon button - ghost, Topbar, Data table.

Anti-patterns more than one destructive row · a menu without a trigger · a two-level submenu · a menu that scrolls (split it — a notification list is the exception and may scroll) · a fill changed on the instance.

Keywords: dropdown menu, menu, actions, context menu, user menu, file menu, submenu

Figma — page Dropdown menu: Dropdown menu (1 variants) · Menu item (12 variants)

shadcn: dropdown-menu

Dropdown menu — a list of actions that opens from a button, a pill or a right-click.

  • Has header + Name / Role (the user menu’s head); Has group label + Group label (a heading over a group of rows); Has separator; Has item 4 / 5 / 6
  • Items 1–6 are exposed Menu items — label, icon, shortcut, Type (submenu · check · destructive) and State on each
  • 220 wide; the height follows the rows; a submenu is a second Dropdown menu to the right, top-aligned to its row

Use when three or more actions hang off one control — a row’s ⋯, a File or Export button, the user pill, a right-click on a table row (Context menu: the same menu at the pointer).

Do not use when the list picks a value (Select, Combobox); one or two actions (Buttons, an Icon button); the content is a form (Popover); it is site navigation (Navigation menu).

Relates to Menu item, Context menu, Select menu, Popover, Kbd, Icon button - ghost, Topbar, Data table.

Anti-patterns more than one destructive row · a menu without a trigger · a two-level submenu · a menu that scrolls (split it — a notification list is the exception and may scroll) · a fill changed on the instance.

Keywords: dropdown menu, menu, actions, context menu, user menu, file menu, submenu

the same Figma parts as dropdown-menu, opened at the pointer; Base UI ContextMenu on the code side

Figma — page Dropdown menu: Dropdown menu (1 variants) · Menu item (12 variants)

shadcn: context-menu

The class, the parts, and nothing that is not in the package. The same block opens dropdown-menu.css.

<div class="pui-dropdown-menu" role="menu">
<div class="pui-dropdown-menu__header"><p class="pui-dropdown-menu__name">the design lead</p><p class="pui-dropdown-menu__role">Owner</p></div>
<div class="pui-dropdown-menu__group-label">Deck</div>
<button class="pui-menu-item" role="menuitem"><span class="pui-icon pui-menu-item__icon">…</span>Duplicate
<kbd class="pui-kbd pui-menu-item__shortcut">⌘D</kbd></button>
<button class="pui-menu-item pui-menu-item--submenu" role="menuitem">Export<span class="pui-icon pui-menu-item__chevron">…</span></button>
<div class="pui-dropdown-menu__separator" role="separator"></div>
<button class="pui-menu-item pui-menu-item--destructive" role="menuitem">Delete</button></div>
What it is Note
Item Type (none) default · --submenu · --check · --destructive —
Item State :hover, :focus, [data-highlighted] share the surface, and KEYBOARD focus adds the inset ring on top of it — the shared surface alone is 1.12 against the menu and WCAG 2.4.13 asks 3:1 · [aria-disabled=“true”] —
Parts __icon · __label · __shortcut · __chevron · __check (shown on [aria-checked=“true”]) —

Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.

ink on ground reads floor the matrix calls it
#e42521 on #ffffff 4.57 4.5 text.error on surface.page (promised) ok
#544d4d on #f2f2f2 7.36 4.5 text.muted on surface.sunken (promised) ok
#544d4d on #ffffff 8.24 4.5 text.muted on surface.page (promised) ok
#0b0202 on #f2f2f2 18.32 4.5 text.default on surface.sunken (promised) ok
#0b0202 on #ffffff 20.51 4.5 text.default on surface.page (promised) ok

5 distinct pair(s) rendered, 0 that axe calls a violation.

primitive on the indicator over the change
Menu item surface/page shadow-inset #6a66e4 2 px #ffffff 4.53 ok

8 of 8 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.

role tab stops what the pattern requires
menu .pui-dropdown-menu 0 0 — its trigger holds it ok
menu .pui-dropdown-menu 0 0 — its trigger holds it ok

What the consumer’s script owes. NO tab stop at rest — the trigger holds it. Every item takes tabindex=“-1” and the script moves focus to the first on open. It owes Up / Down, Home / End, Enter / Space, Escape (which closes and returns focus to the trigger), Right / Left for a submenu, and typeahead by first letter.

Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).

Pepper UI v1.5.0

The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.