Skip to content
SuperCoach

Components · Actions

Dropdown menu

Beta

A menu of actions on one item, such as a client row.

Sara Al-Harbi

Fat loss · week 4 of 12

When to use

  • For the secondary actions on one item, behind a "More" button: a client row, a program card, an invoice.
  • For view options that don't need to be on screen all the time: which columns to show, how to sort.
  • For the extra options of a split button (see Button group).

When not to use

  • To pick a value for a form field: use a Select. A menu runs actions; a select stores a value.
  • For the one main action on a page: show it as a Button, not hidden in a menu.
  • For two or three actions with room to show them: use buttons. Hidden actions are found less often.
  • For navigation between pages: use links, or the Sidebar.

Anatomy

  1. 1. Trigger: a Button, often icon-only with an aria-label
  2. 2. Label (optional): names the menu or the item it acts on
  3. 3. Item: optional 16px icon, then a verb
  4. 4. Submenu trigger: a chevron at the end opens a nested menu
  5. 5. Destructive item: last, after a separator

Open the menu in the example to see parts 2 to 5. Separators split groups of related items.

Variants

Item typeUse for
DropdownMenuItemAn action. Closes the menu when chosen.
DropdownMenuItem variant="destructive"An action that removes data. Put it last, and confirm it in an Alert dialog.
DropdownMenuCheckboxItemOptions that are each on or off: columns in the client list.
DropdownMenuRadioGroup + RadioItemOne option from a set: sort order.
DropdownMenuSubA short second level, such as the programs you can assign. One level only.
DropdownMenuLabel, Separator, ShortcutStructure. Show a shortcut only if it really works in that view.

Use inset on items without icons in a menu where other items have icons, so the text lines up.

States

StateWhat changesToken
OpenMenu panel with a border and medium shadow, on the dropdown layerbg-popover, shadow-md, z-(--z-dropdown)
Highlighted item (hover or arrow keys)Accent fill and textbg-accent, text-accent-foreground
Open submenu triggerStays highlighted while its submenu is opendata-[state=open]:bg-accent
CheckedCheck mark (checkbox) or dot (radio) at the startItemIndicator
Disabled itemHalf opacity, skipped by arrow keysopacity-(--opacity-disabled)
Destructive itemRed text and icon; a red tint when highlightedtext-destructive-text, bg-destructive/10
Trigger focus-visibleThe Button's ringring-ring/50

The disabled state is in the "Sort" menu above: "Reset to default order" is disabled until you change the order.

Destructive items use text-destructive-text, so they stay above 4.5:1 when highlighted on their 10% destructive tint. Upstream's text-destructive computed to about 4.1:1 there.

Usage guidelines

Do.The trigger's label names the item it acts on, and the destructive action is last, after a separator.
Don't.An unlabelled icon trigger, and Remove first, where a quick click lands on it.
  • Order items by frequency, then group them: view, edit, share; then status changes; then destructive.
  • Keep menus short: about 7 items. More than that usually means some belong on the page.
  • Don't put a form or free text inside a menu: use a Popover or Dialog.
  • In a table, every row's trigger has the same position and icon, and its aria-label includes the row's name.

Content guidelines

  • Items start with a verb and name the object when it isn't obvious: "Send message", "Assign program", "Remove client".
  • Sentence case, no ending punctuation. Add "…" when the item opens a dialog that asks for more ("Remove client…").
  • Checkbox and radio items are nouns, not verbs: "Last check-in", "Next payment".
  • Arabic items use the verbal noun consistently: "إرسال رسالة"، "تعيين برنامج"، "إزالة العميل…".

Accessibility

KeyDoes
Enter, Space or Arrow downOn the trigger: opens the menu and focuses the first item
Arrow up / downMoves between items, skipping disabled ones; wraps only if loop is set
Arrow right (left in RTL), Enter or SpaceOn a submenu trigger: opens the submenu
Arrow left (right in RTL), Escape in a submenuCloses the submenu
Enter or SpaceChooses the item. Checkbox and radio items toggle
Type a letterMoves to the next item starting with it
EscapeCloses the menu and returns focus to the trigger
TabDoes nothing: focus stays in the menu until an item is chosen or Escape is pressed
  • The trigger has aria-haspopup="menu" and aria-expanded. The panel is a menu; items are menuitem, menuitemcheckbox or menuitemradio.
  • A screen reader announces the trigger as "Actions for Sara Al-Harbi, menu button, collapsed", then each item as "Send message, menu item, 2 of 5" (exact words vary by screen reader).
  • Focus moves into the menu on open and back to the trigger on close. Nothing outside the menu can be clicked or focused while it is open (it is modal by default).
  • Destructive items need a confirmation; color alone doesn't say "this deletes".

