Input Group
<tng-input-group> is the headless shell primitive for one projected input[tngInput] or textarea[tngInput] plus optional tngPrefix/tngSuffix content.
Reach for it when you want the layout, state mirroring, and slot structure of a grouped field while keeping complete ownership of the surface styles.
Imports
Headless imports
import { TngInput, TngInputGroup, TngPrefix, TngSuffix } from '@tailng-ui/primitives';
import { TngIcon } from '@tailng-ui/icons';
Basic composition
The primitive expects exactly one projected control with tngInput. Leading and trailing regions render only when matching slot content exists.
Minimal input group
<tng-input-group>
<input tngInput type="text" placeholder="Project name" />
</tng-input-group>
Grouped search field
<tng-input-group>
<span tngPrefix aria-hidden="true">
<tng-icon icon="search"></tng-icon>
</span>
<input tngInput type="search" placeholder="Search docs..." />
<span tngSuffix aria-hidden="true">Ctrl+K</span>
</tng-input-group>
Grouped field variants
Compare the same headless input-group composition across plain CSS and Tailwind CSS while keeping the shell primitive in charge of layout and state mirroring.
Input Group (Plain-CSS)
Input Group (Tailwind CSS)
Mirrored state contract
tngInputGroup mirrors focus, invalid, disabled, readonly, and slot-presence state onto the host. That gives you stable styling hooks without owning the shell layout plumbing yourself.
Host state selectors
<tng-input-group class="docs-headless-search-shell">
<input tngInput type="email" aria-invalid="true" />
</tng-input-group>
.docs-headless-search-shell[data-focused] {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.18);
}
.docs-headless-search-shell[data-invalid] {
border-color: #dc2626;
}
Accessibility baseline
- Give the projected control a real accessible name with a label or ARIA.
- Mark decorative prefix and suffix content as
aria-hidden="true". - Trailing buttons should keep their own explicit accessible labels.
Accessible grouped field
<label for="docs-search">Search docs</label>
<tng-input-group>
<span tngPrefix aria-hidden="true">
<tng-icon icon="search"></tng-icon>
</span>
<input id="docs-search" tngInput type="search" />
<button tngSuffix type="button" aria-label="Clear search">Clear</button>
</tng-input-group>