Zetta v1.6

Components · Overlays & menus

Dropdown

Dropdown menu component for contextual action menus and option lists. Distinct from Select (form input value selection) — Dropdown is for triggering actions.

Preview

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Dropdown" 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 `dropdown` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `dropdown` 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

┌──────────────────┐
│ Label  [▼]       │  ← dropdown-trigger
└──────────────────┘
         ↓ (open)
┌──────────────────────┐
│  Section Label        │  ← dropdown-section-label
│  ─────────────────    │  ← dropdown-separator
│  [icon] Item 1        │  ← dropdown-item (see zetta-dropdown-item-v1_6.md)
│  [icon] Item 2        │
│  ─────────────────    │
│  [icon] Danger item   │  ← dropdown-item-danger
└──────────────────────┘  ← dropdown-panel

When to use

  • Row-level action menus in tables (edit, duplicate, delete).
  • Toolbar overflow menus.
  • Contextual options related to a specific element.

When not to use

  • When only one action is available — use a Button.
  • When the user is selecting a form value — use Select.
  • When filtering a dataset — use a Filter component with checkboxes.

Do

  • Group related items with section labels and separators.
  • Place destructive items (delete, remove) at the bottom, separated from safe actions.
  • Keep item labels to 1–4 words.

Don't

  • Don't use a dropdown for navigation — use Links or the Sidebar.
  • Don't nest dropdowns inside dropdown panels.
  • Don't use icons alone without text labels in dropdown items.
  • Section labels: short noun. "Actions", "Export as", "More options". Title case.
  • Item labels: verb phrase. "Edit record", "Export as CSV", "Delete permanently".
  • Danger items: be specific. "Delete project" not "Delete".
  • Max 10 items before grouping or pagination. More than 15 — use a dialog.
  • Keyboard shortcut hints: right-aligned in muted text. "⌘K", "Del".

Keyboard

Key Behavior
Enter / Space Open dropdown when trigger is focused
Down Arrow Move focus to first item when panel opens
Up / Down Arrow Navigate between items
Home / End Jump to first / last item
Enter Activate focused item
Escape Close panel, return focus to trigger
Tab Close panel, move focus to next focusable element

Accessibility

  • Trigger ARIA: aria-haspopup="menu", aria-expanded="true/false", aria-controls="panel-id"
  • Panel ARIA: role="menu", id="panel-id"
  • Items ARIA: role="menuitem" (standard), role="menuitemcheckbox" (checkbox variant), role="menuitemradio" (radio variant)
  • Focus returns to the trigger when the panel closes.
  • Disabled items: aria-disabled="true" — still focusable but not activatable.

Spec

Values and token references resolved straight from the Zetta spec.

dropdown-trigger
backgroundColor
transparent
textColor
{colors.ink}
fontFamily
Geist
fontSize
14px
fontWeight
500
borderColor
{colors.border-strong}
borderWidth
1px
borderRadius
{rounded.base}
paddingX
{spacing.sm}
paddingY
{spacing.xs}
display
inline-flex
alignItems
center
gap
{spacing.xs}
cursor
pointer
height
32px
dropdown-trigger-hover
backgroundColor
{colors.surface-muted}
borderColor
{colors.border-strong}
dropdown-trigger-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
dropdown-trigger-open
backgroundColor
{colors.primary-bg}
textColor
{colors.primary}
borderColor
{colors.primary}
dropdown-trigger-disabled
textColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
backgroundColor
{colors.disabled-bg}
cursor
not-allowed
dropdown-panel
backgroundColor
{colors.surface-card}
borderColor
{colors.hairline}
borderWidth
1px
borderRadius
{rounded.base}
shadow
{elevation.shadow.overlay}
padding
{spacing.xs}
minWidth
160px
maxWidth
280px
zIndex
{elevation.z-index.popup}
dropdown-section-label
textColor
{colors.placeholder}
fontSize
11px
fontWeight
600
fontFamily
Geist
textTransform
uppercase
letterSpacing
0.06em
paddingX
{spacing.sm}
paddingY
{spacing.xs}
dropdown-separator
borderColor
{colors.hairline}
borderWidth
1px
marginX
{spacing.xs}
marginY
{spacing.xs}