Component
<dom-combobox>
custom elementSelect-like combobox with a text input, optional toggle button, managed activedescendant, keyboard navigation, and floating list positioning.
Demo
Select-like search
Select an option value while showing its label in the input. The list still supports filtering, arrow keys, Enter, Esc, and floating positioning.
basic.htmlhtml
<dom-combobox class="relative block w-full max-w-sm">
<input
slot="input"
type="text"
placeholder="Search fruits…"
class="h-10 w-full rounded-full border border-border bg-background px-4 pr-10 text-sm text-fg outline-none focus:ring-2 focus:ring-ring/40"
/>
<button
slot="toggle"
type="button"
aria-label="Show options"
class="absolute right-1 top-1 inline-flex size-8 items-center justify-center rounded-full text-muted-fg hover:bg-secondary"
>
<svg viewBox="0 0 20 20" class="size-4" fill="none">
<path d="M6 8l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
<ul slot="list" class="z-50 max-h-[min(12rem,var(--dom-floating-available-height))] overflow-auto rounded-2xl border border-border bg-background p-1 shadow-lg">
<li data-value="1" data-label="Apple" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg hover:bg-secondary">Apple</li>
<li data-value="2" data-label="Banana" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg hover:bg-secondary">Banana</li>
<li data-value="3" data-label="Cherry" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg hover:bg-secondary">Cherry</li>
</ul>
</dom-combobox>
<script type="module">
import '@getdom/studio/headless/combobox.js';
</script>
Demo
Rich option markup
List items can be authored directly in the DOM with images and nested labels. data-label controls the text shown in the input, and data-value is the selected value.
people.htmlhtml
<dom-combobox class="relative block w-full max-w-md">
<input
slot="input"
type="text"
placeholder="Assign a person…"
class="h-10 w-full rounded-full border border-border bg-background px-4 pr-10 text-sm text-fg outline-none focus:ring-2 focus:ring-ring/40"
/>
<button
slot="toggle"
type="button"
aria-label="Show people"
class="absolute right-1 top-1 inline-flex size-8 items-center justify-center rounded-full text-muted-fg hover:bg-secondary"
>
<svg viewBox="0 0 20 20" class="size-4" fill="none">
<path d="M6 8l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
<ul slot="list" class="z-50 max-h-[min(14rem,var(--dom-floating-available-height))] overflow-auto rounded-2xl border border-border bg-background p-1 shadow-lg">
<li data-value="ada" data-label="Ada Lovelace" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg transition hover:bg-secondary data-[active]:bg-secondary aria-selected:bg-secondary">
<div class="flex items-center gap-3">
<img src="https://unavatar.io/github/adafruit" alt="" class="size-8 rounded-full bg-secondary object-cover" />
<span class="min-w-0">
<span class="block truncate font-medium">Ada Lovelace</span>
<span class="block truncate text-xs text-muted-fg">Mathematician</span>
</span>
</div>
</li>
<li data-value="grace" data-label="Grace Hopper" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg transition hover:bg-secondary data-[active]:bg-secondary aria-selected:bg-secondary">
<div class="flex items-center gap-3">
<img src="https://unavatar.io/github/ghopper" alt="" class="size-8 rounded-full bg-secondary object-cover" />
<span class="min-w-0">
<span class="block truncate font-medium">Grace Hopper</span>
<span class="block truncate text-xs text-muted-fg">Computer scientist</span>
</span>
</div>
</li>
<li data-value="katherine" data-label="Katherine Johnson" class="cursor-pointer rounded-xl px-3 py-2 text-sm text-fg transition hover:bg-secondary data-[active]:bg-secondary aria-selected:bg-secondary">
<div class="flex items-center gap-3">
<img src="https://unavatar.io/github/nasa" alt="" class="size-8 rounded-full bg-secondary object-cover" />
<span class="min-w-0">
<span class="block truncate font-medium">Katherine Johnson</span>
<span class="block truncate text-xs text-muted-fg">NASA mathematician</span>
</span>
</div>
</li>
</ul>
</dom-combobox>
<script type="module">
import '@getdom/studio/headless/combobox.js';
</script>
Usage
Plain HTML
Register every <dom-*> in one import: import '@getdom/studio/headless'.
Slots
| Name | Scope | Description |
|---|---|---|
| #input | — | The text input. Gets role="combobox" and aria-controls wired automatically. |
| #toggle | — | Optional button that opens the list like a select. |
| #list | — | The listbox container. Each option must carry data-value. |
Attributes
| Name | Type | Description |
|---|---|---|
| value | string | Currently-selected value. |
| open | boolean | Reflects whether the list is visible. |
| placement | string | Preferred floating placement before collision handling. |
| floating-mode | 'viewport' | 'anchor' | viewport keeps the list inside the browser; anchor keeps it attached to the input while scrolling. |
| data-menu-id | string | Id of an external or teleported list element. |
From dom-combobox.__doc.attributes.
Events
| Name | Payload | Description |
|---|---|---|
| dom:input | { value } | Fired whenever the text value changes. |
| dom:query | { query } | Fired whenever the user types. Useful for server lookups. |
| dom:select | { value, label, option } | Fired when an option is selected. |
| dom:change | { value, option, custom } | Fired when an option is committed. |
Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.
Keyboard
- ↑ / ↓Move active option.
- EnterCommit active option.
- EscClose the list.
- TypeFilter the list and emit dom:query.
Related