Combobox
July 6, 2026 · View on GitHub
Select with an embedded text input for filtering options. Single and multiple selection.
Contract
<kp-combobox> is a Select-shaped control where the trigger is a real text field. Typing into it narrows the option list via case-insensitive substring match; matches are highlighted. Pick with click, Enter, or Tab. Shares the Input size ramp (xs / sm / md / lg / xl) so it composes with the rest of the form grammar.
Anatomy
$ \text{Combobox} ├─ \text{Trigger} (\text{focuses} \text{on} \text{click}, \text{holds} \text{the} \text{query} \text{text}) │ ├─ \text{Multi}-\text{summary} — "3 \text{selected}" \text{when} \text{closed} \text{in} \text{multi} \text{mode} │ ├─ \text{Input} — \text{filter} \text{query} + \text{aria}-\text{expanded} / \text{aria}-\text{activedescendant} │ ├─ \text{Clear} — \times \text{button} (\text{when} \text{a} \text{value} \text{is} \text{set}) │ └─ \text{Chevron} — \text{rotates} \text{when} \text{open} └─ \text{Dropdown} (\text{listbox}, \text{appears} \text{below} \text{trigger}) ├─ \text{Option} — \text{with} \text{optional} \text{checkbox} (\text{multi}) + <\text{mark}>-\text{highlighted} \text{match} └─ \text{Empty} \text{state} — "\text{No} \text{results} \text{found}" $
API
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Size ramp (inherits Input's grammar) |
options | KpComboboxOption[] | [] | { value, label, disabled? } list |
placeholder | string | 'Search or select…' | Shown when no value |
emptyMessage | string | 'No results found' | Shown when the filtered list is empty |
multiple | boolean | false | Multi-select mode |
showClear | boolean | true | Show the × clear affordance |
disabled | boolean | false | Disable the whole control |
ariaLabel | string | '' | Accessible label forwarded to the <input> |
forceState | KpState | null | null | Pin visual state (hover, focus, error, disabled) for docs |
Outputs
| Name | Payload | Fires when |
|---|---|---|
openChange | boolean | Dropdown opens / closes |
queryChange | string | User types in the filter input |
Forms
Implements ControlValueAccessor. In single mode ngModel is a string \| null; in multi mode it's string[].
<kp-combobox [options]="fruits" [(ngModel)]="fruit"/>
<kp-combobox [multiple]="true" [options]="tags" [(ngModel)]="picked"/>
States
| State | Behavior |
|---|---|
| rest | Neutral border, placeholder visible |
| hover | Border darkens |
| focus / open | Border turns blue; dropdown visible when open |
| disabled | Muted background, no interaction |
| error | Red border |
Accessibility
- Roles:
role="combobox"on the<input>,role="listbox"on the dropdown,role="option"on each row. - ARIA:
aria-expandedtracks open statearia-autocomplete="list"aria-controls+ matchingidon the listboxaria-activedescendantpoints to the current keyboard-highlighted optionaria-selectedon selected options;aria-multiselectableon the listbox whenmultiple
- Keyboard:
↓/↑— move the highlight; skips disabled optionsHome/End— jump to first / lastEnter— pick the highlighted optionEscape— close the dropdown and clear the queryBackspaceon an empty query in multi mode — remove the last picked tag
- Click-outside closes the dropdown; focus returns naturally to where the user clicked.
Do / Don't
Do
- Pair long lists of known values (countries, tags, SKUs) with Combobox — the filter makes them scannable.
- Provide a short
placeholderthat hints at the search, e.g."Search countries…". - Use
multiplewhen users routinely pick more than one value; otherwise stick with single.
Don't
- Don't use Combobox for free-form input — it snaps to known options on commit. If users need to type arbitrary strings, use
<kp-input>+ a suggestions menu. - Don't put more than ~500 options in the default (synchronous) mode. For async / remote results, render
optionsfrom your query observable and let the component filter the displayed slice. - Don't hide the clear affordance when a value is set — users expect to reset without opening a menu.
References
- Figma component:
ComboboxComponent Set - Storybook: https://gregnblack.github.io/kanso-protocol/?path=/docs/components-combobox
- Source:
packages/ui/combobox/src/ - Tokens used:
- Input border / background (shared with
<kp-input>,<kp-select>) - Match highlight:
combobox/highlight
- Input border / background (shared with
Changelog
0.1.0— Initial release. Single + multi, 5 sizes, keyboard navigation, match-highlighting, full CVA support.