Getting StartedInstall and bootstrap headless foundations 4
LayoutHeadless structural primitives for expandable and container patterns 6
OverlayHeadless modal and floating layer behavior 3
FeedbackHeadless notification and status communication patterns 5
FormHeadless input and selection contracts 17
UtilityReusable action, identity, and clipboard behavior 6
NavigationHeadless trails, trees, menus, and command surfaces 7

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.

  1. Direct attachment:tngInput on a real <input>
  2. Grouped composition:tngInputGroup with 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).
  • Styling contract
    • Styling hooks are exposed through data-slot markers and state data attributes.
    • tngInputGroup provides 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.
  • Accessibility pass-through
    • Supports aria-label, aria-labelledby, aria-describedby, aria-required, aria-invalid.
    • aria-invalid is emitted only when invalid.

Simple examples

Compare the same headless Input structure across plain CSS and Tailwind CSS styles.

Basic Inputs (Plain CSS)

Installation

Import only the primitives you need.

Primitives import

ts
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

html
<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

html
<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

  • tngInputGroup renders 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

html
<input tngInput type="email" aria-invalid="true" />

Native browser validation path

Native required validation

html
<input tngInput type="email" required />

Examples

Search with keyboard hint (non-focusable)

Search + hint

html
<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

html
<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

html
<tng-input-group>
  <input tngInput />
</tng-input-group>

Incorrect

html
<tng-input-group>
  <input />
</tng-input-group>