Components · Overlays & menus
Context menu
Right-click triggered contextual action menu. Split from zetta-overlay-v1_4.md (removed in v1.5) in v1.4.
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 "Context menu" 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 `context-menu` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `context-menu` 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
[right-click target]
↓ cursor position
┌─────────────────────────────┐
│ [icon] Open │
│ [icon] Edit │
│ ───────────────────────── │
│ [icon] Duplicate │
│ [icon] Rename │
│ ───────────────────────── │
│ [icon] Delete │ ← context-menu-item-danger
└─────────────────────────────┘
| State | Visual |
|---|---|
| Closed | Not rendered |
| Open | Panel at cursor coordinates, shadow applied |
| Item hover | {colors.primary-bg} fill, {colors.primary} text |
| Item focus | {colors.primary-bg} fill + focus outline |
| Danger item hover | {colors.danger-bg} fill, {colors.danger-text} text |
| Item disabled | {colors.disabled-text}, not-allowed cursor |
| Sub-menu trigger hover | Shows chevron, child panel opens on hover/Enter |
When to use
- Table row right-click actions (open, edit, duplicate, delete).
- Canvas or workspace element right-click.
- File or resource right-click in a file manager.
When not to use
- Primary actions — always surface via buttons or explicit UI.
- As the only way to perform an action — right-click is not discoverable for keyboard and touch users.
Do
- Mirror the most important actions in visible UI (toolbar buttons or icon buttons).
- Always place destructive items (delete) at the bottom, separated by a divider.
- Limit to 8–10 items. More → group into sub-menus.
Don't
- Don't rely on context menu as the sole access point for any action.
- Don't nest more than one level of sub-menus.
- Item labels: verb + noun. "Edit record", "Duplicate", "Delete project".
- Danger item: specific destructive label. "Delete project" not "Delete".
- Sub-menu trigger label: noun describing the group. "Export as", "Move to".
- Separator: group related items. Safety actions (delete) always in their own group at the bottom.
Keyboard
| Key | Behavior |
|---|---|
Right-click / ⇧F10 |
Open context menu |
Up / Down Arrow |
Navigate items |
Right Arrow |
Open sub-menu |
Left Arrow |
Close sub-menu, return to parent |
Enter |
Activate item |
Escape |
Close menu |
Accessibility
role="menu"on the panel,role="menuitem"on items.role="menuitem"witharia-haspopup="menu"on sub-menu triggers.aria-disabled="true"on disabled items (still focusable).- Focus returns to the trigger element on close.
Spec
Values and token references resolved straight from the Zetta spec.
context-menu
- backgroundColor
- {colors.popover}
- textColor
- {colors.popover-foreground}
- borderColor
- {colors.hairline}
- borderWidth
- 1px
- borderRadius
- {rounded.base}
- shadow
- {elevation.shadow.overlay}
- padding
- 6px
- zIndex
- {elevation.z-index.popup}
- minWidth
- 180px
- maxWidth
- 280px
context-menu-item
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
- borderRadius
- {rounded.sm}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 400
- textColor
- {colors.ink}
- cursor
- pointer
- display
- flex
- alignItems
- center
- gap
- {spacing.sm}
- minHeight
- 32px
context-menu-item-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
context-menu-item-focus
- backgroundColor
- {colors.primary-bg}
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- -2px
context-menu-item-danger
- textColor
- {colors.danger-text}
context-menu-item-danger-hover
- backgroundColor
- {colors.danger-bg}
- textColor
- {colors.danger-text}
context-menu-item-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed
context-menu-separator
- height
- 1px
- backgroundColor
- {colors.hairline}
- marginY
- 4px
context-menu-sub-trigger
- chevronColor
- {colors.muted}
- chevronSize
- 14px
context-menu-sub-content
- marginLeft
- 2px