Skip to content

cv-combobox ​

Combobox input with popup listbox, supporting editable and select-only modes, single and multi-select, clearable behavior, and grouped options.

Headless: createCombobox

Cross-Spec Consistency ​

This document is the UIKit surface contract for Combobox.

  • Headless createCombobox is the source of truth for state, transitions, and invariants.
  • UIKit mirrors headless contracts through DOM attributes and events.
  • Any intentional divergence between UIKit and headless MUST be documented in both specs.

Usage ​

View source
html
<div class="combobox-demo-shell" data-demo="combobox" data-live-demo-height="1180">
  <section class="combobox-demo-hero" aria-labelledby="combobox-demo-title">
    <div class="combobox-demo-copy">
      <span class="combobox-demo-kicker">Search and select primitive</span>
      <h3 id="combobox-demo-title">
        Use combobox when a controlled choice also needs filtering, grouping, or tags.
      </h3>
      <p>
        The headless model owns input value, active option, popup state, selection, and ARIA contracts. UIKit
        renders the editable field, select-only trigger, clear control, grouped listbox, and selected tag
        surface.
      </p>
    </div>

    <dl class="combobox-demo-metrics" aria-label="Combobox contract summary">
      <div>
        <dt>Modes</dt>
        <dd>editable / select-only</dd>
      </div>
      <div>
        <dt>Selection</dt>
        <dd>single / multiple / grouped</dd>
      </div>
      <div>
        <dt>Events</dt>
        <dd>input / change / clear</dd>
      </div>
    </dl>
  </section>

  <section class="combobox-demo-board" aria-label="Combobox examples in a vault routing form">
    <form class="combobox-demo-panel" data-combobox-form>
      <div class="combobox-demo-panel-head">
        <div>
          <span>Visible profile routing</span>
          <strong>relay.surface / browser tags / operator handoff</strong>
        </div>
        <cv-badge variant="primary" pill>live contract</cv-badge>
      </div>

      <div class="combobox-demo-field-grid">
        <cv-field required>
          <span slot="label">Visible route</span>
          <cv-combobox
            data-combobox-primary
            clearable
            value="relay"
            input-value="Relay endpoint"
            placeholder="Search routes"
            aria-label="Visible route"
          >
            <span slot="prefix" aria-hidden="true">route</span>
            <cv-combobox-option value="relay">Relay endpoint</cv-combobox-option>
            <cv-combobox-option value="gateway">Gateway unlock</cv-combobox-option>
            <cv-combobox-option value="import">Credential import</cv-combobox-option>
            <cv-combobox-option value="archive" disabled>Archive export disabled</cv-combobox-option>
          </cv-combobox>
          <span slot="description"
            >Editable mode filters options while keeping the committed selection explicit.</span
          >
        </cv-field>

        <cv-field>
          <span slot="label">Layer tags</span>
          <cv-combobox
            multiple
            clearable
            max-tags-visible="2"
            value="browser otp recovery"
            placeholder="Add tags"
            aria-label="Layer tags"
          >
            <cv-combobox-option value="browser">Browser</cv-combobox-option>
            <cv-combobox-option value="otp">OTP</cv-combobox-option>
            <cv-combobox-option value="recovery">Recovery</cv-combobox-option>
            <cv-combobox-option value="shared">Shared vault</cv-combobox-option>
          </cv-combobox>
          <span slot="description">Multiple selection renders tags and keeps the popup open by default.</span>
        </cv-field>

        <cv-field>
          <span slot="label">Operator</span>
          <cv-combobox type="select-only" value="alex" placeholder="Assign operator" aria-label="Operator">
            <cv-combobox-option value="alex">Alex - security review</cv-combobox-option>
            <cv-combobox-option value="maria">Maria - legal hold</cv-combobox-option>
            <cv-combobox-option value="ops">Ops queue</cv-combobox-option>
          </cv-combobox>
          <span slot="description"
            >Select-only mode follows the combobox trigger pattern without text filtering.</span
          >
        </cv-field>

        <cv-field>
          <span slot="label">Jurisdiction</span>
          <cv-combobox placeholder="Filter jurisdiction" aria-label="Jurisdiction">
            <cv-combobox-group label="Low-friction review">
              <cv-combobox-option value="helsinki">Helsinki</cv-combobox-option>
              <cv-combobox-option value="reykjavik">Reykjavik</cv-combobox-option>
            </cv-combobox-group>
            <cv-combobox-group label="Manual approval">
              <cv-combobox-option value="border">Border crossing</cv-combobox-option>
              <cv-combobox-option value="seizure">Device seizure</cv-combobox-option>
            </cv-combobox-group>
          </cv-combobox>
          <span slot="description"
            >Groups keep navigation flat while preserving labeled listbox structure.</span
          >
        </cv-field>
      </div>
    </form>

    <aside class="combobox-demo-side" aria-label="Combobox event output">
      <div class="combobox-demo-side-head">
        <span class="combobox-demo-kicker">Event stream</span>
        <h4>Interact with any combobox to inspect the public state emitted by the component.</h4>
      </div>

      <p class="combobox-demo-log" role="status" aria-live="polite" data-combobox-output>
        Waiting for interaction. Type, pick an option, toggle a tag, or clear a value.
      </p>

      <dl class="combobox-demo-live" aria-label="Live combobox state">
        <div>
          <dt>Selection</dt>
          <dd data-combobox-selected>relay</dd>
        </div>
        <div>
          <dt>Input</dt>
          <dd data-combobox-input>Relay endpoint</dd>
        </div>
        <div>
          <dt>Popup</dt>
          <dd data-combobox-open>closed</dd>
        </div>
      </dl>

      <div class="combobox-demo-open-card">
        <span>Open grouped listbox</span>
        <cv-combobox
          open
          type="select-only"
          value="work"
          placeholder="Select vault"
          aria-label="Open vault selector"
        >
          <cv-combobox-group label="Vaults">
            <cv-combobox-option value="personal">Personal vault</cv-combobox-option>
            <cv-combobox-option value="work">Work vault</cv-combobox-option>
            <cv-combobox-option value="decoy">Visible decoy vault</cv-combobox-option>
          </cv-combobox-group>
        </cv-combobox>
      </div>
    </aside>
  </section>

  <section class="combobox-demo-section" aria-labelledby="combobox-demo-matrix-title">
    <div class="combobox-demo-section-header">
      <span class="combobox-demo-kicker">State matrix</span>
      <h4 id="combobox-demo-matrix-title">
        One component covers typed filtering, trigger-only selection, tag overflow, invalid input, grouping,
        and sizing.
      </h4>
    </div>

    <div class="combobox-demo-matrix" aria-label="Combobox state matrix">
      <div>
        <span>Editable filter</span>
        <cv-combobox input-value="gate" placeholder="Type to filter" aria-label="Editable filter">
          <cv-combobox-option value="gateway">Gateway</cv-combobox-option>
          <cv-combobox-option value="gatekeeper">Gatekeeper</cv-combobox-option>
          <cv-combobox-option value="relay">Relay</cv-combobox-option>
        </cv-combobox>
      </div>

      <div>
        <span>Select-only</span>
        <cv-combobox type="select-only" value="hardware" placeholder="Choose boundary" aria-label="Boundary">
          <cv-combobox-option value="software">Software only</cv-combobox-option>
          <cv-combobox-option value="hardware">Hardware-assisted</cv-combobox-option>
        </cv-combobox>
      </div>

      <div>
        <span>Tag overflow</span>
        <cv-combobox
          multiple
          clearable
          max-tags-visible="1"
          value="legal source hardware"
          placeholder="Tags"
          aria-label="Tag overflow"
        >
          <cv-combobox-option value="legal">Legal</cv-combobox-option>
          <cv-combobox-option value="source">Source</cv-combobox-option>
          <cv-combobox-option value="hardware">Hardware</cv-combobox-option>
        </cv-combobox>
      </div>

      <div>
        <span>Invalid</span>
        <cv-field invalid>
          <span slot="label">Policy route</span>
          <cv-combobox
            invalid
            input-value="unknown relay"
            placeholder="Search route"
            aria-label="Invalid route"
          >
            <cv-combobox-option value="relay">Relay endpoint</cv-combobox-option>
          </cv-combobox>
          <span slot="error">Route is not available in this visible profile.</span>
        </cv-field>
      </div>

      <div>
        <span>Disabled option</span>
        <cv-combobox value="active" placeholder="Choose state" aria-label="Disabled option">
          <cv-combobox-option value="active">Active</cv-combobox-option>
          <cv-combobox-option value="archived" disabled>Archived policy</cv-combobox-option>
        </cv-combobox>
      </div>

      <div>
        <span>Sizes</span>
        <cv-combobox size="small" value="s" placeholder="Small" aria-label="Small combobox">
          <cv-combobox-option value="s">Small</cv-combobox-option>
          <cv-combobox-option value="m">Medium</cv-combobox-option>
        </cv-combobox>
        <cv-combobox size="large" value="l" placeholder="Large" aria-label="Large combobox">
          <cv-combobox-option value="m">Medium</cv-combobox-option>
          <cv-combobox-option value="l">Large</cv-combobox-option>
        </cv-combobox>
      </div>
    </div>
  </section>
