Navigation
Dropdown Menu
A floating action menu with items, checkbox and radio items, submenus and context-menu anchoring.
<script>
import {
DropdownMenu,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
Button,
} from "@codeatlasdev/ui";
</script>
<DropdownMenu>
{#snippet triggerChild(props)}<Button {...props}>Open menu</Button>{/snippet}
<DropdownMenuLabel>File actions</DropdownMenuLabel>
<DropdownMenuItem onclick={edit}>{#snippet children()}Edit{/snippet}</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive" onclick={remove}>{#snippet children()}Delete{/snippet}</DropdownMenuItem>
</DropdownMenu>
Keyboard support follows the WAI-ARIA menu pattern: Enter, Space or ArrowDown opens the menu on the first item, ArrowUp opens it on the last, arrows then move, Enter or Space selects, Escape closes and focus returns to the trigger. Tab closes the menu. Selecting an item closes it. The menu is a popover layer in the browser top layer, positioned with CSS anchor positioning where supported, so it stays on top of a dialog it is opened from. Pass an interactive trigger (Button, IconButton) through triggerChild and spread the received props on it. Use trigger only for non-interactive content such as text or an icon. Use DropdownMenuLabel for a non-interactive group heading and DropdownMenuSeparator for a divider.
Checkbox and radio items
DropdownMenuCheckboxItem renders role="menuitemcheckbox" with aria-checked and a check indicator; bind checked or listen to onCheckedChange. DropdownMenuRadioGroup wraps DropdownMenuRadioItems (role="menuitemradio"): bind the group’s value, each item sets it with its own value. Enter, Space or a click toggles the item and the menu stays open so several options can be set in one visit (the WAI-ARIA menu pattern); pass closeOnSelect to close after one pick. Arrow keys, Home, End and typeahead reach these items like any other, and disabled ones are skipped. Give the group an aria-label or put a DropdownMenuLabel first and the group is named for screen readers.
Controlled and context menus
bind:open and onOpenChange control the menu from outside: set open = true from any handler and it opens; onOpenChange fires for user-driven changes (a trigger press, Escape, an outside press, picking an item), like Dialog. For a right-click menu pass anchor={{ x, y }} (viewport coordinates) together with open = true and leave out trigger: the menu opens at that point on the first item and focus returns to the element that had it when the menu closes. Setting anchor again while the menu is open moves it instead of toggling it, so a second right-click relocates the menu. Handle Shift+F10 and the ContextMenu key in your own onkeydown the same way (the demo shows both); on the built-in trigger they open the menu on its first item.
Submenus
Put a DropdownMenuSub among the items. Its trigger snippet holds a DropdownMenuSubTrigger (an item with a chevron, aria-haspopup="menu" and aria-expanded) and its children are the submenu’s items, labels and further DropdownMenuSubs, nested to any depth. ArrowRight, Enter or Space on the trigger opens the submenu with focus on its first enabled item; ArrowLeft or Escape closes only that level and returns focus to the trigger; arrows, Home, End and typeahead stay inside the open level. Hovering the trigger opens it after openDelay (120 ms) and it stays open for closeDelay (200 ms) after the pointer leaves, so the pointer can cross the gap. Selecting any item closes every level and returns focus to the root trigger. A disabled sub-trigger neither opens nor takes ArrowRight. In a right-to-left layout use side="left" on the sub, and the chevron mirrors.
Leading icons and brand logos
Pass a leading icon with the icon snippet instead of placing it in children: it gets a fixed 14px box, the standard gap and aria-hidden (the label stays the accessible name), so icon size and alignment never drift between menus. Any svg or img inside the box fills it, including one wrapped in SchemeAsset, whatever width or height the asset declares. A plain icon (Lucide, any currentColor glyph) follows the item’s text color, including the highlighted, disabled and destructive states.
A brand logo (a multi-color or fixed-color SVG) must not be recolored: set brandIcon on the item and no state color or dimming reaches the icon. A monochrome mark such as the OpenAI blossom needs a black asset on light themes and a white one on dark: wrap both in SchemeAsset, which renders the variant of the kit’s color scheme (the .dark class on <html>, or a data-theme-scope island) with CSS only, so it is correct in SSR and on first paint and no app has to read the theme store. The same icon / brandIcon pair exists on TabsTrigger. Follow each brand’s own guidelines for the marks you ship.
<DropdownMenuItem brandIcon onclick={openCodex}>
{#snippet icon()}
<SchemeAsset>
{#snippet light()}<img src="/logos/codex-black.svg" alt="" />{/snippet}
{#snippet dark()}<img src="/logos/codex-white.svg" alt="" />{/snippet}
</SchemeAsset>
{/snippet}
Codex
</DropdownMenuItem>
Default
A button trigger with a label, items, a separator and a destructive item.
Reference #158 · Pick in the gallery
Code
Leading icons and brand logos
The icon slot gives every item the same box and gap. brandIcon keeps a logo's own colors, and SchemeAsset swaps a monochrome mark between light and dark.
Reference #239 · Pick in the gallery
Code
Checkbox and radio items
Two toggles and a sort order. The menu stays open while options change; each item reports its state with aria-checked.
Reference #400 · Pick in the gallery
Code
Context menu
Right-click opens the menu at the pointer and a second right-click moves it. Shift+F10 or the ContextMenu key opens it from the keyboard.
Reference #402 · Pick in the gallery
Code
Submenus
Color and Move to open beside their item. ArrowRight opens, ArrowLeft or Escape closes one level, hover opens after a short delay.
Reference #401 · Pick in the gallery
Code
Icon trigger
An IconButton trigger. IconButton needs an aria-label.
Reference #159 · Pick in the gallery
Code
DropdownMenu
| Prop | Type | Default | Description |
|---|---|---|---|
triggerChild | Snippet<[Record<string, unknown>]> | – | Interactive trigger. Receives the trigger props to spread onto your Button or IconButton. |
trigger | Snippet | – | Non-interactive trigger content (text, icon), wrapped in the menu's own button. |
children | Snippet | – | Menu content: items, labels, separators, checkbox and radio items and submenus. |
open | boolean | false | Bindable. Whether the menu is open. Set it from code to open or close the menu. |
onOpenChange | (open: boolean) => void | – | Called when the user opens or closes the menu. |
anchor | { x: number; y: number } | null | null | Viewport point the menu opens at (a context menu). Changing it while open moves the menu. Omit `trigger` when you use it. |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Preferred side of the trigger. |
align | 'start' | 'center' | 'end' | 'start' | Alignment against the trigger. |
sideOffset | number | 6 | Distance from the trigger in pixels (0 when opened at an `anchor` point). |
class | string | '' | Extra CSS classes on the content. |
DropdownMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
onclick | () => void | – | Called when the item is selected. |
variant | 'default' | 'destructive' | 'default' | destructive colors the item with the error token. |
disabled | boolean | false | Prevents selection. |
icon | Snippet | – | Leading icon in a fixed 14px box, `aria-hidden`. A plain icon follows the item's text color. |
brandIcon | boolean | false | The icon is a brand mark with its own colors: no highlighted, disabled or destructive color reaches it. Pair with `SchemeAsset` for light and dark assets. |
children | Snippet | – | Item label. |
class | string | '' | Extra CSS classes. |
DropdownMenuCheckboxItem
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | false | Bindable. Whether the item is checked. |
onCheckedChange | (checked: boolean) => void | – | Called with the new state when the user toggles the item. |
disabled | boolean | false | Prevents toggling; arrow keys skip the item. |
closeOnSelect | boolean | false | Close the menu after toggling. |
children | Snippet | – | Item label. |
class | string | '' | Extra CSS classes. |
DropdownMenuRadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | – | Bindable. Value of the checked radio item. |
onValueChange | (value: string) => void | – | Called with the new value when the user picks an item. |
children | Snippet | – | DropdownMenuRadioItem children (and an optional DropdownMenuLabel). |
class | string | '' | Extra CSS classes. |
DropdownMenuRadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | – | Required. The group value this item sets. |
disabled | boolean | false | Prevents selection; arrow keys skip the item. |
closeOnSelect | boolean | false | Close the menu after picking. |
children | Snippet | – | Item label. |
class | string | '' | Extra CSS classes. |
DropdownMenuSub
| Prop | Type | Default | Description |
|---|---|---|---|
trigger | Snippet | – | Required. The `DropdownMenuSubTrigger` item that opens the submenu. |
children | Snippet | – | Submenu content: items, labels, separators and nested submenus. |
open | boolean | false | Bindable. Whether the submenu is open. |
onOpenChange | (open: boolean) => void | – | Called when the submenu opens or closes. |
disabled | boolean | false | The trigger is aria-disabled and the submenu never opens. |
side | 'top' | 'right' | 'bottom' | 'left' | 'right' | Preferred side of the trigger item. |
align | 'start' | 'center' | 'end' | 'start' | Alignment against the trigger item. |
sideOffset | number | 2 | Distance from the trigger item in pixels. |
openDelay | number | 120 | Milliseconds the pointer rests on the trigger before it opens. |
closeDelay | number | 200 | Milliseconds it stays open after the pointer leaves. |
class | string | '' | Extra CSS classes on the submenu content. |
DropdownMenuSubTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
icon | Snippet | – | Leading icon in a fixed 14px box, `aria-hidden`. |
children | Snippet | – | Item label. |
class | string | '' | Extra CSS classes. |
DropdownMenuLabel
| Prop | Type | Default | Description |
|---|---|---|---|
children | Snippet | – | Heading text. |
class | string | '' | Extra CSS classes. |
Every item, the radio group and the checkbox item forward their remaining attributes (data-*, id, aria-*, title) to their element, so data-testid and aria-keyshortcuts land on the menuitem. role and tabindex stay owned by the component.
DropdownMenuSeparator takes no props.
SchemeAsset
| Prop | Type | Default | Description |
|---|---|---|---|
light | Snippet | – | Required. Rendered while the light scheme is active. |
dark | Snippet | – | Required. Rendered while the dark scheme is active. |
class | string | '' | Extra CSS classes. |