Playground
Overview
- Use a menu for actions. Use
Select for a form value.
When to use
- Use
Menu when you need a short contextual list of actions tied to a trigger. - Use destructive styling when one option carries real risk.
When not to use
- Do not use
Menu for long searchable option lists; use Select or Combobox. - Keep the primary action visible outside the menu.
Structural parts
open control
trigger Opens the menu surface.
menu overlay
surface Contains the action list.
menu item
item Selectable action row.
Public interface
Props| Name | Type | Default | Description |
|---|
open | boolean | false | Controlled open state. |
items | Array<{ value: string; label: string; shortcut?: string; disabled?: boolean; destructive?: boolean }> | — | Menu item definitions. |
placement | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Preferred anchor placement. |
size | 'sm' | 'md' | 'md' | Chooses the menu size recipe. |
onOpenChange | (detail: { open: boolean }) => void | — | Runs when the menu opens or closes. |
onAction | (detail: { value: string }) => void | — | Receives the selected menu item value for pointer and keyboard activation. |
theme | ThemeContract | — | Sets the theme used to resolve component styles. |
Events| Name | Payload | Description |
|---|
openchange | { open: boolean } | Fires when an overlay-style component opens or closes through user interaction. |
action | { value: string } | Fires with the selected menu item value. |
Slots| Name | Description |
|---|
trigger | Trigger content for the menu. |
Variants, sizes, and states
Variants
bottom/top/left/right: Placement chooses the preferred anchored direction.
Sizes
sm: Compact menumd: Default menu
States
closed: Menu hiddenopen: Menu visiblehighlighted: Keyboard-highlighted item
Accessibility
Semantics
- Uses menu semantics and a focused action list.
Keyboard
- Arrow keys move through menu items, Enter commits the current item, and Escape closes the menu.
Screen readers
- Item labels and disabled/destructive state remain understandable without color alone.
Checklist
- Verify that the control has an accessible name.
- Verify that keyboard focus is visible in every state and size.
- Identify disabled, selected, and error states with text or symbols as well as color.
Tokens and CSS variables
root
- --dk-motion-duration
- 200ms
- --dk-menu-offset
- 12px
surface
- --dk-menu-surface-bg
- #f2f5fb
- --dk-menu-surface-fg
- #16181c
- --dk-menu-surface-border
- #95989d
- --dk-menu-surface-radius
- clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
- --dk-menu-surface-shadow
- 0 10px 15px rgba(0, 0, 0, 0.06), 0 4px 6px rgba(0, 0, 0, 0.04)
- --dk-menu-surface-width
- 260px
- --dk-menu-surface-padding
- clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
item
- --dk-menu-item-bg
- transparent
- --dk-menu-item-fg
- #16181c
- --dk-menu-item-radius
- clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
- --dk-menu-item-min-height
- 44px
- --dk-menu-item-inline-padding
- clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
label
- --dk-menu-label-size
- clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
- --dk-menu-label-weight
- 550
shortcut
- --dk-menu-shortcut-size
- clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
Implementation notes
Menu supports action items, not checkbox or radio items.
Examples
Starter
Contextual action list
A short anchored action menu.
<Menu items={[{ value: 'rename', label: 'Rename' }, { value: 'archive', label: 'Archive', destructive: true }]}><svelte:fragment slot="trigger">Open menu</svelte:fragment></Menu>
Common patterns
Shortcut-aware menu
Display keyboard shortcuts with their action labels.
<Menu items={[{ value: 'duplicate', label: 'Duplicate', shortcut: '⌘D' }, { value: 'archive', label: 'Archive', shortcut: '⌘⌫' }]} />
Edge cases
Externally controlled menu
Control open state when a larger workflow coordinates menu visibility.
<Menu open={isOpen} onOpenChange={(detail) => isOpen = detail.open} items={menuItems}><svelte:fragment slot="trigger">Open menu</svelte:fragment></Menu>
Mathematical fixtures
Calculated fixtures; browser evidence is not collected. Layout estimates use declared text behavior and maximum token values.
default (size=sm)
pass size=sm
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 98.5 Lc
- Rows: 44 px
- Surface: 260 × 280 px
default (size=md)
pass size=md
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 98.5 Lc
- Rows: 44 px
- Surface: 260 × 280 px
default (size=lg)
pass size=lg
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 98.5 Lc
- Rows: 44 px
- Surface: 260 × 280 px
selected (size=sm)
pass size=sm
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 97.9 Lc
- Rows: 44 px
- Surface: 260 × 280 px
selected (size=md)
pass size=md
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 97.9 Lc
- Rows: 44 px
- Surface: 260 × 280 px
selected (size=lg)
pass size=lg
- Evidence: mathematical
- Coverage: complete (3 / 3 declared kinds evaluated)
- Declared: contrast, optionRow, anchoredSurface
- Evaluated: contrast, optionRow, anchoredSurface
- Contrast: 97.9 Lc
- Rows: 44 px
- Surface: 260 × 280 px