Cadence Clinical
Composites

Dropdown menu

A list of actions that opens from a trigger. Each item is as high as a control, a checked item carries a tick, and a long item wraps.

GradeTested

Usage

import {
  Button,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@cadence-clinical/ui";

<DropdownMenu>
  <DropdownMenuTrigger render={<Button variant="outline" size="sm" iconOnly />}>
    <Ellipsis aria-hidden />
    Appointment actions
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem onClick={reschedule}>Reschedule</DropdownMenuItem>
    <DropdownMenuItem onClick={printLetter}>Print letter</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem variant="destructive" onClick={cancel}>
      Cancel appointment
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>;

Render DropdownMenuTrigger as a Button through render. The menu takes its name from its trigger, so an icon-only trigger still needs its words.

A menu does things. To hold a choice, use a Select. For controls rather than actions, such as a set of filters, use a Popover.

Dropdown menu is built on the Base UI Menu and accepts all of its props. It follows the shadcn Dropdown menu and keeps its part names, so a menu copied from a shadcn block works as written.

Items that hold a state

DropdownMenuCheckboxItem is on or off. DropdownMenuRadioItem, inside a DropdownMenuRadioGroup, is one of a set. Each carries a tick when it is on, so its state does not rely on colour.

<DropdownMenuGroup>
  <DropdownMenuLabel>Columns</DropdownMenuLabel>
  <DropdownMenuCheckboxItem checked={showClinic} onCheckedChange={setShowClinic}>
    Clinic
  </DropdownMenuCheckboxItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
  <DropdownMenuLabel>Sort by</DropdownMenuLabel>
  <DropdownMenuRadioGroup value={sort} onValueChange={setSort}>
    <DropdownMenuRadioItem value="date">Date</DropdownMenuRadioItem>
    <DropdownMenuRadioItem value="clinic">Clinic</DropdownMenuRadioItem>
  </DropdownMenuRadioGroup>
</DropdownMenuGroup>

Labels and shortcuts

  • DropdownMenuLabel inside a DropdownMenuGroup or a DropdownMenuRadioGroup names that set for a screen reader. On its own it is words above the items and names nothing.
  • DropdownMenuShortcut shows the keys that do what the item does, at the end of the item. It is a hint: handling the keys is up to you.
  • inset on an item or a label lines it up with items that have an icon.

A destructive item

variant="destructive" sets an item in the critical colour. The colour adds to the words and never stands in for them: the item says what it does. For an action that cannot be undone, ask first in a Dialog.

What Cadence changes

  • No submenus. shadcn's DropdownMenuSub parts are left out. A submenu hides actions a level deeper, and inside one Base UI's focus guards fail an accessibility check in Safari. Put the actions in the menu, or in a second menu.

  • An item is as high as a control, so it is a 44px target when the density is comfortable. The shadcn item is about 28px.

  • A long item wraps, and the menu is never wider or taller than the room it has. The shadcn menu is the width of its trigger with a small minimum, and hides what overflows sideways.

  • The menu opens at once. A menu is opened often and from the keyboard, where an animation only delays. shadcn zooms and slides it in.

  • A label works outside a group. Base UI's group label throws outside a group. shadcn's menus, such as the user menu of its sidebar, put a label straight into the content, so here that works.

  • No dark: overrides or tinted text. A destructive item uses critical-text, on critical-subtle when highlighted. Both pairings, and critical-text on the popover surface, are asserted in the @cadence-clinical/tokens test suite.

Accessibility

  • The arrow keys move through the items, Enter or Space acts on one, and Escape closes the menu. Focus goes back to the trigger.
  • The menu is named by its trigger, and a group by its label.
  • A disabled item has aria-disabled and does nothing.

Props

PropOnTypeDefault
open, onOpenChangeDropdownMenu
sideDropdownMenuContentAs in Base UI's Positioner"bottom"
alignDropdownMenuContentAs in Base UI's Positioner"start"
variantDropdownMenuItem"default" | "destructive""default"
insetItems, DropdownMenuLabelbooleanfalse
checkedDropdownMenuCheckboxItemboolean
valueDropdownMenuRadioGroup, DropdownMenuRadioItemstring

On this page