0.1.0

Combobox

A combobox combines a text field with a filterable popup list. Use it when users need to search a known set of options. Typed text filters the list; only selecting an option commits a value.

Try it

Cat
Dog
No results found.

Mental model

<rui-combobox> is a behavior host: helpers place the input row and popup; the custom element owns filtering, keyboard, and selection. Options live in an embedded RuiListbox, usually wrapped in RuiAutocomplete. Named regions are Composition Helpers, not HTML slots.

The live value property is always a string[] ([] when nothing is selected). The HTML attribute is those tokens as a comma-separated string. JSX may pass a string or an array; a string is parsed as CSV. rui-change emits { value: string[] }. Commas in the attribute are delimiters.

Important: typing does not create a custom value. Enter with no active option closes the popup. Escape while closed clears the committed value and the input. If only predefined options are valid, that is already the runtime contract — do not expect free-text to be stored on value.

Not form-associated. Wrap in RuiField and read the selected tokens from RuiForm onSubmit. The filter textbox is not listed in new FormData(form).

Usage

Pass options for the default composition, or compose the helpers explicitly. Always set embedded on the listbox inside the popup.

import {
  RuiCombobox,
  RuiComboboxClear,
  RuiComboboxControl,
  RuiComboboxInput,
  RuiComboboxListbox,
  RuiComboboxTrigger,
} from '@ecopages/radiant-ui/combobox';
import { RuiAutocomplete, RuiAutocompleteCollection, RuiAutocompleteEmpty } from '@ecopages/radiant-ui/autocomplete';
import { RuiListbox, RuiListboxOption } from '@ecopages/radiant-ui/listbox';
import { RuiIconX } from '@ecopages/radiant-ui/icons';
 
<RuiCombobox value="cat" placeholder="Choose an animal">
  <RuiComboboxControl>
    <RuiComboboxInput />
    <RuiComboboxClear aria-label="Clear animal selection">
      <RuiIconX />
    </RuiComboboxClear>
    <RuiComboboxTrigger />
  </RuiComboboxControl>
  <RuiComboboxListbox>
    <RuiAutocomplete>
      <RuiAutocompleteCollection>
        <RuiListbox embedded>
          <RuiListboxOption value="cat">Cat</RuiListboxOption>
          <RuiListboxOption value="dog">Dog</RuiListboxOption>
        </RuiListbox>
        <RuiAutocompleteEmpty>No results found.</RuiAutocompleteEmpty>
      </RuiAutocompleteCollection>
    </RuiAutocomplete>
  </RuiComboboxListbox>
</RuiCombobox>

Children on RuiComboboxClear and RuiComboboxTrigger replace the default SVGs. For decorated options, set label on RuiListboxOption so the committed input text stays clean of icons and indicators.

Custom markup

<rui-combobox> coordinates any light-DOM tree that matches its query contract. The Rui* helpers stamp these targets; they are not required.

import '@ecopages/radiant-ui/combobox';
import '@ecopages/radiant-ui/listbox';
 
<rui-combobox value="cat" placeholder="Choose an animal">
  <div data-ref="root" class="rui-combobox">
    <div class="rui-combobox__control">
      <input type="text" data-combobox-input data-autocomplete-input />
      <button type="button" data-combobox-trigger></button>
    </div>
    <div data-combobox-listbox hidden>
      <rui-listbox embedded>
        <div role="option" data-value="cat">Cat</div>
      </rui-listbox>
    </div>
  </div>
</rui-combobox>

Multiple selection

selectionMode="multiple" toggles known options and keeps the popup open. Selected options render as removable chips. Typed text only filters; it is not appended as a token.

CatDog
Cat
Dog
No results found.
<RuiCombobox
  selectionMode="multiple"
  value={['ca', 'ny']}
  options={[
    { value: 'ca', label: 'California' },
    { value: 'ny', label: 'New York' },
  ]}
/>

For composed layouts, place RuiComboboxValue (with a RuiTagGroup) before RuiComboboxInput. Add RuiListboxOptionIndicator inside an option to replace the selected checkmark.

Trigger kind

triggerKind controls what opens the listbox:

ValueOpens when
input (default)The user types a matching query, or uses the trigger / arrow keys
focusThe input receives focus (and while typing)
manualThe trigger button or arrow keys only; typing filters without opening
Cat
Dog
No results found.

Theming

Combobox surfaces map to semantic surface roles, never Tailwind palette steps. Override at the theme layer.

PartCSS roles
Control row (rui-combobox__control)border, background, radius-control; focus-ring on focus-within
Input texton-background, --text-control, --space-control-*
Disabledopacity-muted
Popup (rui-combobox__listbox)popover surface + shadow-overlay (via rui-popover)

Icon swaps are Composition Helper children, not CSS variables.

API

RuiCombobox is a custom element (<rui-combobox>) that coordinates a composed input row and listbox popup. It is not form-associated.

Attributes

AttributeTypeDefaultDescription
valuestringomittedComma-separated selected tokens. Empty selection removes the attribute. The JS property is string[].
labelstring''Accessible name when there is no visible RuiLabel.
placeholderstring''Placeholder text for the input.
disabledbooleanfalseDisable the input and trigger.
selection-modesingle · multiplesingleSingle or multi-select.
should-close-on-selectbooleanWhether selection closes the popup (defaults to true for single, false for multiple).
trigger-kindinput · focus · manualinputControls what opens the listbox.

Light-DOM contract

TargetRequiredHost writesAuthor owns
[data-ref="root"]yes—shell for popover anchoring
[data-combobox-input]yesrole, aria-expanded, aria-controls, id, disabledthe input
[data-combobox-listbox]yeshiddenpopup shell
[role="option"]yes (in listbox)aria-selecteddata-value, label text
[data-combobox-trigger]noaria-expanded, tabIndex="-1"toggle button
[data-combobox-clear]nohidden, disabledclear button
[data-combobox-value]no—chip region before input

Nested hosts: rui-listbox (embedded), rui-tag-group in [data-combobox-value], optional rui-autocomplete.

Events

EventDetailDescription
rui-change{ value: string[] }Fired when an option is selected, a chip is removed, clear runs, or Escape clears a closed field.

View helpers

ComponentTarget stampedNotes
RuiCombobox<rui-combobox> + [data-ref="root"]Accepts options or children
RuiComboboxControl—Input row wrapper
RuiComboboxInput[data-combobox-input]Also [data-autocomplete-input]
RuiComboboxClear[data-combobox-clear]Hidden when empty
RuiComboboxTrigger[data-combobox-trigger]Children replace chevron
RuiComboboxValue[data-combobox-value]Before input in multiple mode
RuiComboboxListbox[data-combobox-listbox]Place embedded RuiListbox inside

CSS classes

ClassDescription
.rui-comboboxRoot surface.
.rui-combobox__controlBordered control-height row.
.rui-combobox__inputCombobox text input.
.rui-combobox__valueSelected-value chip region.
.rui-combobox__listboxPopup shell (adds rui-popover rui-popover--listbox rui-floating).

Theme roles

PartCSS variables consumed
Control row--border, --background, --radius-control, --focus-ring
Input text--on-background, --text-control, --space-control-x, --space-control-y
Disabled--opacity-muted
Popup--shadow-overlay (via rui-popover)

Accessibility

  • The input exposes role="combobox" with aria-expanded reflecting popup state.
  • Multiple mode marks the popup listbox aria-multiselectable="true"; each known option toggles aria-selected.
  • Compose RuiComboboxClear for an explicit clear action; it restores focus to the input.
  • Associate a visible label via RuiLabel or label.