RTL

  • The menu aligns to the trigger's start or end edge as set by align, so align="end" opens toward the left in RTL.
  • Icons and check marks sit at the start of each item and move to the right. Shortcuts sit at the end.
  • The submenu chevron mirrors (rtl:-scale-x-100) and submenus open toward the left. Arrow keys follow the direction because the site provides Radix's DirectionProvider.

Tokens used

Read from components/ui/dropdown-menu.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.

Semantic color tokens used by this component
TokenUtilitiesSuperCoachCoach
--accentbg-accentbrand-50orange-50
--accent-foregroundtext-accent-foregroundbrand-700orange-700
--borderbg-borderneutral-200neutral-200
--destructivebg-destructive/10 text-destructivered-600red-600
--destructive-texttext-destructive-textred-700red-700
--muted-foregroundtext-muted-foregroundneutral-600neutral-600
--popoverbg-popoverwhitewhite
--popover-foregroundtext-popover-foregroundneutral-950neutral-950
  • Layerz-(--z-dropdown)
  • Opacityopacity-(--opacity-disabled)
  • Radiusrounded-md
  • Radiusrounded-sm
  • Shadowshadow-lg
  • Shadowshadow-md
  • Typefont-medium
  • Typetext-sm
  • Typetracking-wide
  • Typetext-xs

Props

<DropdownMenu>

Also takes every prop of Radix DropdownMenu.Root

No props of its own.

<DropdownMenuTrigger>

Also takes every prop of Radix DropdownMenu.Trigger

No props of its own.

<DropdownMenuContent>

Also takes every prop of Radix DropdownMenu.Content

No props of its own.

<DropdownMenuItem>

Also takes every prop of Radix DropdownMenu.Item

DropdownMenuItem props
PropTypeDefault
inset?boolean—
variant?"default" | "destructive""default"

<DropdownMenuCheckboxItem>

Also takes every prop of Radix DropdownMenu.CheckboxItem

No props of its own.

<DropdownMenuRadioItem>

Also takes every prop of Radix DropdownMenu.RadioItem

No props of its own.

<DropdownMenuLabel>

Also takes every prop of Radix DropdownMenu.Label

DropdownMenuLabel props
PropTypeDefault
inset?boolean—

<DropdownMenuSubTrigger>

Also takes every prop of Radix DropdownMenu.SubTrigger

DropdownMenuSubTrigger props
PropTypeDefault
inset?boolean—

Replaces in coach-admin

From the read-only audit of coach-admin: 94 style values, 178 uses.

coach-admin has at least five action menus: Bootstrap's .dropdown-menu, the shared ActionDropdown, .actionMenu, the landing page's SectionActionsDropdown and the bulk-actions menu. All become this component. Dropdowns that pick a value (dropdown-list, el-select) map to Select instead.

Classes and selectors

  • .dropdown-menu67
  • .dropdown-item:hover:first-child12
  • .dropdown-menu:not(.nutrition-tab__dropdown-menu)12
  • .actionMenuButton10
  • .dropdown-item:hover9
  • .dropdown-item9
  • .section-actions-dropdown__toggle6
  • .filter-dropdown-menu5
  • .section-actions-dropdown__menu5
  • .action-dropdown-menu5
  • .dropdown-item:hover:last-child4
  • .actionMenu-content4

Files

  • src/views/LandingPage/components/SectionActionsDropdown/SectionActionsDropdown.scss20
  • src/reusableComponents/formComponents/action-dropdown/ActionDropdown.scss11

The inventory lists the first 12 locations of each value, so file counts are a lower bound.

SCSS variables in these styles

  • $border-color→ --border
  • $white-text-color→ --primary-foreground
  • $spacing-2xsnot a color: spacing, size or layout
  • $secondary-color→ nearest orange-500 (no semantic token)
  • $line-height-basenot a color: spacing, size or layout
  • $secondary-color-hover→ nearest red-200 (no semantic token)
  • $spacing-xsnot a color: spacing, size or layout
  • $spacing-smnot a color: spacing, size or layout
  • $dropdown-border-color→ nearest neutral-300 (no semantic token)
  • $icon-color-hover-filternot a color: spacing, size or layout

Context menu, Select, Button group, Popover, Alert dialog.