Input
tngInput is the native input primitive, and this page documents the headless path: you compose a real native <input> with tngInput and optional tngInputGroup slots.
Use direct primitive attachment for the simplest field, or add tngInputGroup when you need grouped composition with optional leading/trailing content.
- Direct attachment:
tngInputon a real<input> - Grouped composition:
tngInputGroupwith optional leading/trailing content using slot directives
What you get
- Native-first behavior
- Uses a real
<input>instead of a synthetic control. - Preserves standard browser behavior (typing, composition events, Enter submit, native validation).
- Uses a real
- Styling contract
- Styling hooks are exposed through
data-slotmarkers and state data attributes. tngInputGroupprovides the base structural layout for leading, control, and trailing regions.- Prefix/suffix placement works without consumer deep selectors or internal wrapper styling.
- Consumers style the visual appearance such as border, radius, background, color, spacing, and focus treatment.
- No required classes.
- Styling hooks are exposed through
- Accessibility pass-through
- Supports
aria-label,aria-labelledby,aria-describedby,aria-required,aria-invalid. aria-invalidis emitted only when invalid.
- Supports
Simple examples
Compare the same headless Input structure across plain CSS and Tailwind CSS styles.
Basic Inputs (Plain CSS)
Basic Inputs (Tailwind CSS)
Installation
Import only the primitives you need.
Primitives import
import { TngInput, TngInputGroup, TngPrefix, TngSuffix } from '@tailng-ui/primitives';
Basic usage
1) Direct primitive attachment (simplest)
Use this when you do not need leading/trailing adornments.
Direct tngInput usage
<input tngInput type="email" placeholder="team@tailng.dev" />
2) Grouped input with leading/trailing slots
Use this for icons, prefixes/suffixes, clear buttons, and helper affordances. tngInputGroup handles the internal leading, control, and trailing layout automatically.
tngInputGroup composition
<tng-input-group>
<span tngPrefix aria-hidden="true">Search</span>
<input tngInput type="search" placeholder="Search..." />
<span tngSuffix aria-hidden="true">Ctrl+K</span>
</tng-input-group>
Structure
The headless path never creates its own control. You apply tngInput to the real native <input> and optionally wrap it in tngInputGroup.
tngInputGroup does not replace the native control, but it does create structural wrapper regions for grouped composition. These regions provide a consistent leading, control, and trailing layout so prefix/suffix content is positioned correctly without deep selectors.
Detailed API bindings are documented in the API tab. Styling slot/state contract is documented in the Styling tab. Textarea is documented on the dedicated Textarea page.
Layout contract
tngInputGrouprenders a grouped horizontal layout.- Leading content is placed in an optional leading region.
- The projected control is placed in the flexible control region.
- Trailing content is placed in an optional trailing region.
- This structural layout is built into the primitive so consumers can focus on visual styling.
Accessibility guidance
- Mark decorative leading/trailing content as
aria-hidden="true". - Projected interactive elements (for example trailing clear button) are valid tab stops by design.
- Interactive prefix/suffix content participates inside the grouped layout without requiring extra wrapper styling.
Validation patterns
Recommended for unit tests (jsdom)
Stable invalid test input
<input tngInput type="email" aria-invalid="true" />
Native browser validation path
Native required validation
<input tngInput type="email" required />
Examples
Search with keyboard hint (non-focusable)
Search + hint
<tng-input-group class="demo-group">
<span tngPrefix aria-hidden="true">Search</span>
<input tngInput type="search" placeholder="Search primitives" />
<span tngSuffix aria-hidden="true">Ctrl+K</span>
</tng-input-group>
Search with clear button (focusable trailing)
Search + clear action
<tng-input-group class="demo-group">
<input tngInput type="search" placeholder="Search..." />
<button tngSuffix type="button" aria-label="Clear">X</button>
</tng-input-group>
Common pitfalls
"My group warns: found 0 controls"
Ensure the projected control includes tngInput.
"Do I need to style internal group wrappers?"
No. tngInputGroup owns the base leading/control/trailing layout. Consumer styles should focus on visual appearance, not internal wrapper placement.
Correct
<tng-input-group>
<input tngInput />
</tng-input-group>
Incorrect
<tng-input-group>
<input />
</tng-input-group>