Combobox
Filters and selects an option from large sets while preserving form value and keyboard navigation.
Design
Functional preview
Running with the stable ds-tis/combobox JavaScript runtime.
Open the HTML/CSS/JS playground
Anatomy
- Brazil
Type to filter countries.
.ds-combobox) — wrapper with input, icon, clear, and chevron.2 Input (
.ds-combobox__input) — editable text with role="combobox".3 Icon (
.ds-combobox__icon) — optional, decorative.4 Listbox (
.ds-combobox__listbox) — popup with filterable options.5 Option (
.ds-combobox__option) — selectable item with role="option".6 Label and helper — external via
ds-field (ADR-017).
Default
- Argentina
- Brazil
- Chile
Type to filter countries.
Open listbox
Compose with ds-field for label, helper, and error. Wrap .ds-combobox and .ds-combobox__listbox in .ds-combobox-anchor to position the popup. The public ds-tis/combobox module (initComboboxes / destroyComboboxes) is required for open state, filtering, selection, and keyboard. Event: ds-combobox-change.
- Argentina
- Brazil
- Chile
- Colombia (unavailable)
Type to filter countries.
Sizes
| Size | Height | Padding |
|---|---|---|
Small (--sm) | component.combobox.height.sm (32px) | component.combobox.padding-x.sm |
Medium (--md) | component.combobox.height.md (40px) | component.combobox.padding-x.md |
Large (--lg) | component.combobox.height.lg (48px) | component.combobox.padding-x.lg |
States
Error
Disabled
Read-only
Figma API
The live component set composes the shared field (ADR-019) with a local listbox. State covers Default, Hover, Focus, and Disabled; Filled, Error, and Read-only are separate properties, like Select.
| Property | Type | Repo equivalent |
|---|---|---|
Show Label | BOOLEAN | ds-field + .ds-field__label |
Label | TEXT | .ds-field__label |
Placeholder | TEXT | placeholder no input |
Content | TEXT | valor preenchido em .ds-combobox__input |
Show Left Icon / Left Icon | BOOLEAN / INSTANCE_SWAP | .ds-combobox__icon |
Show Clear Button / Clear Icon | BOOLEAN / INSTANCE_SWAP | .ds-combobox__clear |
Chevron Icon | INSTANCE_SWAP | .ds-combobox__chevron |
Show Helper Text / Helper Text | BOOLEAN / TEXT | .ds-field__helper |
Error Message | TEXT | .ds-field__error + ds-combobox--error |
Size | VARIANT | ds-combobox--sm / --md / --lg |
State | VARIANT | Default, Hover, Focus, Disabled |
Filled / Error / Read-only | BOOLEAN | ds-combobox--filled, --error, --readonly |
Usage
When to use
Best practices
Content guidelines
| Rule | Example |
|---|---|
| Placeholder em sentence case | "Choose a country" — not "COUNTRY" |
| Options concisas e consistentes | "Brazil", "United States" |
Sempre com ds-field + label | <label for="country">Country</label> |
Related
Implementation
- Status
- Stable
- Distribution
- npm package
Installation
npm install ds-tispnpm add ds-tisyarn add ds-tisbun add ds-tisImports
import 'ds-tis/css'
import { initComboboxes } from 'ds-tis/combobox'
initComboboxes()Markup
<div class="ds-field">
<label class="ds-field__label" for="country">País</label>
<div class="ds-combobox-anchor">
<div class="ds-combobox ds-combobox--md">
<input id="country" class="ds-combobox__input" role="combobox" aria-expanded="false" aria-controls="country-list" aria-autocomplete="list" />
<button class="ds-combobox__clear" type="button" aria-label="Limpar seleção">…</button>
</div>
<ul id="country-list" class="ds-combobox__listbox" role="listbox" hidden>
<li class="ds-combobox__option" role="option">Brasil</li>
</ul>
</div>
</div>Web implementation contract
Token mapping
| Property | Token Component | CSS variable |
|---|---|---|
| field surface | component.field.* | --ds-field-* |
| height | component.combobox.height.{sm|md|lg} | --ds-combobox-height-* |
| padding | component.combobox.padding-x.* | --ds-combobox-padding-x-* |
| listbox | component.combobox.listbox.container.* | --ds-combobox-listbox-* |
| option | component.combobox.option.* + component.menu.item.* | --ds-combobox-option-* |
| focus ring | component.focus-ring.* | --ds-focus-ring-* |
CSS classes
| Class | Description |
|---|---|
ds-combobox | Field wrapper |
ds-combobox__input | Editable input with role="combobox" |
ds-combobox__listbox | Options popup container |
ds-combobox__option | Listbox item (role="option") |
ds-combobox__icon | Optional leading icon (Lucide) |
ds-combobox__clear | Clear selection button |
ds-combobox__chevron | Decorative popup indicator |
ds-combobox--sm/md/lg | 32 / 40 / 48px sizes |
ds-combobox--error | Error state |
ds-combobox--disabled | Disabled state |
ds-combobox--readonly | Read-only — visible value without editing |
ds-combobox--filled | Filled value |
Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Arrow Down / Arrow Up | Moves focus between options when listbox is open |
Enter | Selects focused option and closes listbox |
Escape | Closes listbox and returns focus to input |
| Typing | Filters options (product implementation) |
Accessibility
| WCAG criterion | Requirement | Status |
|---|---|---|
| 4.1.2 Name, Role, Value (A) | role="combobox", aria-expanded, aria-controls, role="listbox" / role="option" | ✓ |
| 1.3.1 Info and Relationships (A) | Label via ds-field; error with aria-invalid + aria-describedby | ✓ |
| 2.4.11 Focus Appearance (AA) | Visible focus ring on field and options | ✓ |
Output responsibility
Initialize the public runtime after render and destroy it when the owning view unmounts.
Validation evidence
Covered by the stable Storybook, public runtime tests when applicable, keyboard scenarios, responsive checks, and Axe.