MultiSelect
A tag / category editor — colour-coded chips in a searchable popover.
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/crispKeyboard
The popup carries role="listbox" and runs crisp's shared model — the same one
Select uses.
| Key | Action |
|---|---|
↓ / ↑ | Move the highlight, wrapping at both ends. |
Home / End | Jump to the first or last option, within the filtered list. |
Enter / Space | Toggle the highlighted option. |
Esc | Close, returning focus to the trigger. |
Tab | Close — 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;
readOnlykeeps the trigger focusable,disabledremoves it from the tab order and dims it. variant="ghost"fills whatever row height it's embedded in, instead of the fixedsizetrigger height — for an inline table cell editor.- Sizing (crisp/#1071).
sizeis still accepted so existing code compiles, but is ignored: Attio has one trigger (a 32px floor that grows with wrapped chips).