Autocomplete
v0.0.10
mix mishka.ui.gen.headless autocomplete
Overview of the Phoenix and LiveView Headless Autocomplete Component
The Mishka Chelekom Headless Autocomplete ships zero opinionated styling - only the semantic markup, full WAI-ARIA combobox wiring, typeahead filtering, a roving highlight, and paired-presence state (data-open/data-closed/data-highlighted/data-hidden). Every part - input, clear, popup, group and item - is addressable through a *_class attribute, so you style it entirely with your own Tailwind classes while the accessibility and behavior keep working underneath.
The root takes class, input_class, clear_class, popup_class, status_class, empty_class, item_class, group_label_class and group_list_class. The last three reach every item, group label and group list, so the options themselves need no classes. An option slot can still add its own class to one item, and the first option of a group takes group_class, group_label_class and group_list_class for that group only.
Base UI Autocomplete example (Phoenix headless)
Base UI Autocomplete example (Phoenix headless)
<label class="flex flex-col gap-1 text-sm font-bold text-neutral-950 dark:text-white">
Search tags
<.autocomplete
id="baseui-autocomplete-hero"
placeholder="e.g. feature"
input_class="h-8 w-[16rem] border border-neutral-950 bg-white dark:bg-neutral-950 px-2 text-sm font-normal text-neutral-950 placeholder:text-neutral-500 dark:placeholder:text-neutral-400 focus:outline-2 focus:-outline-offset-1 focus:outline-neutral-950 dark:focus:outline-white dark:border-white dark:text-white"
popup_class="top-full mt-1 font-normal w-[16rem] max-w-[16rem] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0] shadow-black/12 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none outline-0 overflow-y-auto scroll-py-[0.25rem] py-1 overscroll-contain max-h-[22.5rem]"
item_class="flex cursor-default items-center gap-2 py-2 pr-2 pl-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-x-0 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white"
>
<:option value="feature">feature</:option>
<:option value="bug">bug</:option>
<:option value="docs">docs</:option>
<:option value="design">design</:option>
<:option value="urgent">urgent</:option>
<:empty>
<div class="py-4 pr-4 pl-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400">
No tags found.
</div>
</:empty>
</.autocomplete>
</label>Parts & data attributes
Each part carries a stable data-part and a structural chelekom-autocomplete__* class you can target, plus paired-presence state for filtering, highlighting, and open/closed transitions.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Input | input | role="combobox", aria-controls, aria-expanded, aria-autocomplete, aria-activedescendant | The text field that filters options as you type. Takes input_class. |
| Clear | clear | aria-label, data-hidden | Opt-in button (clear) that empties the input; hidden while it is already empty. Takes clear_class. |
| Value | value | - | Hidden form input holding the currently selected value. |
| Status | status | role="status", aria-live="polite" | Live region announcing the result count as you type. Takes status_class. |
| Popup | popup | role="listbox", data-open, data-closed | The floating (or inline) list of options. Takes popup_class. |
| Group / Group label | group / group-label | role="group", aria-labelledby, data-hidden | Wraps consecutive same-group options under a labeled group; hidden when every item inside is filtered out. The label takes group_label_class. |
| Group list | group-list | role="presentation" | The list of options inside a group. Takes group_list_class. |
| Item | item | role="option", data-value, data-highlighted, data-hidden, aria-selected | An individual option; hidden when it does not match the query. Takes item_class on the root and class on an option slot. |
| Empty | empty | data-hidden | Shown when the query matches no options. Takes empty_class. |
Accessibility
Follows the WAI-ARIA Combobox pattern: ArrowDown/ArrowUp opens the popup and moves a roving highlight, Enter selects the highlighted option, Escape closes the popup, and typing filters the list (auto_highlight highlights the first match so Enter selects it without arrowing down). The live status region announces the result count to screen readers on every keystroke.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (anchors the popup and option ids) |
name
|
string
| — | — | No | Name for the hidden form input |
value
|
string
| — | — | No | Currently selected value |
placeholder
|
string
| — | — | No | Input placeholder text |
disabled
|
boolean
|
false
| — | No | Disable the input (never opens) |
readonly
|
boolean
|
false
| — | No | Make the input read-only |
required
|
boolean
|
false
| — | No | Mark the input as required |
auto_highlight
|
boolean
|
false
| — | No | Highlight the first match as you type so Enter selects it without ArrowDown |
filter
|
string
|
contains
|
containsstarts_with
| No | Match mode for the typed query |
clear
|
boolean
|
false
| — | No | Render a `data-part="clear"` button |
class
|
any
| — | — | No | — |
input_class
|
any
| — | — | No | Extra classes for the `data-part="input"` element |
popup_class
|
any
| — | — | No | Extra classes for the `data-part="popup"` listbox |
status_class
|
any
| — | — | No | Extra classes for the `data-part="status"` live region |
empty_class
|
any
| — | — | No | Extra classes for the `data-part="empty"` wrapper |
inline
|
boolean
|
false
| — | No | Render the list in normal flow (static, always open) instead of a floating popup — e.g. a command palette |
clear_class
|
any
| — | — | No | — |
item_class
|
any
| — | — | No | — |
group_label_class
|
any
| — | — | No | — |
group_list_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Slots
| Name | Required | Description |
|---|---|---|
option
| Yes | — |
empty
| No | Shown when the query matches no options |
Attributes of the option slot
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
value
|
string
| — | — | Yes | — |
label
|
string
| — | — | No | Display string shown in the input/chip when selected (defaults to `value`); the option's inner content can be richer |
group
|
string
| — | — | No | Optional group label (consecutive same-group options are grouped) |
class
|
any
| — | — | No | Extra classes for this option's `data-part="item"` |
group_class
|
any
| — | — | No | Extra classes for the `data-part="group"` wrapper (first option of a group) |
group_label_class
|
any
| — | — | No | Extra classes for the `data-part="group-label"` (first option of a group) |
group_list_class
|
any
| — | — | No | Extra classes for the `data-part="group-list"` (first option of a group) |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
autocomplete
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless autocomplete