</div>

<script type="module">
  document
    .querySelectorAll('.combobox-demo-shell[data-demo="combobox"]:not([data-ready])')
    .forEach((shell) => {
      shell.dataset.ready = 'true'

      const form = shell.querySelector('[data-combobox-form]')
      const output = shell.querySelector('[data-combobox-output]')
      const selected = shell.querySelector('[data-combobox-selected]')
      const input = shell.querySelector('[data-combobox-input]')
      const popup = shell.querySelector('[data-combobox-open]')

      const eventLabels = {
        'cv-input': 'input',
        'cv-change': 'change',
        'cv-clear': 'clear',
      }

      const getFieldName = (combobox) => {
        const label = combobox.closest('cv-field')?.querySelector('[slot="label"]')?.textContent?.trim()
        return label || combobox.getAttribute('aria-label') || 'combobox'
      }

      const readState = (combobox, detail = {}) => {
        const selectedIds = Array.isArray(detail.selectedIds) ? detail.selectedIds : []
        const value = typeof detail.value === 'string' ? detail.value : combobox.value || ''
        const inputValue =
          typeof detail.inputValue === 'string' ? detail.inputValue : combobox.inputValue || ''
        const open = typeof detail.open === 'boolean' ? detail.open : combobox.open

        return {
          selection: selectedIds.length > 0 ? selectedIds.join(', ') : value || 'none',
          inputValue: inputValue || 'empty',
          popup: open ? 'open' : 'closed',
        }
      }

      const updateState = (combobox, detail = {}) => {
        const state = readState(combobox, detail)

        if (selected) selected.textContent = state.selection
        if (input) input.textContent = state.inputValue
        if (popup) popup.textContent = state.popup

        shell.dataset.comboboxState =
          state.popup === 'open' ? 'open' : state.selection === 'none' ? 'idle' : 'selected'
        return state
      }

      const report = (event) => {
        const combobox = event.target instanceof HTMLElement ? event.target : null
        if (!combobox?.matches('cv-combobox')) return

        const state = updateState(combobox, event.detail)
        const name = getFieldName(combobox)
        const label = eventLabels[event.type] ?? event.type

        if (output) {
          output.textContent = `${label}: ${name} -> ${state.selection}; input ${state.inputValue}; popup ${state.popup}`
        }
      }

      form?.addEventListener('submit', (event) => {
        event.preventDefault()
        if (output) output.textContent = 'submit: form boundary reached without changing combobox state.'
      })

      Object.keys(eventLabels).forEach((eventName) => {
        shell.addEventListener(eventName, report)
      })
    })
