components / combobox

Combobox

Filterable field-and-list control for faster option discovery when scanning a full menu is too heavy.

Overview

What it is

This section explains the intent of the component before the implementation details.

  • Combobox extends the DK field shell into a filterable selection surface for moderate option sets.
  • Use it when users benefit from narrowing a list by typing rather than scanning a full static menu.

Aliases

autocomplete, filterable select

Decision guide

How to choose it

Use these notes to decide quickly whether this is the right DK component for the job.

Decision guide

  • Choose Combobox when the interaction should feel deliberate, documented, and reusable across product surfaces.
  • If the scenario is more specialized than the current Combobox contract, prefer a simpler component or build a dedicated workflow on top of the lower-level DK primitives.

Do

  • Keep the Combobox label, value, or status language direct enough to scan quickly.
  • Use the documented size and state recipes instead of inventing one-off variants in product code.

Do not

  • Do not hide the main purpose of Combobox behind decorative copy or overloaded surface treatments.
  • Do not stretch Combobox into a workflow it was not designed to handle just because the visuals are close.

Usage

When to use

Prefer these situations when choosing this component.

  • Use Combobox when the option set is long enough that filtering is faster than scanning.
  • Use it when people may know part of the label but not the exact final option.

Usage

When not to use

These patterns are better served by a different component or a simpler surface.

  • Do not use Combobox when the option set is tiny and a Select is easier.
  • Do not use it for async search or multi-select in this v1 contract.

Anti-patterns

  • Using Combobox as a generic layout container instead of a purpose-built interaction or content surface.
  • Forking the documented recipe in product code instead of extending the shared DK contract.

Migration notes

  • Migrate legacy combobox usage by mapping existing variants and states onto the documented DK props before porting any custom styling.
  • Treat the docs examples as the reference contract for accessibility and event behavior during adoption.

Anatomy

Structural parts

The anatomy explains which pieces matter to the recipe and accessibility model.

field wrapper

root

Provides label, description, and shell treatment.

combobox input

input

Receives the typed filter text and current selection label.

anchored options surface

listbox

Shows the filtered option set.

list option

option

Selectable option row with label and optional description.

API

Public interface

The docs contract distinguishes props, DOM events, and slots so integration behavior is explicit.

Props

NameTypeDefaultDescription
labelstringVisible label used for the field or control.
descriptionstringSupporting helper copy rendered below or beside the main label.
errorstringValidation or problem copy rendered in the error slot when present.
placeholderstringPlaceholder text shown before a user makes a selection or enters text.
requiredbooleanfalseMarks the field or control as required for form semantics and docs examples.
disabledbooleanfalseDisables interaction and exposes the correct non-interactive semantics for the control.
namestringForm field name used for native form submission.
idstringOptional id used to bind external labels or descriptions to the component.
size'sm' | 'md' | 'lg''md'Chooses the compiled size recipe for the component.
itemsArray<{ value: string; label: string; description?: string; disabled?: boolean }>Available options rendered inside the listbox.
valuestringControlled selected value.
onChange(detail: { value: string | undefined }) => voidCallback fired when a selection is committed.
themeThemeContractOverrides the compiled DK theme used to resolve tokens and recipes for this component.

Events

NamePayloadDescription
change{ value: string | undefined }Fires when the component commits a new value.

Slots

NameDescription
defaultCombobox is data-driven in v1 and does not expose option slots.

Recipes

Variants, sizes, and states

These notes summarize the intended recipe surface rather than exposing raw implementation detail first.

Variants

  • default: Combobox uses one searchable shell and varies by size.

Sizes

  • sm: Compact searchable field.
  • md: Default searchable field.
  • lg: Larger searchable field.

States

  • closed: Input focused or idle without the list surface visible.
  • open: Anchored listbox is visible.
  • highlighted: One option is active for keyboard navigation.
  • selected: A value has been committed into the field.

Accessibility

Accessibility contract

This is the behavior the component promises to assistive tech and keyboard users today.

Semantics

  • Uses standard field labeling through visible labels, descriptions, and error text.
  • Exposes disabled and required semantics through the underlying form control.
  • Uses combobox and listbox semantics through the searchable input and anchored option surface.

Keyboard

  • Supports native focus and text entry behavior.
  • Arrow keys move through options when the listbox is open.
  • Enter commits the highlighted option and Escape closes the listbox.

Screen readers

  • Connects visible label, helper text, and error text to the native control.
  • The current value, available options, and highlight movement are exposed through the combobox/listbox relationship.

