Combobox
A text field that filters a list of options as the user types and lets them pick one. Use when there are too many options to scan in a plain list. Don't use for short lists (use Select or Radio) or when more than one choice is allowed (use Multi-Select).
Description
What it does
Combobox combines a text field with a list of options. As the user types, the list narrows to the matches, and they choose one with the keyboard or pointer.
Where it appears
Choosing a council area, organisation or stakeholder from a long list, picking a country, and any single-choice field where the user already knows roughly what they are looking for.
Why it exists
A long list is slow to scroll and hard to use with a screen reader. Typing to narrow it is faster for everyone. Combobox is a separate component from Select because the behaviour differs: the user types, and the list is announced as it changes.
Dependencies
Input, Popover for the floating behaviour, and the option rows shared with Select. For several choices use Multi-Select.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Label | Required | Names the field. Always visible, and linked to the input. A required marker can follow it. |
| Field | Required | The text input where the user types. Same size, border and fill as Input. |
| Clear button | Optional | Empties the field and the current choice. Shown only when there is something to clear. |
| Toggle button | Required | The chevron that opens and closes the list for people who don't want to type. Turns to point up while the list is open. |
| List | Required | The floating panel of options under the field. Same surface as Select and Dropdown. Scrolls once it reaches its maximum height. |
| Option | Required | One choice. Shows a highlight when focused or hovered, and a tick when it is the selected choice. |
| Section heading | Optional | Groups related options, for example by region. |
| Match highlight | Optional | The part of each option that matches what was typed is shown in a heavier weight. |
| No results message | Required | Replaces the options when nothing matches, so the list never just goes blank. |
| Loading message | Optional | A spinner and a short message while options are fetched from a server. |
| Hint and error | Optional | Helper text below the field, or the error message when the field is invalid. |
Variants
Outlined
White field with a visible border
Default appearance. Use on plain page backgrounds.
Filled-outlined
Tinted field with a visible border
Use on forms that sit on a white or raised surface, where a tinted field gives the field more definition.
Options list
Options, with optional section headings, a tick on the selected option and match highlighting
The normal open state when the typed text matches one or more options.
No results list
A single message in place of the options
When nothing matches. Say what happened in plain words, for example "No results found".
Loading list
A spinner and a message in place of the options
While options are being fetched. Keep the message short.
States
| State | Behaviour |
|---|---|
| Default | Shows the placeholder or the current choice, with the toggle button at the right. |
| Hover | A light overlay appears over the field. The border doesn't change. |
| Focused | A focus ring appears around the field. The list opens when the user types or activates the toggle. |
| Open | The list is shown under the field and the chevron points up. Keyboard focus stays in the field; the highlighted option is announced to screen readers. |
| Invalid | The border takes the invalid colour and an error message appears below. The message names the problem and says how to fix it. |
| Disabled | Greyed out and removed from the tab order. It can't be opened or typed in. |
Usage guidelines
When to use
- The list has roughly seven or more options and the user can type part of what they want.
- Options come from a large or changing source and have to be searched or loaded.
- Only one choice is allowed.
When not to use
- Fewer than about seven options — use Select or Radio buttons, which are quicker.
- More than one choice is allowed — use Multi-Select.
- The user is searching for records, not choosing a value for a field — use Search.
- Any text is acceptable — use Input. A Combobox only accepts one of the listed options.
Do / Don't
Do
Show a clear message when nothing matches ("No results found") and offer a way forward if there is one.
Don't
Don't let the list close or go blank with no explanation. Screen reader users get no signal that anything happened.
Do
Label the field with what the user is choosing ("Council area"), and keep options short so matches are easy to scan.
Don't
Don't use match highlighting as the only cue. It supports scanning but must not carry meaning on its own.
Do
Put the most likely options first, and use section headings only when they genuinely help the user find things.
Don't
Don't hide the toggle button. Some people prefer to open the list and browse it.
Layout & Spacing
Combobox is built from three shared sets of tokens, plus a small set of its own. The field is the same as Input, and the list is the same as Select.
| Part | Tokens used |
|---|---|
| Field: height, padding, corner radius, border, fill, text, placeholder, hover overlay, focus ring, hint and error | --orbit-form-control-* (the same tokens as Input and Select) |
| List: surface, border, corner radius, padding, shadow | --orbit-menu-* |
| Options and headings: size, padding, text, highlight, divider | --orbit-menu-size-*, --orbit-menu-item-*, --orbit-menu-heading-* |
| Tick on the selected option | --orbit-menu-item-selected-icon |
| Anything specific to Combobox | --orbit-combobox-* |
| Element | Spec |
|---|---|
| Field height | 24 / 32 / 40 / 48 / 56px for XS / S / M / L / XL. M is the default. --orbit-form-control-size-*-height |
| Field text | 12 / 14 / 16 / 16 / 20px for XS / S / M / L / XL. --orbit-form-control-size-*-font-size |
| List maximum height | 320px; longer lists scroll. --orbit-combobox-popup-max-height |
| Match highlight weight | Semibold (600) on the matching letters. --orbit-combobox-match-font-weight |
| No results text | Quiet text colour. --orbit-combobox-empty-text |
| Loading spinner | Quiet icon colour, the same size as the option text. --orbit-combobox-loading-icon |
| Chevron turn and list open | Fast duration, standard easing. --orbit-combobox-transition-duration, -easing |
Component tokens
These are the variables that belong to Combobox. The field and list tokens are documented on Input, Select and Popover.
| Token | What it sets |
|---|---|
--orbit-combobox-popup-max-height |
Maximum height of the open list (320px). |
--orbit-combobox-empty-text |
Colour of the "No results" message. |
--orbit-combobox-loading-icon |
Colour of the loading spinner. |
--orbit-combobox-match-font-weight |
Weight of the matched letters in each option. |
| `--orbit-combobox-transition-{duration | easing}` |
--orbit-menu-item-selected-icon |
Colour of the tick on the selected option. Shared with Select and Dropdown. |
| Element | Token |
|---|---|
| Field fill (outlined) | --orbit-form-control-outlined-background |
| List surface | --orbit-menu-background |
| Selected tick | --orbit-menu-item-selected-icon |
| No results text | --orbit-combobox-empty-text |
Engineering notes
- In the React Aria trial, Combobox is
ComboBoxwith anInput, aButtonfor the chevron, aPopoverand aListBoxofListBoxItemrows. Each part takes one group of tokens: the input and label use--orbit-form-control-*, the popover and list use--orbit-menu-*, and the extras use--orbit-combobox-*. Treat this mapping as a proposal until Engineering confirms it in the trial. - React Aria marks hovered, focused, selected, disabled and invalid parts with state attributes you can style, so each state in the Figma file maps to one rule. Confirm the attribute names against the version used in the trial.
- Show the no-results message from the list's empty state rather than closing the popover, so the message is announced.
- Set the list's maximum height from
--orbit-combobox-popup-max-heightand let the list scroll inside it. Keep the page itself from scrolling when the list is open. - Match highlighting should wrap the matched letters in an element styled with
--orbit-combobox-match-font-weight. Don't change the option's text; screen readers should hear the full option. - The Popover for the list uses the menu surface so Combobox looks the same as Select. Don't switch it to the popover tokens.
Keyboard interaction
| Key | Action |
|---|---|
| Down arrow | Opens the list if it is closed and moves the highlight to the next option. Focus stays in the field. |
| Up arrow | Moves the highlight to the previous option. |
| Enter | Chooses the highlighted option and closes the list. |
| Escape | Closes the list. Pressing it again with the list closed clears the field. |
| Home or End | Moves the text cursor within the field, as in any text input. |
| Tab | Moves to the next control. If an option is highlighted it can be accepted first, but never choose an option the user hasn't acted on. |
Why it matters
A combobox changes what's on the page as the user types. Screen reader users can't see the list, so the field has to announce that a list exists, which option is highlighted, and how many results there are. Without this, a long list is harder to use than a plain Select.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
role="combobox" | On the input. Tells assistive technology this field controls a popup list. Libraries such as React Aria add it for you. | <input role="combobox" aria-expanded="true" aria-controls="country-list" aria-autocomplete="list" /> |
aria-expanded | On the input. true while the list is open, false when it is closed. | <input role="combobox" aria-expanded="false" /> |
aria-controls | On the input. Points to the id of the list. | <input role="combobox" aria-controls="country-list" /> |
aria-activedescendant | On the input. Points to the id of the highlighted option, so focus can stay in the field while the option is announced. | <input role="combobox" aria-activedescendant="option-canada" /> |
role="listbox" and role="option" | On the list and its rows. The selected row has aria-selected="true". Disabled rows have aria-disabled="true". | <ul role="listbox" id="country-list"><li role="option" id="option-canada" aria-selected="true">Canada</li></ul> |
aria-live | On a visually hidden status element that announces the number of results, for example "5 results available" or "No results found". Needed so a change in the list is heard (WCAG 4.1.3). | <div role="status" aria-live="polite">5 results available</div> |
How to apply it
Keep keyboard focus in the field the whole time. The list is operated with the arrow keys, and the highlighted option is announced through aria-activedescendant. Never move focus into the list.
Contrast
Option text, the No results message and the placeholder meet 4.5:1 against the list and field surfaces in Light and Dark. The selected tick, the loading spinner, the chevron and the field border meet 3:1 against their surfaces (WCAG 1.4.11). The token checks in the repository enforce these.
Things to avoid
- Don't rely on match highlighting, colour or the tick alone to show state. The tick has an accessible name through
aria-selected. - Don't open the list on page load, and don't open it when the field only receives focus unless the user asked for that.
- Don't accept text that isn't in the list. A Combobox returns one of its options, or nothing.
- Don't remove the toggle button to save space. Some people can't type easily.