Imports
Primitive imports
ts
import {
TngSelect,
TngSelectContent,
TngSelectIcon,
TngSelectListbox,
TngSelectOption,
TngSelectOverlay,
TngSelectTrigger,
TngSelectValue,
} from '@tailng-ui/primitives';Basic usage
Owned select composition
html
<section
tngSelect
[value]="selectedStage()"
(valueChange)="onSelectedStageChange($event)"
>
<button type="button" tngSelectTrigger>
<span tngSelectValue>{{ selectedStageLabel() ?? 'Choose workflow stage' }}</span>
<span tngSelectIcon aria-hidden="true">▾</span>
</button>
<div tngSelectContent class="docs-headless-selectbox-basic-content">
<div tngSelectOverlay>
<div
tngSelectListbox
[value]="selectedStage()"
(valueChange)="onSelectedStageChange($event)"
>
@for (stage of workflowStages; track stage.value) {
<div tngSelectOption [tngValue]="stage.value">{{ stage.label }}</div>
}
</div>
</div>
</div>
</section>Select variants
The same headless select primitive rendered with an owned plain-CSS shell and a light Tailwind shell.
Workflow stage (Plain-CSS)
Workflow stage
Primitive-first single selection with an owned trigger and owned overlay rows.
Selected: In review
Workflow stage (Tailwind CSS)
Workflow stage
Primitive-first single selection with an owned trigger and owned overlay rows.
Draft
Internal drafting only.
In review
Waiting on editorial review.
QA ready
Approved for validation.
Live
Already published.
Selected: QA ready
Behavior baseline
- The trigger owns combobox semantics, focus, and
aria-expanded. - The listbox bridge keeps active option state and committed value in sync.
Enter,Space, and pointer selection commit the active option and close the menu.- Use
data-active,data-selected, anddata-disabledfor option state styling.