</script>

Anatomy ​

Editable mode (default) ​

<cv-combobox> (host)
└── <div part="base">
    ├── <div part="input-wrapper">
    │   ├── <div part="tags">                    ← only when [multiple], contains selected tags
    │   │   ├── <span part="tag">                ← one per selected item (up to max-tags-visible)
    │   │   │   ├── <span part="tag-label">
    │   │   │   └── <button part="tag-remove">
    │   │   └── <span part="tag-overflow">       ← "+N more" when overflow
    │   ├── <input part="input" role="combobox">
    │   ├── <button part="clear-button">         ← only when [clearable] and value is present
    │   └── <span part="expand-icon">
    └── <div part="listbox" role="listbox">
        ├── <div part="group" role="group">      ← one per cv-combobox-group
        │   ├── <div part="group-label" role="presentation">
        │   └── <slot>                           ← accepts <cv-combobox-option> within group
        └── <slot>                               ← accepts <cv-combobox-option> (ungrouped)

Select-only mode ​

<cv-combobox type="select-only"> (host)
└── <div part="base">
    ├── <div part="input-wrapper">
    │   ├── <div part="tags">                    ← only when [multiple]
    │   │   └── (same tag structure as editable)
    │   ├── <div part="trigger" role="combobox"> ← replaces <input> in select-only
    │   │   └── <span part="label">             ← selected value text or placeholder
    │   ├── <button part="clear-button">         ← only when [clearable] and value is present
    │   └── <span part="expand-icon">
    └── <div part="listbox" role="listbox">
        └── (same listbox structure as editable)

