Dropdown menu
A list of words on the popover panel; the one you point at is marked with the highlighter.
Report an issue with Dropdown menuExample
List, pointed at
Leaders, pointed at
Marginalia, pointed at
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base, kbd, and popover along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/dropdown-menu.jsonUsage
This example also uses Button.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/ button.json"use client"import * as React from "react"import { Button } from "@/components/ui/button"import {DropdownMenu,DropdownMenuCheckboxItem,DropdownMenuContent,DropdownMenuGroup,DropdownMenuItem,DropdownMenuLabel,DropdownMenuRadioGroup,DropdownMenuRadioItem,DropdownMenuSeparator,DropdownMenuShortcut,DropdownMenuSub,DropdownMenuSubContent,DropdownMenuSubTrigger,DropdownMenuTrigger,} from "@/components/ui/dropdown-menu"function Options() {const [notes, setNotes] = React.useState(true)const [view, setView] = React.useState("list")return (<DropdownMenu><DropdownMenuTrigger asChild><Button variant="bracket">Options</Button></DropdownMenuTrigger><DropdownMenuContent aria-label="Options for Halden"><DropdownMenuGroup><DropdownMenuItem>Rename</DropdownMenuItem><DropdownMenuItem>Duplicate <DropdownMenuShortcut>⌘D</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuItem>Archive</DropdownMenuItem></DropdownMenuGroup><DropdownMenuSeparator /><DropdownMenuLabel>Show</DropdownMenuLabel><DropdownMenuCheckboxItem checked={notes} onCheckedChange={setNotes}>Show margin notes <DropdownMenuShortcut>N</DropdownMenuShortcut></DropdownMenuCheckboxItem><DropdownMenuRadioGroup value={view} onValueChange={setView}><DropdownMenuRadioItem value="list">As a list</DropdownMenuRadioItem><DropdownMenuRadioItem value="grid">As a grid</DropdownMenuRadioItem></DropdownMenuRadioGroup><DropdownMenuSeparator /><DropdownMenuSub><DropdownMenuSubTrigger>Move to</DropdownMenuSubTrigger><DropdownMenuSubContent><DropdownMenuItem>Northlight</DropdownMenuItem><DropdownMenuItem>Oda Studio</DropdownMenuItem></DropdownMenuSubContent></DropdownMenuSub><DropdownMenuItem variant="destructive">Delete Halden for good</DropdownMenuItem></DropdownMenuContent></DropdownMenu>)}/** A contents page: every word joined to its keys by leader dots. */function Arrange() {return (<DropdownMenu><DropdownMenuTrigger asChild><Button variant="bracket">Arrange</Button></DropdownMenuTrigger><DropdownMenuContent variant="leaders" aria-label="Arrange"><DropdownMenuItem>Bring forward <DropdownMenuShortcut>⌘]</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuItem>Send backward <DropdownMenuShortcut>⌘[</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuSeparator /><DropdownMenuItem>Group <DropdownMenuShortcut>⌘G</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuItem>Ungroup <DropdownMenuShortcut>⇧⌘G</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuSub><DropdownMenuSubTrigger>Align</DropdownMenuSubTrigger><DropdownMenuSubContent><DropdownMenuItem>To the left <DropdownMenuShortcut>⌥A</DropdownMenuShortcut></DropdownMenuItem><DropdownMenuItem>To the centre <DropdownMenuShortcut>⌥H</DropdownMenuShortcut></DropdownMenuItem></DropdownMenuSubContent></DropdownMenuSub><DropdownMenuItem>Lock in place</DropdownMenuItem></DropdownMenuContent></DropdownMenu>)}/** Each word explained by a note hung beside the list. */function Share() {return (<DropdownMenu><DropdownMenuTrigger asChild><Button variant="bracket">Share</Button></DropdownMenuTrigger><DropdownMenuContent variant="marginalia" aria-label="Share Halden"><DropdownMenuItem hint="Anyone with the link can read Halden, not change it.">Copy the link</DropdownMenuItem><DropdownMenuItem hint="They get a note from you and can change anything.">Invite to edit</DropdownMenuItem><DropdownMenuItem hint="A public page at studio.com/halden, updated as you work.">Publish to the web</DropdownMenuItem><DropdownMenuSeparator /><DropdownMenuItem hint="Every link stops working at once. Invited people keep access.">Stop sharing</DropdownMenuItem></DropdownMenuContent></DropdownMenu>)}export default function Example() {return (<div className="flex w-full flex-wrap items-start gap-x-16 gap-y-10"><div className="grid justify-items-start gap-3"><span className="ot-label">List</span><Options /></div><div className="grid justify-items-start gap-3"><span className="ot-label">Leaders</span><Arrange /></div><div className="grid justify-items-start gap-3"><span className="ot-label">Marginalia</span><Share /></div></div>)}/** A list, pinned open with one item pointed at. The docs' states row is inert, so this is the look alone. */function Pinned({ variant, children, note }: { variant: string; children: React.ReactNode; note?: React.ReactNode }) {return (<div className="ot-pop ot-menu" data-variant={variant} data-state="open" style={{ position: "relative" }}>{children}{note}</div>)}const narrow = "(max-width: 40rem)"const onResize = (change: () => void) => (addEventListener("resize", change), () => removeEventListener("resize", change))Contract
- Underneath: Radix DropdownMenu, ContextMenu and Menubar.
- Anatomy:
<div class="ot-pop ot-menu" popover role="menu">holding.ot-menu-labelheadings and<button class="ot-menu-item" role="menuitem">(ormenuitemcheckboxwitharia-checked), each with optional.ot-menu-keys.data-variant="danger"for a destructive item. A menubar is<div class="ot-menubar" role="menubar">of.ot-menubar-triggerbuttons on a hairline; each menu is aot-menuhung in that hairline. A context menu is anot-menuwithdata-at="point", placed where the person pressed. - The item you point at is marked with the highlighter. Items arrive in turn. A menubar opens its hairline into a pocket under the open word: the menu rises over the rule, its paper hides the rule there, and its sides and foot carry the same line on, down, along and back up. The word and its menu are one shape, with no leader and no stroke. The menu unrolls down out of the line and rolls back up into it. Its words stand under the word, and the bar's words sit in from the rule's ends by a menu's own inset, so the first menu folds from the rule too. With no room below, it turns above the words in a whole frame. Focus on a word is the accent stroke laid on the rule under it. A context menu spreads from the point like ink (
ot-spread); it opens toward the line's end and down from the point, back from it without room, and on a phone slides in to fit. A submenu opens beside its item; on a narrow screen, with room on neither side, it drops open under the item, in by the words' inset, the next level of an outline, and hangs from the item on the popover's leader (data-drop): the dot on the marked item's foot, the line dropping over a strip of paper. The item's arrow turns down while it is open. Shortcut keys (dir="ltr") read ⌘Z in either direction. Without adir, each menu takes the direction of the page around its trigger or bar, so on a right-to-left page it hangs from the right end, submenus open to the left, and the arrow keys mirror. varianton the list (DropdownMenuContent,ContextMenuContent;data-variant):list(the default) orleaders, and alsomarginaliaon a dropdown menu ororbiton a context menu. On theMenubarroot:pocket(the default),leadersorcaption. Under every variant a context menu's point is pinned by a cross, the register mark of "the uncreative", over the menu's corner, and the menu spreads from it.- leaders: a book's contents page. Each word is joined to its keys (or a submenu's arrow) by a row of leader dots in
rule-strong; a word with neither has none, and the words don't wrap. The highlighter stands down: the word you point at inks, and its dots ink from the word to its keys, passing through as the highlighter does. Submenus keep the leaders. A menubar set toleaderssets every menu so, in its pocket. - marginalia (dropdown menu): the small notes on hairline arrows of "It has to be design.". Each item's
hintis a note (pp, graphite, at most 13em) hung beside the list at the height of the word you point at, on a hairline arrow pointing back at it; it glides from word to word, and a word without a hint leaves the margin empty. The list keeps its highlighter. With no room beside the list (under 220px) the note stands under it as its caption, without the arrow. The note is aria-hidden: each item carries its hint as itsaria-description. - orbit (context menu): after WOVE. The words stand on an arc bowed away from the point, one dot on the arc for each; the one you point at inks and its dot lands as the accent. The arc is the panel's edge: the paper lies only outside it, and the panel's hairline and shadow stand down. Every row is one step high, and the words swing out to the arc as they arrive. Put items straight in the list (a group counts as one step); a separator is a step of silence with a short rule in it. In forced colours the arc gives way to a framed panel and the dots stay.
- caption (menubar): the label at the far end of the SHAPES / GRADIENTS frame, and an editor's status line. The bar's far end keeps a small line of where you are, the path from the open word to the item you point at, parted by slashes (File / Export / As PDF); its last step inks and arrives as you move. At rest it shows the bar's
aria-labelin pencil. Below 40rem only the last step shows. It is aria-hidden, since the menus already name the path. - Forced colours: the highlighter gives way to the system's
Highlight; leaders' dots and the marginalia arrow areCanvasText; orbit's pointed dot isHighlight. Reduced motion: the dots, the note and the caption move straight to their places. - Keyboard: Down and Up move, Home and End go to the ends, Enter runs, Escape closes, Tab closes and moves on. Right opens a submenu and Left closes it (mirrored right to left). In a menubar, Down opens, and Left and Right move between menus, open or closed. The context menu opens with the ContextMenu key or Shift+F10.
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
DropdownMenuContent side / align / sideOffset | Radix | "bottom" / "start" / 27 | Where the list hangs. It wears the popover's panel (ot-pop), so it shares the leader line and the in and out. |
DropdownMenuContent variant | "list" | "leaders" | "marginalia" | "list" | How the list is set. list: the word you point at takes the highlighter. leaders: a book's contents page; each word is joined to its keys by leader dots, and the dots of the word you point at ink from the word to its keys (submenus keep the leaders). marginalia: the word you point at is explained by a small note hung beside the list on a hairline arrow, gliding from word to word; with no room beside the list it stands under it. |
DropdownMenuItem hint | string | For marginalia: the note hung beside the item while you point at it. Screen readers hear it as the item's description. Checkbox, radio and sub-trigger items take it too. | |
DropdownMenuItem variant | "default" | "destructive" | "default" | A destructive item sits a little heavier and says so in its words ("Delete Halden for good"). It is never red; red is for errors. |
DropdownMenuCheckboxItem / RadioItem | Radix | Checked, the sentence turns to the expression italic. No tick, no dot; screen readers get aria-checked. | |
DropdownMenuShortcut | span | Shortcut keys as ot-kbd caps. "⌘D" is two keys; a string with spaces splits on them. It reads ⌘D on a right-to-left page too. | |
DropdownMenu dir | "ltr" | "rtl" | Without one, the menu takes the direction of the page around its trigger: on a right-to-left page it hangs from the trigger's right end, submenus open to the left, and Left opens them. | |
DropdownMenuSubTrigger | Radix | The only item with an arrow (→): it opens something. Right opens, Left closes (mirrored right to left). | |
DropdownMenuSubContent | Radix | Opens beside its item. On a narrow screen, with room on neither side, it drops open under the item, in by the words' inset, and hangs from it on a leader; the item's arrow turns down (↓). | |
Keyboard | keys | Down and Up move, Home and End go to the ends, Enter runs, Escape closes and returns focus to the trigger. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Combobox, menu, command | Weingart letter | The highlighter marks what matches |
| Leaders menu | A book's contents page | Leader dots join each word to its keys |
| Marginalia menu | "It has to be design." notes on hairline arrows | The word you point at is explained in the margin |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Menu | Arpeggio | Dot, leader, words in turn; a context menu spreads from the point |
| Leaders menu | Pass-through | The dots of the word you point at ink from the word to its keys and leave the same way |
| Marginalia menu | Glide | The note glides beside the list to the word you point at |