Menu

Triggered action list with keyboard navigation and dismissal.

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
NameTypeDefaultDescription
openbooleanfalseControlled open state.
itemsArray<{ 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.
themeThemeContract—Sets the theme used to resolve component styles.
Events
NamePayloadDescription
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
NameDescription
triggerTrigger content for the menu.

Variants, sizes, and states

Variants

  • bottom/top/left/right: Placement chooses the preferred anchored direction.

Sizes

  • sm: Compact menu
  • md: Default menu

States

  • closed: Menu hidden
  • open: Menu visible
  • highlighted: 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