Components

MultiSelect

A tag / category editor — colour-coded chips in a searchable popover.

Available since@crisp-ui-kit/crisp@0.11.0

A tag/category editor: a searchable popover of colour-coded chips, each with a right-aligned checkbox that toggles without closing the menu. Selected values render back in the trigger as the same chips. Measured from Attio's Categories field, so its trigger, listbox and rows read as the same family as Select (12px popover radius, shared shadow, 36px search row, 32px rows); each row ends in a 16px checkbox that fills brand when selected.

Tag swatches

An option can carry a swatch (1 to 12) instead of a tone. Each swatch is one of Attio's twelve option colours: a pastel fill, its own darker text colour and a 1px inset ring, all measured off Attio's Categories options.

Installation

npm install @crisp-ui-kit/crisp

Keyboard

The popup carries role="listbox" and runs crisp's shared model — the same one Select uses.

KeyAction
↓ / ↑Move the highlight, wrapping at both ends.
Home / EndJump to the first or last option, within the filtered list.
Enter / SpaceToggle the highlighted option.
EscClose, returning focus to the trigger.
TabClose — but focus moves on rather than snapping back to the trigger.

Props

Prop

Type

MultiSelectOption is { value, label, tone? } — value is the stable identity used in value/onValueChange, label is the chip's display text, and tone is an optional chip colour (derived from the label by a stable hash if unset).

Notes

  • Chips. Each option renders as a tone-coloured pill. Tones are assigned by a stable hash of the label, so the same category is always the same colour without hand-mapping every option.
  • Toggle in place. Clicking a row toggles its checkbox and keeps the popover open, so you can add several tags in one pass.
  • Keyboard. ↑/↓ move through options, Enter/Space toggles the focused row, Esc closes; the filter input narrows the list as you type (Space stays a normal character there).
  • readOnly vs disabled. Both refuse to open; readOnly keeps the trigger focusable, disabled removes it from the tab order and dims it.
  • variant="ghost" fills whatever row height it's embedded in, instead of the fixed size trigger height — for an inline table cell editor.
  • Sizing (crisp/#1071). size is still accepted so existing code compiles, but is ignored: Attio has one trigger (a 32px floor that grows with wrapped chips).

On this page