Attributes ​

AttributeTypeDefaultDescription
valueString""Selected option id. In multi mode, space-delimited string of selected option values.
input-valueString""Editable input text. Read-only in select-only mode.
openBooleanfalsePopup open state
typeString"editable"Combobox mode: "editable" | "select-only"
multipleBooleanfalseEnables multi-select behavior
clearableBooleanfalseShows clear button when a value is selected
max-tags-visibleNumber3Maximum tags shown before "+N more" overflow. 0 = unlimited. Only meaningful when multiple is true.
open-on-focusBooleantrueOpens popup when input receives focus
open-on-clickBooleantrueOpens popup on input/trigger click when closed
close-on-selectBooleantrue (single) / false (multi)Closes popup after selection commit. Default depends on multiple.
match-modeString"includes"Default filter mode: includes | startsWith. Ignored in select-only mode.
placeholderString""Placeholder text for input or trigger
disabledBooleanfalsePrevents interaction
sizeString"medium"Size: small | medium | large
aria-labelString""Accessible label for input/listbox

Sizes ​

Size--cv-combobox-min-height--cv-combobox-padding-inline
small30pxvar(--cv-space-2, 8px)
medium36pxvar(--cv-space-3, 12px)
large42pxvar(--cv-space-4, 16px)

Slots ​

SlotDescription
(default)One or more <cv-combobox-option> or <cv-combobox-group> children
prefixIcon or element before the input/trigger
suffixIcon or element after the input/trigger (before expand icon)

CSS Parts ​

PartElementDescription
base<div>Root layout container
input-wrapper<div>Wrapper around input/trigger, tags, clear button, and expand icon
cv-input<input>Editable combobox control (editable mode only)
trigger<div>Button-like trigger control (select-only mode only)
label<span>Selected value text inside trigger (select-only mode)
listbox<div>Popup listbox container
tags<div>Container for selected item tags (multi-select only)
tag<span>Individual selected item tag (multi-select only)
tag-label<span>Text label inside a tag
tag-remove<button>Remove button inside a tag
tag-overflow<span>"+N more" overflow indicator
clear-button<button>Clear selection button (clearable mode only)
expand-icon<span>Dropdown expand/collapse indicator icon
group<div>Option group container inside the listbox
group-label<div>Group header label inside the listbox
prefix<span>Wrapper around the prefix slot
suffix<span>Wrapper around the suffix slot

CSS Custom Properties ​

