Components · Overlays & menus
Menu item
The rows inside a Menu or dropdown. Three types — a plain action (with optional icon, description and shortcut), a checkbox item for toggles, and a single-select item with a check for one-of-many. All hover and select in the Primary tint.
Preview
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Menu item" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `menu-item` spec (variants: action, checkbox, single-select, danger) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `menu-item` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
╭─────────────────────────────────────────────╮
│ [icon] Label text ⌘K [shortcut] │ ← standard item
│ Optional description │
├─────────────────────────────────────────────┤
│ [icon] Danger action │ ← danger variant
╰─────────────────────────────────────────────╯
Parts:
dropdown-item-icon— optional leading icon (16px,{colors.muted})dropdown-item-label— primary action text, truncated at container edgedropdown-item-description— optional secondary line (12px muted)dropdown-item-shortcut— optional trailing keyboard shortcut (11px muted, right-aligned)dropdown-item-separator— 1px hairline divider between groupsdropdown-item-section-heading— optional group label above a section of items
| State | Background | Text | Icon |
|---|---|---|---|
| Rest | transparent | {colors.ink} |
{colors.muted} |
| Hover | {colors.primary-bg} |
{colors.primary} |
{colors.primary} |
| Focus | 2px inset {colors.border-focus} |
— | — |
| Active | {colors.primary-bg} |
{colors.primary} |
— |
| Selected | {colors.primary-bg} |
{colors.primary} / 500 |
— |
| Danger rest | transparent | {colors.danger} |
{colors.danger} |
| Danger hover | {colors.danger-bg} |
{colors.danger} |
— |
| Disabled | transparent | {colors.disabled-text} |
— |
When to use
- Any menu action that triggers a single operation and should close the menu immediately.
- Actions like: Edit, Duplicate, Rename, Export, Delete, Copy link, Move to, Open in new tab.
When not to use
- Multi-selection — use Dropdown Item Checkbox.
- Persistent single selection (sort order, view mode) — use Dropdown Item Single Select.
Do
- Use the danger variant only for irreversible or destructive actions. Position danger items last, separated by a
dropdown-item-separator. - Show a shortcut only if the keyboard shortcut actually works — don't display decorative shortcuts.
- Keep labels concise — verb + noun. "Delete item", "Copy link", "Move to project".
Don't
- Don't use dropdown items as navigation links — use a Menu or Sidebar Nav for navigation.
- Don't nest more than one level of sub-menus inside a dropdown.
- Don't disable items without explaining why — use a tooltip on the trigger or add a description.
- Labels: verb + noun phrase. "Delete project", "Export as CSV", "Copy shareable link".
- Danger labels: be explicit — "Delete permanently", not just "Delete" if the action is irreversible.
- Shortcuts: use standard OS notation. Mac: ⌘⇧K. Windows/Linux: Ctrl+Shift+K.
- Description: one short phrase. Max 60 characters. Never required — use only when label alone is ambiguous.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Moves focus into and through menu items |
Up / Down Arrow |
Navigate between items in the open dropdown |
Home |
Move focus to first item |
End |
Move focus to last item |
Enter / Space |
Activate the focused item and close the menu |
Escape |
Close the dropdown without activating an item |
Accessibility
- ARIA role:
role="menuitem"on each item.role="menu"on the list container. - Disabled:
aria-disabled="true". Do not use thedisabledHTML attribute — it removes the item from focus order and keyboard focus, which breaks navigation. - Shortcut: use
aria-keyshortcutson the item to announce the keyboard shortcut to screen readers. - Danger: communicate destructive nature in the label text — don't rely on color alone.
- Section heading:
role="presentation"on headings — they are not interactive and should not be announced as actionable.
Spec
Values and token references resolved straight from the Zetta spec.
dropdown-item
- display
- flex
- alignItems
- center
- gap
- {spacing.sm}
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
- borderRadius
- {rounded.sm}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 400
- lineHeight
- 1.40
- textColor
- {colors.ink}
- cursor
- pointer
- minHeight
- 32px
- userSelect
- none
dropdown-item-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
dropdown-item-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- -2px
dropdown-item-active
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
dropdown-item-selected
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
- fontWeight
- 500
dropdown-item-danger
- textColor
- {colors.danger}
dropdown-item-danger-hover
- backgroundColor
- {colors.danger-bg}
- textColor
- {colors.danger}
dropdown-item-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed
dropdown-item-icon
- iconSize
- {icons.sizeMap.md}
- iconColor
- {colors.muted}
- flexShrink
- 0
dropdown-item-icon-hover
- iconColor
- {colors.primary}
dropdown-item-icon-danger
- iconColor
- {colors.danger}
dropdown-item-label
- flex
- 1
- overflow
- hidden
- textOverflow
- ellipsis
- whiteSpace
- nowrap
dropdown-item-description
- fontSize
- 12px
- textColor
- {colors.muted}
- marginTop
- 1px
- overflow
- hidden
- textOverflow
- ellipsis
- whiteSpace
- nowrap
dropdown-item-shortcut
- fontSize
- 11px
- fontWeight
- 400
- textColor
- {colors.muted}
- marginLeft
- auto
- flexShrink
- 0
- paddingLeft
- {spacing.sm}
dropdown-item-separator
- height
- 1px
- backgroundColor
- {colors.hairline}
- marginY
- {spacing.xs}
dropdown-item-section-heading
- fontFamily
- Geist
- fontSize
- 11px
- fontWeight
- 500
- textColor
- {colors.muted}
- textTransform
- uppercase
- letterSpacing
- 0.06em
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
dropdown-item-checkbox
- display
- flex
- alignItems
- center
- gap
- {spacing.sm}
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
- borderRadius
- {rounded.sm}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 400
- lineHeight
- 1.40
- textColor
- {colors.ink}
- cursor
- pointer
- minHeight
- 32px
- userSelect
- none
dropdown-item-checkbox-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
dropdown-item-checkbox-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- -2px
dropdown-item-checkbox-checked
- backgroundColor
- {colors.primary-bg}
dropdown-item-checkbox-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed
dropdown-item-checkbox-indicator
- width
- 16px
- height
- 16px
- borderRadius
- {rounded.sm}
- borderWidth
- 1.5px
- borderStyle
- solid
- borderColor
- {colors.border-strong}
- backgroundColor
- {colors.surface-card}
- flexShrink
- 0
- display
- flex
- alignItems
- center
- justifyContent
- center
dropdown-item-checkbox-indicator-checked
- backgroundColor
- {colors.primary}
- borderColor
- {colors.primary}
- iconColor
- {colors.primary-text}
- iconSize
- {icons.sizeMap.sm}
dropdown-item-checkbox-indicator-disabled
- backgroundColor
- {colors.disabled-bg}
- borderColor
- {colors.disabled-border}
dropdown-item-checkbox-label
- flex
- 1
- overflow
- hidden
- textOverflow
- ellipsis
- whiteSpace
- nowrap
dropdown-item-checkbox-description
- fontSize
- 12px
- textColor
- {colors.muted}
- marginTop
- 1px
dropdown-item-single-select
- display
- flex
- alignItems
- center
- gap
- {spacing.sm}
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
- borderRadius
- {rounded.sm}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 400
- lineHeight
- 1.40
- textColor
- {colors.ink}
- cursor
- pointer
- minHeight
- 32px
- userSelect
- none
dropdown-item-single-select-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
dropdown-item-single-select-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- -2px
dropdown-item-single-select-selected
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
- fontWeight
- 500
dropdown-item-single-select-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed
dropdown-item-single-select-check
- width
- {icons.sizeMap.md}
- height
- {icons.sizeMap.md}
- flexShrink
- 0
- iconColor
- {colors.primary}
- opacity
- 0
dropdown-item-single-select-check-visible
- opacity
- 1
dropdown-item-single-select-label
- flex
- 1
- overflow
- hidden
- textOverflow
- ellipsis
- whiteSpace
- nowrap
dropdown-item-single-select-description
- fontSize
- 12px
- textColor
- {colors.muted}
- marginTop
- 1px
dropdown-item-single-select-leading-icon
- iconSize
- {icons.sizeMap.md}
- iconColor
- {colors.muted}
- flexShrink
- 0
Known gaps
- Nested sub-menu item (item that opens a child menu) not yet specified.
- Item with avatar (e.g. user list item) not yet specified.