Skip to content

Components

Menus

DropdownMenu and ContextMenu are two triggers for one menu language: actions and options attached to a trigger, or to a target. Radix owns focus, typeahead, submenus and Escape; LUC owns the surface.

Examples

import { Button, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, IconButton, } from "@luc/ui";

Dropdown menu

Icons lead, shortcuts trail, and a destructive action is a negative label rather than a filled red row — nothing implies the click already happened.

Choices inside a menu

Checkbox and radio items are for options you are toggling in place, not for picking a value a form should own — that is Select or Combobox.

Sorted by sharpe.

Context menu on a row

The same actions, attached to the object itself. Right click, or press Shift+F10 / the context-menu key with the row focused. Every row is a fair target — the menu is not a replacement for a visible action when one action dominates.

Research runs, with a context menu on each row
RunStatusActions
btc-sma-2026-09Complete
eth-momentum-2026-09Running
macro-regime-2026-08Failed

Which control?

Four components look similar in a screenshot and behave completely differently. Pick by the question being answered.

Select

Choose one value from a known, short list. Reads like a form field and reports a value.

Combobox

Search, then choose. Use when the list is long, remote or free-text filtered.

DropdownMenu

Run an action, or toggle options, attached to a trigger. Nothing is submitted.

ContextMenu

The same actions, attached to the object the pointer is on.

CommandMenu

Global navigation and search across the application.

API

Component props
PropTypeDefaultNotes
DropdownMenu*Radix DropdownMenu parts—Root, Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Sub/SubTrigger/SubContent, Label, Separator.
ContextMenu*Radix ContextMenu parts—Identical surface and row rhythm; the trigger is a target instead of a button.
shortcutReactNode—Trailing keyboard hint on an item. Purely presentational.
variant"default" | "destructive""default"Destructive is a negative label, not a filled red row.
insetbooleanfalseAligns a row without a leading indicator with rows that have one.
sideOffsetnumber6Distance from the trigger; menu sections use 4.

Rules

Do

  • Keep the first action visible on the row and put the rest in the menu.
  • Name the trigger for what it acts on — “Actions for btc-sma-2026-09”.
  • Group with labels and separators once there are more than about five items.

Don't

  • Use a menu to pick a form value.
  • Hide the only route to a primary action inside a context menu.
  • Colour a whole row red — the label carries the tone.

Usage

import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuSeparator } from "@luc/ui/context-menu";
<ContextMenu>
  <ContextMenuTrigger>Right-click this row</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem shortcut="⌘C">Copy id</ContextMenuItem>
    <ContextMenuSeparator />
    <ContextMenuItem variant="danger">Archive</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

Guidelines

Do

  • Use it for actions on the object the pointer is over, and keep the same actions reachable from a visible control for touch and keyboard readers.
  • Write the `shortcut` as the key chord the app actually binds; it is a hint beside the label, not a binding the menu installs.

Don't

  • Put the only path to an action in a context menu; there is no right-click on a phone and no discoverable affordance on a page.

Accessibility

The trigger opens on the keyboard's context-menu key as well as on right-click, and the content is a menu: Arrow keys move, Escape closes, and focus returns to the trigger. Items are announced with their shortcut as a description.

import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "@luc/ui/dropdown-menu";
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="secondary">Actions</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent align="end">
    <DropdownMenuItem>Duplicate</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem variant="danger">Delete</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

DropdownMenu

Do

  • Use it for a short list of commands that all act on the page's current object, and set `align` so the panel opens toward the available space.
  • Use `variant="danger"` for a destructive item; it is the colour and weight the reader has learned to read as destructive.

Don't

  • Put more than a handful of items in a dropdown; a menu the reader has to scroll is a list that wants its own surface.

Accessibility

The trigger is a menu button with `aria-haspopup`; the content is a `menu` with roving focus. Arrow keys move, Escape closes, typeahead jumps to an item, and focus returns to the trigger on close.

Luc UI v1.0ContextMenudark theme