PropertyDefaultDescription
--cv-combobox-min-width260pxMinimum inline size of the host
--cv-combobox-min-height36pxMinimum block size of the input/trigger
--cv-combobox-padding-inlinevar(--cv-space-3, 12px)Horizontal padding of the input/trigger
--cv-combobox-listbox-max-block-size220pxPreferred maximum block size of the listbox popup
--cv-combobox-listbox-min-fit-block-size12remFit-content minimum used by supported calc-size() sizing
--cv-combobox-listbox-viewport-block-gutter32pxReserved viewport block space before popup scrolling
--cv-combobox-max-height220pxLegacy alias for --cv-combobox-listbox-max-block-size
--cv-combobox-border-colorvar(--cv-color-border, #2a3245)Border color for input/trigger and listbox
--cv-combobox-border-radiusvar(--cv-radius-sm, 6px)Border radius of the input/trigger
--cv-combobox-backgroundvar(--cv-color-surface)Background of the input/trigger
--cv-combobox-listbox-radiusvar(--cv-radius-md, 10px)Border radius of the listbox popup
--cv-combobox-gapvar(--cv-space-1, 4px)Gap between base layout sections
--cv-combobox-tag-gapvar(--cv-space-1, 4px)Gap between tags in multi-select
--cv-combobox-tag-radiusvar(--cv-radius-sm, 6px)Border radius of tag chips
--cv-combobox-font-sizeinheritFont size of the input/trigger text

Visual States ​

Host selectorDescription
:host([disabled])Reduced opacity (0.55), cursor: not-allowed
:host([open])Popup listbox is visible
:host([type="select-only"])Trigger is a button-like element instead of an input
:host([multiple])Multi-select mode with tag chips
:host([clearable])Clear button may be shown
:host([size="small"])Small size overrides
:host([size="large"])Large size overrides

ARIA Contract ​

Editable mode ​

  • Input role is combobox
  • Input exposes aria-haspopup="listbox", aria-expanded, aria-controls, aria-autocomplete="list"
  • When popup is open and active option exists, input exposes aria-activedescendant

Select-only mode ​

  • Trigger role is combobox
  • Trigger exposes aria-haspopup="listbox", aria-expanded, aria-controls
  • aria-autocomplete is not present (no text input)
  • When popup is open and active option exists, trigger exposes aria-activedescendant

Common ​

  • Popup role is listbox
  • Options use role option
  • When multiple=true, listbox exposes aria-multiselectable="true"
  • Each selected option exposes aria-selected="true" (all selected in multi mode, not just one)
  • Option groups use role="group" with aria-labelledby pointing to the group label element
  • Group label elements use role="presentation"

All ARIA attributes are derived from headless contracts (getInputProps, getListboxProps, getOptionProps, getGroupProps, getGroupLabelProps). UIKit does not compute ARIA state independently.

Events ​

EventDetailDescription
cv-input{value: string | null, inputValue: string, activeId: string | null, open: boolean, selectedIds: string[]}Fires when combobox interaction changes observable state
cv-change{value: string | null, inputValue: string, activeId: string | null, open: boolean, selectedIds: string[]}Fires when selected option(s) change
cv-clear{}Fires when the clear button is clicked

In multi mode, cv-input fires on each toggle and cv-change fires on each toggle (since every toggle changes selection). The selectedIds array in the detail reflects all currently selected option ids.

Reactive State Mapping ​

cv-combobox is a visual adapter over headless createCombobox reactive state.

Attribute to Headless (UIKit -> Headless) ​

UIKit PropertyDirectionHeadless Binding
valueattr -> actionactions.select(id) / actions.clearSelection(). In multi mode, parsed as space-delimited ids.
input-valueattr -> actionactions.setInputValue(value)
openattr -> actionactions.open() / actions.close()
typeattr -> optionpassed as type in createCombobox(options)
multipleattr -> optionpassed as multiple in createCombobox(options)
clearableattr -> optionpassed as clearable in createCombobox(options)
close-on-selectattr -> optionpassed as closeOnSelect in createCombobox(options)
match-modeattr -> optionpassed as matchMode in createCombobox(options)
aria-labelattr -> optionpassed as ariaLabel in createCombobox(options)

Headless to DOM (Headless -> UIKit) ​

Headless StateDirectionDOM Reflection
state.selectedId()state -> attr[value] host attribute (single mode)
state.selectedIds()state -> attr[value] host attribute as space-delimited string (multi mode)
state.inputValue()state -> attr[input-value] host attribute
state.isOpen()state -> attr[open] host attribute
state.activeId()state -> renderaria-activedescendant on input/trigger
state.hasSelection()state -> renderclear button visibility
state.type()state -> renderdetermines input vs trigger rendering
state.multiple()state -> renderdetermines tag rendering

Contract Spreading ​

  • contracts.getInputProps() is spread onto [part="input"] (editable) or [part="trigger"] (select-only) -- applies role, aria-haspopup, aria-expanded, aria-controls, aria-autocomplete (editable only), aria-activedescendant, aria-label
  • contracts.getListboxProps() is spread onto [part="listbox"] -- applies role, tabindex, aria-label, aria-multiselectable (multi only)
  • contracts.getOptionProps(id) is spread onto each cv-combobox-option -- applies role, tabindex, aria-selected, aria-disabled, data-active
  • contracts.getGroupProps(groupId) is spread onto each [part="group"] -- applies role, aria-labelledby
  • contracts.getGroupLabelProps(groupId) is spread onto each [part="group-label"] -- applies id, role
  • contracts.getVisibleOptions() drives option/group visibility (supports grouped structure; empty groups are hidden)
  • contracts.getFlatVisibleOptions() available for navigation index calculations

UIKit-Only Concerns (NOT in headless) ​

  • Tag/chip rendering for multi-select selected items
  • "+N more" overflow display for multi-select (controlled by max-tags-visible)
  • Clear button rendering and visibility (uses state.hasSelection() + clearable attribute)
  • Select-only trigger visual (button-like with selected label + expand icon)
  • Option group visual styling (indentation, group header)
  • Popup positioning and animation
  • cv-clear event dispatch
  • Size variants (small / medium / large)

Behavioral Contract ​

Editable Mode (default) ​

  • Text input updates input-value, opens popup, and filters visible options
  • Focus opens popup only when open-on-focus=true
  • Input click opens popup only when open-on-click=true
  • Arrow/Home/End navigation follows headless combobox behavior
  • Enter commits active option (value, input-value, popup closes only when close-on-select=true)
  • Escape closes popup without clearing committed selection
  • Clicking outside closes popup
  • match-mode="startsWith" uses case-insensitive starts-with filtering
  • Slot changes rebuild model while preserving still-valid selected value

Select-Only Mode ​

  • input-value is not user-editable; setInputValue is a no-op in headless
  • Trigger displays the selected option's label (or placeholder when no selection)
  • Keyboard when closed: Space/Enter opens popup; ArrowDown/ArrowUp opens and activates first/last option
  • Keyboard when open: ArrowDown/ArrowUp navigate; Enter/Space commit active option; Escape closes; Home/End navigate to first/last
  • Type-to-select via printable characters: typeahead jumps to matching option by label prefix
  • Filtering is disabled; all non-disabled options are always visible

Multi-Select ​

  • commitActive toggles the active option in selectedIds instead of replacing selection
  • select(id) toggles the option instead of replacing
  • Listbox stays open after each selection (default close-on-select=false)
  • input-value is NOT overwritten on commit (it drives filtering in editable multi mode)
  • In select-only multi mode, inputValue is always "" (trigger shows tags instead)
  • Tags/chips are rendered inside [part="tags"] for each selected item
  • When selectedIds.length > max-tags-visible, overflow shows "+N more" in [part="tag-overflow"]
  • Clicking [part="tag-remove"] calls actions.removeSelected(id)
  • value attribute reflects all selected ids as a space-delimited string

Clearable ​

  • Clear button [part="clear-button"] is visible when clearable=true and state.hasSelection() is true
  • Clicking the clear button calls actions.clear() (resets both selection and input value)
  • cv-clear event is dispatched when the clear button is clicked

Option Groups ​

  • <cv-combobox-group label="Name"> wraps <cv-combobox-option> children into a visual group
  • Groups are rendered as [part="group"] with role="group" and aria-labelledby pointing to [part="group-label"]
  • Groups with all options filtered out are hidden
  • Navigation crosses group boundaries seamlessly (headless handles this via flat visible options)

Disabled State ​

  • When disabled=true, the combobox is non-interactive: input/trigger cannot be focused, popup cannot open, clear/tag-remove buttons are inert

Optional Advanced Behaviors (Future Scope) ​

These behaviors are optional and currently not required on cv-combobox:

  • free-text/custom value commit when no option is active
  • async option loading
  • inline autocomplete completion rendering

Child Elements ​

cv-combobox-option ​

Individual option within a combobox. The parent cv-combobox manages all ARIA attributes on this element via headless contracts.

Anatomy ​

<cv-combobox-option> (host)
└── <div part="base">
    └── <slot>

Attributes ​

AttributeTypeDefaultDescription
valueString""Unique identifier for this option. Auto-generated as option-{n} if omitted.
disabledBooleanfalseWhether the option is disabled
selectedBooleanfalseWhether the option is selected. Managed by parent.
activeBooleanfalseWhether the option is the active (highlighted) option. Managed by parent.

Slots ​

SlotDescription
(default)Option label content

CSS Parts ​

PartElementDescription
base<div>Root wrapper for the option content

Visual States ​

Host selectorDescription
:host([selected])Option is currently selected
:host([active]) / :host([data-active="true"])Option is the active (highlighted) option
:host([disabled])Option is disabled
:host([hidden])Option is filtered out or popup is closed

cv-combobox-group ​

Groups related options under a labeled header. Must be a direct child of cv-combobox.

Anatomy ​

<cv-combobox-group> (host)
└── <slot>           ← accepts <cv-combobox-option> children

Attributes ​

AttributeTypeDefaultDescription
labelString""Visible group header text. Also used for aria-labelledby linkage via headless getGroupLabelProps.

Slots ​

SlotDescription
(default)One or more <cv-combobox-option> children

Visual States ​

Host selectorDescription
:host([hidden])All options in this group are filtered out

ChromVoid UIKit documentation