Combo Box
formUSWDS-derivedinteractive
A typeahead/autocomplete select with text input and filterable dropdown list.
Reference: USWDS documentation ↗
Variants
Combo Box With Default
- California
- Florida
- New York
- Texas
- Washington
<flex-combo-box>
<label class="flex-label" for="state">Select a state</label>
<div class="flex-combo-box__wrapper">
<input class="flex-combo-box__input" id="state" name="state" type="text" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-controls="state-list" autocomplete="off" value="New York" data-value="ny"/>
<button type="button" class="flex-combo-box__toggle" tabindex="-1" aria-label="Toggle options">
<svg class="flex-icon" aria-hidden="true" focusable="false">
<use href="/static/sprite.svg#expand_more">
</use>
</svg>
</button>
<button type="button" class="flex-combo-box__clear" tabindex="-1" aria-label="Clear selection">
<svg class="flex-icon" aria-hidden="true" focusable="false">
<use href="/static/sprite.svg#close">
</use>
</svg>
</button>
<ul class="flex-combo-box__list" id="state-list" role="listbox" hidden="">
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="ca" id="state-opt-ca">California</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="fl" id="state-opt-fl">Florida</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="ny" id="state-opt-ny" aria-selected="true">New York</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="tx" id="state-opt-tx">Texas</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="wa" id="state-opt-wa">Washington</li>
</ul>
</div>
</flex-combo-box>Default Combo Box
- Apple
- Apricot
- Avocado
- Banana
- Blackberry
- Blueberry
- Cherry
- Cranberry
- Grape
- Grapefruit
- Lemon
- Lime
- Mango
- Orange
- Papaya
- Peach
- Pear
- Pineapple
- Raspberry
- Strawberry
- Watermelon
<flex-combo-box>
<label class="flex-label" for="fruit">Select a fruit</label>
<div class="flex-combo-box__wrapper">
<input class="flex-combo-box__input" id="fruit" name="fruit" type="text" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-controls="fruit-list" autocomplete="off" value="" data-value=""/>
<button type="button" class="flex-combo-box__toggle" tabindex="-1" aria-label="Toggle options">
<svg class="flex-icon" aria-hidden="true" focusable="false">
<use href="/static/sprite.svg#expand_more">
</use>
</svg>
</button>
<button type="button" class="flex-combo-box__clear" tabindex="-1" aria-label="Clear selection" hidden="">
<svg class="flex-icon" aria-hidden="true" focusable="false">
<use href="/static/sprite.svg#close">
</use>
</svg>
</button>
<ul class="flex-combo-box__list" id="fruit-list" role="listbox" hidden="">
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="apple" id="fruit-opt-apple">Apple</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="apricot" id="fruit-opt-apricot">Apricot</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="avocado" id="fruit-opt-avocado">Avocado</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="banana" id="fruit-opt-banana">Banana</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="blackberry" id="fruit-opt-blackberry">Blackberry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="blueberry" id="fruit-opt-blueberry">Blueberry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="cherry" id="fruit-opt-cherry">Cherry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="cranberry" id="fruit-opt-cranberry">Cranberry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="grape" id="fruit-opt-grape">Grape</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="grapefruit" id="fruit-opt-grapefruit">Grapefruit</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="lemon" id="fruit-opt-lemon">Lemon</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="lime" id="fruit-opt-lime">Lime</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="mango" id="fruit-opt-mango">Mango</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="orange" id="fruit-opt-orange">Orange</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="papaya" id="fruit-opt-papaya">Papaya</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="peach" id="fruit-opt-peach">Peach</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="pear" id="fruit-opt-pear">Pear</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="pineapple" id="fruit-opt-pineapple">Pineapple</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="raspberry" id="fruit-opt-raspberry">Raspberry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="strawberry" id="fruit-opt-strawberry">Strawberry</li>
<li class="flex-combo-box__option" role="option" tabindex="-1" data-value="watermelon" id="fruit-opt-watermelon">Watermelon</li>
</ul>
</div>
</flex-combo-box>Contract
Class mapping
| USWDS | Flex | Notes |
|---|---|---|
usa-combo-box | <flex-combo-box> (custom element) | Container element |
usa-combo-box__input | .flex-combo-box__input | Text input with combobox role |
usa-combo-box__toggle-list | .flex-combo-box__toggle | Toggle dropdown button |
usa-combo-box__clear-input | .flex-combo-box__clear | Clear selection button |
usa-combo-box__list | .flex-combo-box__list | Dropdown listbox |
usa-combo-box__list-option | .flex-combo-box__option | Individual option items |
usa-combo-box__list-option--focused | [data-focused] | Highlighted option via keyboard navigation |
Verified properties
font-familyfont-sizeline-heightcolorbackground-colorcursorIntentional differences
padding-right: ours = 4.5rem (space for toggle + clear buttons), USWDS = 2.5rem (USWDS uses different button layout)
Our layout positions both toggle and clear buttons inside the input area
Behavior promises
- ✓ Click input opens dropdown showing all options
- ✓ Typing filters options case-insensitively
- ✓ Shows "No results found" when filter matches nothing
- ✓ Click option selects it and closes list
- ✓ Enter key selects highlighted option
- ✓ ArrowDown opens list if closed
- ✓ ArrowDown/Up navigate options
- ✓ Escape closes list and restores previous value
- ✓ Tab closes list and keeps selection
- ✓ Clear button resets input
- ✓ Outside click closes dropdown
- ✓ aria-expanded updates on open/close
- ✓ aria-activedescendant updates on keyboard nav
- ✓ aria-selected marks the selected option
- ✓ Accessibility audit passes
Source CSS
Base styles: Form Control Base
/* flex-combo-box -- USWDS Combo Box conformance
Extends: Form Control Base (base-classes.css#form-control)
Features: typeahead input, filterable dropdown, keyboard navigation */
flex-combo-box {
display: block;
max-inline-size: var(--flex-control-max-width);
}
.flex-combo-box__wrapper {
position: relative;
display: flex;
align-items: center;
margin-block-start: var(--flex-control-margin-top);
}
/* Input — extends form control base styles */
.flex-combo-box__input {
font-size: var(--flex-text-uswds);
line-height: 1.3;
color: var(--flex-color-text);
background-color: var(--flex-color-surface);
border: var(--flex-control-border);
border-radius: 0;
appearance: none;
display: block;
inline-size: 100%;
block-size: var(--flex-control-height);
padding: var(--flex-control-padding);
padding-inline-end: 4.5rem;
margin-block-start: 0;
&:focus-visible {
outline: var(--flex-focus-ring);
outline-offset: var(--flex-focus-offset-inset);
}
}
/* Toggle and clear buttons — positioned inside input */
.flex-combo-box__toggle,
.flex-combo-box__clear {
position: absolute;
inset-block: 0;
display: flex;
align-items: center;
justify-content: center;
inline-size: 2.25rem;
padding: 0;
background: transparent;
border: none;
cursor: pointer;
color: var(--flex-color-text);
}
.flex-combo-box__toggle {
inset-inline-end: 0;
border-inline-start: 1px solid var(--flex-color-ink);
}
.flex-combo-box__clear {
inset-inline-end: 2.25rem;
border-inline-start: 1px solid var(--flex-color-ink);
&[hidden] {
display: none;
}
}
.flex-combo-box__toggle:hover,
.flex-combo-box__clear:hover {
background-color: var(--flex-gray-cool-5);
}
.flex-combo-box__toggle .flex-icon,
.flex-combo-box__clear .flex-icon {
inline-size: 1.25rem;
block-size: 1.25rem;
}
/* Dropdown list */
.flex-combo-box__list {
position: absolute;
inset-block-start: 100%;
inset-inline: 0;
z-index: 100;
max-block-size: 12.1em;
overflow-y: auto;
margin: 0;
padding: 0;
list-style: none;
background-color: var(--flex-color-surface);
border: 1px solid var(--flex-color-ink);
border-block-start: none;
&[hidden] {
display: none;
}
}
/* Options */
.flex-combo-box__option {
padding: 0.5rem;
font-size: var(--flex-text-uswds);
line-height: 1.3;
color: var(--flex-color-text);
cursor: pointer;
&:hover {
background-color: var(--flex-blue-vivid-60);
color: var(--flex-white);
}
&[data-focused] {
background-color: var(--flex-blue-vivid-60);
color: var(--flex-white);
}
&[aria-selected="true"] {
background-color: var(--flex-blue-vivid-60);
color: var(--flex-white);
font-weight: 700;
}
&[hidden] {
display: none;
}
}
/* No results message */
.flex-combo-box__no-results {
padding: 0.5rem;
font-size: var(--flex-text-uswds);
line-height: 1.3;
color: var(--flex-color-text-muted);
font-style: italic;
}
A digital services project by Flexion