What you get
Headless autocomplete gives you a combobox-style query + option-selection contract without forcing a wrapper DOM shape. You own the trigger, icon, content shell, overlay shell, and option rendering.
- Single-value selection with keyboard-safe combobox semantics.
- Separate
queryandvaluestate, so filtering and selection stay distinct. - Owned parts for trigger, icon, content, overlay, listbox, and options.
- Free-form create support when
allowCreateis enabled.
Installation
Import the primitive root and the owned parts you plan to render.
Autocomplete primitive imports
ts
import {
TngAutocomplete,
TngAutocompleteTrigger,
TngAutocompleteTriggerContainer,
TngAutocompleteIcon,
TngAutocompleteContent,
TngAutocompleteOverlay,
TngAutocompleteListbox,
TngAutocompleteOption,
} from '@tailng-ui/primitives';
Basic usage
The primitive root owns selection, open state, invalid/loading state, and query/create behavior. Your trigger and overlay markup remain native.
Basic headless autocomplete
html
<section
tngAutocomplete
[open]="open()"
(openChange)="open.set($event)"
[value]="selectedCountry()"
(valueChange)="selectedCountry.set($event)"
[query]="query()"
(queryChange)="query.set($event)"
>
<div tngAutocompleteTriggerContainer>
<input
tngAutocompleteTrigger
type="text"
[value]="displayText()"
(input)="onInput($event)"
aria-label="Country directory"
placeholder="Search countries"
/>
<span tngAutocompleteIcon aria-hidden="true">▾</span>
</div>
<div tngAutocompleteContent class="contents">
<div tngAutocompleteOverlay>
<ul tngAutocompleteListbox [value]="selectedCountry()">
@for (country of filteredCountries(); track country.code) {
<li tngAutocompleteOption [tngValue]="country.code">{{ country.label }}</li>
}
</ul>
</div>
</div>
</section>
Style variants
The same primitive structure can sit inside a plain CSS shell or a Tailwind utility shell.
Country directory (Plain-CSS)
Selected: India
Country directory (Tailwind CSS)
- Canada
- Germany
- Indonesia
- India
- Japan
- Mexico
- Spain
Selected: Indonesia
Accessibility baseline
- The trigger stays a real
inputwithrole="combobox"applied by the primitive. - The list uses listbox/option semantics and active option tracking through
aria-activedescendant. Tableaves the combobox; arrow keys and Enter stay inside the autocomplete interaction model.aria-labelledby,aria-describedby, and invalid/error wiring come from the primitive root inputs.