Checklist

  • Verify the visible label or title still produces a clear accessible name.
  • Verify focus remains obvious in every supported state and size.
  • Verify disabled, selected, and error states do not rely on color alone.

Implementation

Tokens and slot vars

This section shows the representative compiled slot variables that the runtime consumes for the selected design system.

root

--dk-field-stack-gap
0.45rem
--dk-motion-duration
200ms
--dk-combobox-offset
8px

label

--dk-field-label-color
#16181c
--dk-field-label-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-field-label-weight
600

input

--dk-combobox-input-bg
#f2f5fb
--dk-combobox-input-fg
#16181c
--dk-combobox-input-border
#95989d
--dk-combobox-input-block-size
48px
--dk-combobox-input-inline-padding
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-combobox-input-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-combobox-input-font-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)

icon

--dk-combobox-icon-size
1rem
--dk-combobox-icon-color
#16181c

description

--dk-field-description-color
#16181c
--dk-field-description-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

error

--dk-field-error-color
#cc272e
--dk-field-error-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

surface

--dk-combobox-surface-bg
#f2f5fb
--dk-combobox-surface-fg
#16181c
--dk-combobox-surface-border
#95989d
--dk-combobox-surface-radius
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-combobox-surface-shadow
0 10px 15px rgba(0, 0, 0, 0.06), 0 4px 6px rgba(0, 0, 0, 0.04)
--dk-combobox-surface-width
320px
--dk-combobox-surface-padding
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

item

--dk-combobox-item-bg
transparent
--dk-combobox-item-fg
#16181c
--dk-combobox-item-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-combobox-item-min-height
44px
--dk-combobox-item-inline-padding
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)

itemLabel

--dk-combobox-item-label-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-combobox-item-label-weight
550

itemDescription

--dk-combobox-item-description-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

Implementation notes

  • Combobox is single-select and local-filter only in v1; async and freeform modes are deferred.

Implementation checklist

  • Use the public component API first and only drop to lower-level primitives when the component contract genuinely does not fit.
  • Keep theme overrides token-driven so proofs and recipes stay meaningful.
  • Verify the shipped examples and proof fixtures still represent the real product scenario you are implementing.

Examples

Copy-paste examples

Each example is intentionally practical, grouped by starter, common pattern, and edge-case coverage.

Examples
3
Depth
expanded

Starter

1 example

starter

Starter: searchable preset picker

A searchable field for moderate preset lists.

Copy snippet

<Combobox label="Choose a system" description="Filter by name or personality." items={systems} />

Common patterns

1 example

common

Common: compact tool picker

Smaller size works well in dense operational tools.

Copy snippet

<Combobox size="sm" label="Preset" placeholder="Find preset" items={systems} />

Edge cases

1 example

edge

Edge: controlled selection

Control the selected value when the surrounding app owns the state.

Copy snippet

<Combobox label="Preset" items={systems} value={selectedPreset} onChange={(detail) => selectedPreset = detail.value} />

Verification

Curated proof fixtures

Proofs stay visible in the docs so the system shows what it can guarantee, not just what it can render.

default (size=sm)

pass

size=sm

  • Contrast: 98.5 Lc
  • Target: 44px
  • Rows: 44px
  • Surface: 320x280

default (size=md)

pass

size=md

  • Contrast: 98.5 Lc
  • Target: 48px
  • Rows: 44px
  • Surface: 320x280

default (size=lg)

pass

size=lg

  • Contrast: 98.5 Lc
  • Target: 52px
  • Rows: 44px
  • Surface: 320x280

selected (size=sm)

pass

size=sm

  • Contrast: 98.5 Lc
  • Target: 44px
  • Rows: 44px
  • Surface: 320x280

selected (size=md)

pass

size=md

  • Contrast: 98.5 Lc
  • Target: 48px
  • Rows: 44px
  • Surface: 320x280

selected (size=lg)

pass

size=lg

  • Contrast: 98.5 Lc
  • Target: 52px
  • Rows: 44px
  • Surface: 320x280

invalid (size=sm)

pass

size=sm

  • Contrast: 98.5 Lc
  • Target: 44px
  • Rows: 44px
  • Surface: 320x280

invalid (size=md)

pass

size=md

  • Contrast: 98.5 Lc
  • Target: 48px
  • Rows: 44px
  • Surface: 320x280

invalid (size=lg)

pass

size=lg

  • Contrast: 98.5 Lc
  • Target: 52px
  • Rows: 44px
  • Surface: 320x280