Select
- Layer: Component
- Base:
@ark-ui/solid/select(single, multi) +@ark-ui/solid/combobox(searchable)
Select is one component with three call shapes: standard single, multi-select with chips, and searchable (combobox). Trigger height, menu offset, and item geometry are shared across all three so they read as the same primitive.
Single
Section titled “Single”Backend picker
Selected: ollama
Single with meta column
Section titled “Single with meta column”Site picker — right-aligned mono caption ("127 evt")
The selected row gets a left accent bar (2px cyan with glow) — not a checkmark. This is the canonical single-select mark.
Single with icons
Section titled “Single with icons”Priority — icon left of the label, mirrors into the trigger
Set icon: () => <YourIcon /> on any SelectItemType. The icon renders to the left of the label in every row, left of the selected value in the trigger, and left of the chip label in multi-select chips. Comes through unchanged into the searchable variant’s rows too (the searchable trigger is a text <input>, so icons don’t appear there — that’s a structural property of comboboxes).
Multi-select
Section titled “Multi-select”Phenomena — chips inside the trigger, +N more overflow
4 selected
The trigger renders selected values as chips (with their icon left of the label, if set); once the count exceeds maxVisibleChips (default 2), the remainder collapses into a dashed +N MORE overflow chip. Menu rows show a 14×14 tick box (not an accent bar) so the multi nature is obvious at a glance.
Searchable
Section titled “Searchable”Combobox-backed single select with type-to-filter
Searchable selects use Ark UI’s Combobox underneath — the trigger becomes an input you can type into, and results filter live.
Anatomy
Section titled “Anatomy”import { Select, type SelectItemType } from "@risingswamp/design-system";
const items: SelectItemType[] = [ { value: "swamp-east-2", label: "Swamp bank · east-2", meta: "127 evt" }, { value: "swamp-west-1", label: "Swamp bank · west-1", meta: "89 evt" },];
// Single<Select label="Site" items={items} value={value()} onChange={setValue} />
// Multi<Select multiple label="Phenomena" items={items} value={values()} onChange={setValues}/>
// Searchable<Select searchable label="Site" items={items} value={value()} onChange={setValue} />Select — common
Section titled “Select — common”| Prop | Type | Default | Description |
|---|---|---|---|
items | SelectItemType[] | required | Options. See SelectItemType below |
label | string | — | Floating label rendered above the trigger |
placeholder | string | — | Trigger placeholder when nothing is selected |
disabled | boolean | false | Disables the trigger |
name | string | — | Form field name (for <form> integration) |
class | string | — | Extra class on the root |
variant | 'outline' | 'ghost' | 'outline' | Park UI recipe variant |
size | 'sm' | 'md' | 'lg' | 'md' | Park UI recipe size |
Single mode (default)
Section titled “Single mode (default)”| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled selected value |
onChange | (value: string) => void | — | Called when the selection changes |
searchable | boolean | false | Use combobox-backed type-to-filter trigger |
Multi mode (multiple)
Section titled “Multi mode (multiple)”| Prop | Type | Default | Description |
|---|---|---|---|
multiple | true | required | Switches the component into multi mode (discriminant) |
value | string[] | — | Controlled selected values |
onChange | (value: string[]) => void | — | Called when the selection changes |
maxVisibleChips | number | 2 | Chips rendered before collapsing the remainder into a +N MORE overflow |
SelectItemType
Section titled “SelectItemType”| Field | Type | Description |
|---|---|---|
value | string | Stable identifier |
label | string | Visible row text |
meta | string | Optional right-aligned mono uppercase caption (e.g. 127 evt, dormant) |
icon | () => JSX.Element | Optional renderer placed left of the label (and left of the trigger value) |
disabled | boolean | Disables the row |
Geometry
Section titled “Geometry”| Token | Value | Source / note |
|---|---|---|
| Trigger min-height | 40px | Brief: 04 · Select spec |
| Menu offset | 6px | Ark UI positioning={{ gutter: 6 }} |
| Menu max-height | 240px | ~6 rows then scroll |
| Tick box (multi) | 14×14px | Same geometry as Checkbox |
| Single mark | left accent bar | 2px rail with cyan glow, no checkmark |
| Chip text | mono 10px UPPER | accent.dim background, accent.text colour |
Design tokens
Section titled “Design tokens”| Token | Usage |
|---|---|
bg.surface.alt | Trigger background |
bg.surface | Menu background — opaque, no blur, no shadow |
bg.dim | Hover / highlighted row |
accent.default | Single accent bar, multi tick fill |
accent.dim | Selected row background, chip background |
accent.text | Selected row text, chip text |
accent.glow | Single accent bar box-shadow |
border | Trigger and menu border, tick-box outline |
text.muted | Caret, meta column, placeholder |
{shadows.focus-input} | Trigger glow on _open and _focusVisible |
Accessibility
Section titled “Accessibility”- Built on Ark UI
Select(single, multi) andCombobox(searchable) — full keyboard support out of the box. - Trigger exposes
aria-expanded,aria-haspopup="listbox", andaria-controls. - Items expose
aria-selected(single) /aria-checked(multi); the indicator is decorative. - Multi-select chip × buttons stop pointer-down propagation so removing a chip doesn’t reopen the menu.
- Multi-select never uses an accent bar; single-select never uses a tick box. Reversing them breaks the “what kind of select is this?” read at a glance.
metais opt-in per item — leave it off for short option lists where the caption would be noise.- The trigger is Plane-1 by design system rules: no drop shadow, no blur. The cyan glow on focus/open is
box-shadowfrom{shadows.focus-input}, which is permitted as a focus affordance.