Zetta v1.6

Components · Forms & selection

Multi-select

Combobox variant that supports selecting multiple values displayed as inline tags. Split from zetta-form-v1_4.md (removed in v1.5) in v1.4.

Preview

Design Research Add team…
Design
Engineering
Marketing
Research
Sales
Support

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 "Multi-select" 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 `multi-select` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `multi-select` 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

┌──────────────────────────────────────────┐
│ [Design ×] [Frontend ×] [type to search…]│  ← trigger (grows vertically)
└──────────────────────────────────────────┘
  ↓ open
┌──────────────────────────────────────────┐
│  ✓ Design       ← selected (primary)     │
│    Backend                               │  ← item
│    Frontend  ✓  ← selected               │
│  (No results)   ← multi-select-empty     │
└──────────────────────────────────────────┘
State Trigger border Notes
Default (empty) {colors.border-strong} Placeholder shown
Default (has values) {colors.border-strong} Tags shown inline
Hover {colors.border-strong} + shadow
Focus / Open {colors.border-focus} + glow Dropdown visible
Invalid {colors.danger} Error state
Disabled {colors.disabled-border} not-allowed, tags not removable

When to use

  • Selecting multiple values from a known list (tags, categories, assignees, skills).
  • When selected items need to be visible and individually removable.

When not to use

  • Single selection — use Select.
  • A small fixed set (≤5 options) — use Checkbox group instead.

Do

  • Show a "No results" empty state when the search yields nothing.
  • Support Backspace to remove the last tag when the input is empty.
  • Limit maxSelected when there is a meaningful constraint.

Don't

  • Don't allow duplicate selections.
  • Don't allow free-text entry unless explicitly a "creatable" multi-select.
  • Placeholder: "Select options…" or context-specific — "Add tags", "Assign members".
  • Tag remove button aria-label: "Remove [value]".
  • Empty state: "No options found." or "No results for '[query]'."
  • Selected item in dropdown: show check icon prefix in {colors.primary}.

Keyboard

Key Behavior
Tab Focus trigger
Type Filter options in dropdown
Down Arrow Move focus to first dropdown item
Up / Down Arrow Navigate dropdown items
Enter / Space Select / deselect focused item
Escape Close dropdown
Backspace (empty input) Remove last selected tag

Accessibility

  • ARIA role: role="combobox" on trigger, aria-multiselectable="true" on listbox.
  • aria-selected="true" on selected options in the listbox.
  • Each tag remove button: aria-label="Remove [value]".
  • aria-live="polite" region to announce additions and removals: "Design added. 3 items selected."
  • aria-invalid="true" + aria-describedby in error state.

Spec

Values and token references resolved straight from the Zetta spec.

multi-select-trigger
backgroundColor
{colors.surface-card}
borderColor
{colors.border-strong}
borderWidth
1px
borderRadius
{rounded.base}
minHeight
32px
paddingX
14px
paddingY
6px
display
flex
flexWrap
wrap
alignItems
center
gap
6px
cursor
text
multi-select-trigger-hover
borderColor
{colors.border-strong}
boxShadow
{elevation.shadow.raised}
multi-select-trigger-focus
borderColor
{colors.border-focus}
outline
2px solid {colors.border-focus}
outlineOffset
2px
multi-select-trigger-invalid
borderColor
{colors.danger}
multi-select-trigger-invalid-focus
borderColor
{colors.danger}
outline
2px solid {colors.danger}
outlineOffset
2px
multi-select-trigger-disabled
backgroundColor
{colors.disabled-bg}
borderColor
{colors.disabled-border}
cursor
not-allowed
multi-select-tag
backgroundColor
{colors.surface-secondary}
textColor
{colors.ink}
borderRadius
{rounded.full}
fontFamily
Geist
fontSize
12px
fontWeight
400
lineHeight
1.5
paddingX
10px
paddingY
2px
display
flex
alignItems
center
gap
4px
multi-select-tag-remove
iconSize
12px
iconColor
{colors.muted}
cursor
pointer
borderRadius
{rounded.full}
padding
2px
multi-select-tag-remove-hover
iconColor
{colors.ink}
backgroundColor
{colors.surface-pressed}
multi-select-input
fontFamily
Geist
fontSize
14px
fontWeight
400
textColor
{colors.ink}
placeholderColor
{colors.placeholder}
placeholderStyle
italic
flex
1
minWidth
80px
height
20px
backgroundColor
transparent
multi-select-content
backgroundColor
{colors.popover}
textColor
{colors.popover-foreground}
borderColor
{colors.hairline}
borderWidth
1px
borderRadius
{rounded.base}
padding
6px
shadow
{elevation.shadow.overlay}
multi-select-item
textColor
{colors.ink}
fontFamily
Geist
fontSize
14px
fontWeight
400
borderRadius
{rounded.base}
paddingX
12px
paddingY
10px
cursor
pointer
display
flex
alignItems
center
gap
8px
multi-select-item-hover
backgroundColor
{colors.accent}
multi-select-item-selected
textColor
{colors.primary}
fontWeight
500
checkIconColor
{colors.primary}
checkIconSize
16px
multi-select-item-disabled
textColor
{colors.disabled-text}
cursor
not-allowed
multi-select-empty
textColor
{colors.muted}
fontFamily
Geist
fontSize
14px
textAlign
center
paddingX
12px
paddingY
16px