Overview of the Phoenix and LiveView Headless Combobox Component
The Mishka Chelekom Headless Combobox
ships zero opinionated styling
- only the semantic markup, full WAI-ARIA
wiring, and paired-presence state
(
data-open
/
data-closed
).
Typing filters the options (non-matching options get
data-hidden
), ArrowDown opens the popup and
moves a roving highlight over the navigable options, Enter or a click selects, and
Escape closes. A selected option carries
data-selected
and
aria-selected
, styleable through the
indicator
part. Every part - input, trigger, clear, chips, popup, group, item, indicator,
empty
and create
- is addressable through a
*_class
attribute, so you style it entirely with your own Tailwind
classes while the accessibility and behavior keep working underneath.
Base UI Combobox example - single-select with clear & trigger (Phoenix headless)
- Apple
- Banana
- Orange
- Pineapple
- Grape
- Mango
- Strawberry
- Blueberry
- Raspberry
- Blackberry
- Cherry
- Peach
- Pear
- Plum
- Kiwi
- Watermelon
- Cantaloupe
- Honeydew
- Papaya
- Guava
- Lychee
- Pomegranate
- Apricot
- Grapefruit
- Passionfruit
- No fruits found.
<.combobox id="baseui-combobox-hero" clear trigger placeholder="e.g. Apple" class="relative" control_class="relative h-8 w-56 border border-neutral-950 bg-white dark:bg-neutral-950 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white dark:border-white" input_class="h-full w-full border-0 bg-white pl-2 pr-[calc(0.5rem+2rem*2)] dark:bg-neutral-950 text-sm font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" clear_class="absolute right-6 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white data-[hidden]:hidden" trigger_class="absolute right-0 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white" popup_class="mt-1 w-56 max-w-[var(--available-width)] origin-[var(--transform-origin)] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] max-h-[22.5rem] overflow-y-auto overscroll-contain py-1 scroll-py-1 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" indicator_class="col-start-1 invisible group-data-[selected]/item:visible" empty_class="py-4 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400 data-[hidden]:hidden" > <:trigger_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display:block"> <path d="M12 6H4l4 4.5z" /> </svg> </:trigger_icon> <:clear_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:clear_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={ f <- ~w(Apple Banana Orange Pineapple Grape Mango Strawberry Blueberry Raspberry Blackberry Cherry Peach Pear Plum Kiwi Watermelon Cantaloupe Honeydew Papaya Guava Lychee Pomegranate Apricot Grapefruit Passionfruit) } value={String.downcase(f)} > <span class="col-start-2">{f}</span> </:option> <:empty>No fruits found.</:empty> </.combobox>
Base UI Combobox example - grouped options (Phoenix headless)
-
Fruits
- Apple
- Banana
- Mango
- Kiwi
- Grape
- Orange
- Strawberry
- Watermelon
-
Vegetables
- Broccoli
- Carrot
- Cauliflower
- Cucumber
- Kale
- Bell pepper
- Spinach
- Zucchini
- No produce found.
<.combobox id="baseui-combobox-grouped" clear trigger placeholder="e.g. Mango" class="relative" control_class="relative h-8 w-64 border border-neutral-950 bg-white dark:bg-neutral-950 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white dark:border-white" input_class="h-full w-full border-0 bg-white pl-2 pr-[calc(0.5rem+2rem*2)] dark:bg-neutral-950 text-sm font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" clear_class="absolute right-6 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white data-[hidden]:hidden" trigger_class="absolute right-0 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white" popup_class="mt-1 w-64 max-w-[var(--available-width)] origin-[var(--transform-origin)] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] max-h-[22.5rem] overflow-auto overscroll-contain py-1 scroll-py-1 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" group_class="block pb-2 last:pb-0" group_label_class="block py-2 pr-2 pl-8 text-sm leading-4 text-neutral-500 select-none dark:text-neutral-400" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" indicator_class="col-start-1 flex items-center justify-center invisible group-data-[selected]/item:visible" empty_class="py-4 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400 data-[hidden]:hidden" > <:trigger_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display:block"> <path d="M12 6H4l4 4.5z" /> </svg> </:trigger_icon> <:clear_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:clear_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={f <- ~w(Apple Banana Mango Kiwi Grape Orange Strawberry Watermelon)} value={"fruit-" <> String.downcase(f)} group="Fruits" > <span class="col-start-2">{f}</span> </:option> <:option :for={ v <- [ "Broccoli", "Carrot", "Cauliflower", "Cucumber", "Kale", "Bell pepper", "Spinach", "Zucchini" ] } value={"veg-" <> String.downcase(v)} group="Vegetables" > <span class="col-start-2">{v}</span> </:option> <:empty>No produce found.</:empty> </.combobox>
Base UI Combobox example - multiple selection with chips (Phoenix headless)
- JavaScript
- TypeScript
- Python
- Java
- C++
- C#
- PHP
- Ruby
- Go
- Rust
- Swift
- No languages found.
<.combobox id="baseui-combobox-multiple" multiple placeholder="e.g. TypeScript" control_class="flex min-h-8 w-64 cursor-text flex-wrap items-center gap-0.5 border border-neutral-950 bg-white dark:bg-neutral-950 px-2 py-1 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white has-[button]:px-1 dark:border-white min-[32rem]:w-[22rem]" chips_class="flex w-full flex-wrap items-center gap-1" chip_class="group flex min-h-[calc(1.5rem-2px)] cursor-default items-center gap-1 overflow-hidden bg-neutral-100 py-0 pr-[0.2rem] pl-[0.4rem] text-sm leading-none text-neutral-950 outline-none focus-within:bg-neutral-950 focus-within:text-white dark:bg-neutral-800 dark:text-white dark:focus-within:bg-white dark:focus-within:text-neutral-950" chip_remove_class="flex size-4 items-center justify-center border-0 bg-transparent p-0 text-inherit hover:bg-neutral-200 group-focus-within:hover:bg-neutral-700 dark:hover:bg-neutral-700 dark:group-focus-within:hover:bg-neutral-200" input_class="h-[calc(1.5rem-2px)] min-w-12 flex-1 border-0 bg-white p-0 text-sm dark:bg-neutral-950 font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" popup_class="mt-1 w-64 max-h-[24.5rem] max-w-[var(--available-width)] origin-[var(--transform-origin)] overflow-y-auto overscroll-contain border border-neutral-950 bg-white py-1 text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-[selected]:relative data-[selected]:z-0 data-[selected]:text-neutral-950 data-[selected]:before:absolute data-[selected]:before:inset-0 data-[selected]:before:z-[-1] data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[selected]:text-white dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" indicator_class="col-start-1 invisible group-data-[selected]/item:visible" empty_class="py-2 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400 data-[hidden]:hidden" > <:chip_remove_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:chip_remove_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={ lang <- [ "JavaScript", "TypeScript", "Python", "Java", "C++", "C#", "PHP", "Ruby", "Go", "Rust", "Swift" ] } value={lang} > <span class="col-start-2">{lang}</span> </:option> <:empty>No languages found.</:empty> </.combobox>
Base UI Combobox example - input inside popup (Phoenix headless)
- Australia
- Canada
- France
- Germany
- India
- Italy
- Japan
- Mexico
- Spain
- United Kingdom
- United States
- Vietnam
- No countries found.
<.combobox id="baseui-combobox-input-inside-popup" placeholder="e.g. United Kingdom" control_class="relative flex h-8 min-w-40 items-center border border-neutral-950 bg-white px-2 text-sm dark:bg-neutral-950 dark:border-white focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white" input_class="h-full w-full border-0 bg-white text-sm font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:bg-neutral-950 dark:placeholder:text-neutral-400 dark:text-white" popup_class="mt-1 max-h-[24.5rem] max-w-[var(--available-width)] origin-[var(--transform-origin)] overflow-auto overscroll-contain border border-neutral-950 bg-white py-1 text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid min-w-80 cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-hidden select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" indicator_class="col-start-1 invisible group-data-[selected]/item:visible" empty_class="py-4 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400 data-[hidden]:hidden" > <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={ {code, label} <- [ {"au", "Australia"}, {"ca", "Canada"}, {"fr", "France"}, {"de", "Germany"}, {"in", "India"}, {"it", "Italy"}, {"jp", "Japan"}, {"mx", "Mexico"}, {"es", "Spain"}, {"gb", "United Kingdom"}, {"us", "United States"}, {"vn", "Vietnam"} ] } value={code} > <span class="col-start-2">{label}</span> </:option> <:empty>No countries found.</:empty> </.combobox>
Base UI Combobox example - creatable multi-select (Phoenix headless)
- bug
- documentation
- enhancement
- help wanted
- good first issue
- No labels found.
- Create “”
<.combobox id="baseui-combobox-creatable" multiple creatable placeholder="e.g. bug" control_class="flex min-h-8 w-64 cursor-text flex-wrap items-center gap-0.5 border border-neutral-950 bg-white dark:bg-neutral-950 px-2 py-1 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white has-[button]:px-1 dark:border-white min-[32rem]:w-[22rem]" chips_class="flex w-full flex-wrap items-center gap-1" chip_class="group flex min-h-[calc(1.5rem-2px)] cursor-default items-center gap-1 overflow-hidden bg-neutral-100 py-0 pr-[0.2rem] pl-[0.4rem] text-sm leading-none text-neutral-950 outline-none focus-within:bg-neutral-950 focus-within:text-white dark:bg-neutral-800 dark:text-white dark:focus-within:bg-white dark:focus-within:text-neutral-950" chip_remove_class="flex size-4 items-center justify-center border-0 bg-transparent p-0 text-inherit hover:bg-neutral-200 group-focus-within:hover:bg-neutral-700 dark:hover:bg-neutral-700 dark:group-focus-within:hover:bg-neutral-200" input_class="h-[calc(1.5rem-2px)] min-w-12 flex-1 border-0 bg-white p-0 text-sm dark:bg-neutral-950 font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" popup_class="mt-1 w-64 max-h-[24.5rem] max-w-[var(--available-width)] origin-[var(--transform-origin)] overflow-y-auto overscroll-contain border border-neutral-950 bg-white py-1 text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-[selected]:relative data-[selected]:z-0 data-[selected]:text-neutral-950 data-[selected]:before:absolute data-[selected]:before:inset-0 data-[selected]:before:z-[-1] data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[selected]:text-white dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" indicator_class="col-start-1 invisible group-data-[selected]/item:visible" create_class="grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white data-[hidden]:hidden" create_label_class="col-start-2" empty_class="py-2 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400 data-[hidden]:hidden" > <:chip_remove_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:chip_remove_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:create_icon> <span class="col-start-1"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="M1.5 8h13M8 14.5v-13" /> </svg> </span> </:create_icon> <:option :for={lbl <- ["bug", "documentation", "enhancement", "help wanted", "good first issue"]} value={lbl} > <span class="col-start-2">{lbl}</span> </:option> <:empty>No labels found.</:empty> </.combobox>
Base UI Combobox example - async single-select (Phoenix headless)
- Leslie Alexander [email protected] @leslie Product Manager
- Kathryn Murphy [email protected] @kathryn Marketing Lead
- Courtney Henry [email protected] @courtney Design Systems
- Michael Foster [email protected] @michael Engineering Manager
- Lindsay Walton [email protected] @lindsay Product Designer
- Try a different search term.
<.combobox id="baseui-combobox-async-single" clear trigger placeholder="e.g. Michael" class="relative" control_class="relative h-8 w-64 border border-neutral-950 bg-white dark:bg-neutral-950 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white dark:border-white md:w-80" input_class="h-full w-full border-0 bg-white pl-2 pr-[calc(0.5rem+2rem*2)] dark:bg-neutral-950 text-sm font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" clear_class="absolute right-6 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white data-[hidden]:hidden" trigger_class="absolute right-0 bottom-0 flex h-full w-6 items-center justify-center border-0 bg-transparent p-0 text-neutral-950 dark:text-white" popup_class="mt-1 w-64 md:w-80 max-w-[var(--available-width)] origin-[var(--transform-origin)] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] max-h-[22.5rem] overflow-y-auto overscroll-contain py-1 scroll-pt-1 scroll-pb-1 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-start gap-2 px-2 py-2 text-sm leading-[1.2rem] outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-neutral-950 data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-100 dark:data-[highlighted]:text-white dark:data-[highlighted]:before:bg-neutral-800 data-[hidden]:hidden" indicator_class="col-start-1 mt-1 invisible group-data-[selected]/item:visible" > <:trigger_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display:block"> <path d="M12 6H4l4 4.5z" /> </svg> </:trigger_icon> <:clear_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:clear_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={ u <- [ %{ id: "leslie-alexander", name: "Leslie Alexander", username: "leslie", email: "[email protected]", title: "Product Manager" }, %{ id: "kathryn-murphy", name: "Kathryn Murphy", username: "kathryn", email: "[email protected]", title: "Marketing Lead" }, %{ id: "courtney-henry", name: "Courtney Henry", username: "courtney", email: "[email protected]", title: "Design Systems" }, %{ id: "michael-foster", name: "Michael Foster", username: "michael", email: "[email protected]", title: "Engineering Manager" }, %{ id: "lindsay-walton", name: "Lindsay Walton", username: "lindsay", email: "[email protected]", title: "Product Designer" } ] } value={u.id} > <span class="col-start-2 flex flex-col gap-1"> <span class="text-sm leading-5 font-bold">{u.name}</span> <span class="text-xs">{u.email}</span> <span class="flex flex-wrap gap-2 text-xs text-neutral-500 dark:text-neutral-400"> <span>@{u.username}</span> <span>{u.title}</span> </span> </span> </:option> <:empty>Try a different search term.</:empty> </.combobox>
Base UI Combobox example - async multi-select (Phoenix headless)
- Leslie Alexander [email protected] @leslie Product Manager
- Kathryn Murphy [email protected] @kathryn Marketing Lead
- Courtney Henry [email protected] @courtney Design Systems
- Michael Foster [email protected] @michael Engineering Manager
- Lindsay Walton [email protected] @lindsay Product Designer
- Try a different search term.
<.combobox id="baseui-combobox-async-multiple" multiple placeholder="e.g. Michael" control_class="flex min-h-8 w-64 cursor-text flex-wrap items-center gap-0.5 border border-neutral-950 bg-white dark:bg-neutral-950 px-2 py-1 focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-neutral-950 dark:focus-within:outline-white has-[button]:px-1 dark:border-white min-[32rem]:w-[22rem]" chips_class="flex w-full flex-wrap items-center gap-1" chip_class="group flex min-h-[calc(1.5rem-2px)] cursor-default items-center gap-1 overflow-hidden bg-neutral-100 py-0 pr-[0.2rem] pl-[0.4rem] text-sm leading-none text-neutral-950 outline-none focus-within:bg-neutral-950 focus-within:text-white dark:bg-neutral-800 dark:text-white dark:focus-within:bg-white dark:focus-within:text-neutral-950" chip_remove_class="flex size-4 items-center justify-center border-0 bg-transparent p-0 text-inherit hover:bg-neutral-200 group-focus-within:hover:bg-neutral-700 dark:hover:bg-neutral-700 dark:group-focus-within:hover:bg-neutral-200" input_class="h-[calc(1.5rem-2px)] min-w-12 flex-1 border-0 bg-white p-0 text-sm dark:bg-neutral-950 font-normal text-neutral-950 outline-none placeholder:text-neutral-500 dark:placeholder:text-neutral-400 dark:text-white" popup_class="mt-1 w-64 max-w-[var(--available-width)] origin-[var(--transform-origin)] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0_rgb(0_0_0_/_12%)] max-h-[24.5rem] overflow-y-auto overscroll-contain py-1 scroll-pt-1 scroll-pb-1 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none data-[closed]:hidden" item_class="group/item grid cursor-default grid-cols-[1rem_1fr] items-start gap-2 px-2 py-2 text-sm leading-[1.2rem] outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-neutral-950 data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-100 dark:data-[highlighted]:text-white dark:data-[highlighted]:before:bg-neutral-800 data-[hidden]:hidden" indicator_class="col-start-1 mt-1 invisible group-data-[selected]/item:visible" > <:chip_remove_icon> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" style="display:block" > <path d="m4.5 4.5 7 7m-7 0 7-7" /> </svg> </:chip_remove_icon> <:item_indicator> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" style="display:block" > <path d="m2.5 8.5 4 4 7-9" /> </svg> </:item_indicator> <:option :for={ u <- [ %{ id: "leslie-alexander", name: "Leslie Alexander", username: "leslie", email: "[email protected]", title: "Product Manager" }, %{ id: "kathryn-murphy", name: "Kathryn Murphy", username: "kathryn", email: "[email protected]", title: "Marketing Lead" }, %{ id: "courtney-henry", name: "Courtney Henry", username: "courtney", email: "[email protected]", title: "Design Systems" }, %{ id: "michael-foster", name: "Michael Foster", username: "michael", email: "[email protected]", title: "Engineering Manager" }, %{ id: "lindsay-walton", name: "Lindsay Walton", username: "lindsay", email: "[email protected]", title: "Product Designer" } ] } value={u.id} > <span class="col-start-2 flex flex-col gap-1"> <span class="text-sm leading-5 font-bold">{u.name}</span> <span class="text-xs">{u.email}</span> <span class="flex flex-wrap gap-2 text-xs text-neutral-500 dark:text-neutral-400"> <span>@{u.username}</span> <span>{u.title}</span> </span> </span> </:option> <:empty>Try a different search term.</:empty> </.combobox>
Parts & data attributes
Each part carries a stable
data-part
and a structural
chelekom-combobox__*
class you can target, plus state attributes for filtering, highlighting, and
selection.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Input | input | role="combobox", aria-controls, aria-expanded, aria-autocomplete, aria-activedescendant | The text field the user types into to filter options. |
| Trigger | trigger | - | Optional button that opens/closes the popup without typing. |
| Clear | clear | data-hidden | Optional button that clears the query/selection. |
| Chips / Chip | chips / chip | data-chip-value | Multiple-mode: the container of selected chips, and each removable chip (with a nested chip-remove button). |
| Value | value | - | Hidden form input for a single selection; multiple submits through each chip's name[]. |
| Popup | popup | role="listbox", data-open, data-closed | The listbox that shows the filtered options. |
| Group / Group label | group / group-label | role="group", aria-labelledby, data-hidden | Wraps consecutive options that share the same group. |
| Item | item | role="option", data-value, data-highlighted, data-hidden, data-selected, data-disabled, aria-selected, aria-disabled | A single selectable option. |
| Indicator | indicator | - | The selected checkmark, shown when the item is data-selected. |
| Empty | empty | data-hidden | Shown when the query matches no options. |
| Create | create | data-hidden | Creatable mode: offers to create the typed query as a new option. |
Accessibility
Follows the
WAI-ARIA Combobox pattern
: Down/Up open the popup and navigate the options, skipping disabled and
filtered-out ones; Enter selects the highlighted option (or creates it, when
creatable
is set); Escape closes the popup; and typing filters the list live. The input keeps
aria-activedescendant
pointed at the highlighted option so screen readers announce it as focus moves.