Dropdown Menu
A menu of actions and toggles launched from a button.
@garn/ui/dropdown-menuDefault
Account menu with a label, items, shortcuts, and a separator.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add dropdown-menu- Registry
dropdown-menu- Source
- dropdown-menu.tsx
- Deps
@radix-ui/react-dropdown-menulucide-react- Registry deps
utils
Import
import { DropdownMenu } from "@garn/ui/dropdown-menu";Props
Generated from the component's TypeScript types.
No component-specific props — this is a thin wrapper over its native element.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="dropdown-menu-content"]| Property | Token | Tier |
|---|---|---|
itemMinHeight | --garn-control-h-md | semantic · air |
Overview
DropdownMenu offers a list of commands or toggles from a button — row/overflow (kebab) actions, an account menu, view options. It's a rich compound: action DropdownMenuItems, toggleable CheckboxItems, single-select RadioGroup/RadioItems, Label/Separator for structure, Shortcut hints, and nested Sub menus. It's a menu of actions, not a form control — to pick a value that stays on a field use Select, to right-click an element use ContextMenu, and to search-and-run use Command.
Guidelines
Menus are for actions, not data entry. Use it to do things or flip view toggles; if the user is choosing a form value, that's a Select. Order items by likelihood, group related ones with a Separator, and keep destructive actions visually distinct and away from the default-focus path.
Label icon-only triggers and keep the list shallow. The trigger needs an accessible name (a kebab button is meaningless to screen readers otherwise). Avoid deep Sub nesting — one level is usually plenty; more becomes a maze on touch.
Shortcut is a hint, not a binding. It displays the key combo but doesn't register it — wire the actual keyboard handler yourself. Selecting an item closes the menu; for toggles that should persist the menu open, use CheckboxItem/RadioItem.
Best practices
- Give the trigger an accessible name (aria-label if it's icon-only).
- Group with DropdownMenuLabel + DropdownMenuSeparator; keep destructive items separated.
- Control CheckboxItem (
checked+onCheckedChange) and RadioGroup (value+onValueChange) state.
- Don't use a dropdown-menu as a form select — use select.
- Don't rely on DropdownMenuShortcut to actually bind a key — it's display only; wire the handler yourself.
- Don't nest deep submenu trees — flatten or split the menu.
Content guidelines
- Phrase items as actions (verb-first) with short, parallel labels.
- Group related items, separate destructive ones, and show shortcuts as hints — not instructions.
Accessibility
- Role
menu- ARIA APG
- menu button
- Focus
- Roving focus across items; focus returns to the trigger on close.
Enforceable contracts
Give an icon-only menu trigger an accessible name (aria-label).
when the DropdownMenuTrigger is an icon-only control with no aria-label / aria-labelledby / text
Keyboard
| EnterSpaceArrowDownArrowUp | Open the menu from the trigger. |
| ArrowUpArrowDown | Move between items. |
| ArrowRightArrowLeft | Open / close a submenu. |
| HomeEnd | Jump to the first / last item. |
| A–Z | Type-ahead to a matching item. |
| EnterSpace | Activate the highlighted item. |
| Escape | Close the menu and return focus to the trigger. |
- • Trigger is a menu button (aria-haspopup=menu, aria-expanded); Content is role=menu with roving focus.
- • Items are menuitem / menuitemcheckbox / menuitemradio; checked items expose aria-checked.
- • An icon-only trigger relies on the menu for nothing — give the trigger its own accessible name.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.1.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.