TextInput
A labeled single-line input covering the common HTML input types, with description, inline error, and decorative prefix and suffix slots.
Import
import { TextInput } from "@hyzer-labs/ui"Demo
let playerName = $state('');
<TextInput name="player" label="Player name" bind:value={playerName} />One component for every single-line type. Pair type with autocomplete and inputmode where it helps. Autofill and the right
virtual keyboard are most of the mobile form experience.
<TextInput name="email" label="Email" type="email" autocomplete="email" /><TextInput name="password" label="Password" type="password" autocomplete="current-password" /><TextInput name="q" label="Search courses" type="search" /><TextInput name="phone" label="Phone" type="tel" autocomplete="tel" /><TextInput name="site" label="Club website" type="url" placeholder="https://" /><TextInput name="rating" label="PDGA rating" type="number" inputmode="numeric" /><TextInput name="round-date" label="Round date" type="date" /><TextInput name="tee-time" label="Tee time" type="time" /><TextInput name="start" label="Tournament start" type="datetime-local" />The slots are decorative (aria-hidden): an icon for a search box, a unit
for a measurement. The search demo also uses hideLabel, so the label stays
for screen readers while the icon and placeholder carry the visual weight.
<TextInput name="q" label="Search courses" type="search" hideLabel placeholder="Search courses…">
{#snippet prefix()}<IconSearch />{/snippet}
</TextInput>
<TextInput name="distance" label="Longest throw" type="number" inputmode="numeric">
{#snippet suffix()}ft{/snippet}
</TextInput>description and error are announced with the field: both chain
into aria-describedby. Error and disabled are also field states. The
wrapper's data-state reflects them, and error wins.
Leave blank if you're not a member yet.
Enter a valid email address.
<TextInput
name="pdga"
label="PDGA number"
description="Leave blank if you're not a member yet."
/>
<TextInput name="email" label="Email" type="email" error="Enter a valid email address." />
<TextInput name="player" label="Player name" required />
<TextInput name="division" label="Division" disabled value="MA2" />Props
| Name | Type | Default | Note |
|---|---|---|---|
name | string | — | Required. Form field name. |
label | string | — | Required. Always in the DOM; sr-only with hideLabel. |
type | 'text' | 'email' | 'password' | 'tel' | 'url' | 'search' | 'number' | 'date' | 'time' | 'datetime-local' | 'text' | Date/time types render the native platform picker. |
value | string | '' | Bindable. |
placeholder | string | — | |
autocomplete | HTMLInputAttributes['autocomplete'] | — | Native autofill hint, e.g. "email", "name". |
maxlength | number | — | |
pattern | string | — | Native validation regex. |
inputmode | HTMLInputAttributes['inputmode'] | — | Virtual-keyboard hint, e.g. "numeric", "tel". |
description | string | — | Help text below the label. |
error | string | — | Inline error message; sets the error state. |
required | boolean | false | |
disabled | boolean | false | |
hideLabel | boolean | false | |
prefix | Snippet | — | Decorative leading content (icon, symbol); rendered aria-hidden. |
suffix | Snippet | — | Decorative trailing content (unit, icon); rendered aria-hidden. |
class | string | — | Merged after the hz-field class. |
Theme hooks
What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.
Root class: .hz-field
Data attributes
| Hook | Values | Styles |
|---|---|---|
data-state | 'default' | 'error' | 'disabled' | The universal field state hook, on the root. Precedence is fixed: error beats disabled. |
data-has-prefix | present when a prefix is set | On .hz-input-wrapper. The reference theme ships no rule for it — it exists so you can, e.g., retune padding when an affix is present. |
data-has-suffix | present when a suffix is set | On .hz-input-wrapper. Same: yours to use. |
Part classes
| Hook | Values | Styles |
|---|---|---|
.hz-field-label | child element | The label. |
.hz-field-required | child element | The required marker. |
.hz-field-description | child element | The hint text. |
.hz-field-error | child element | The error message. |
.hz-input-wrapper | child element | The bordered box. It carries the border, so prefix and suffix sit inside it. |
.hz-input-prefix | child element | The leading affix. |
.hz-input-suffix | child element | The trailing affix. |
Accessibility
The input is associated with its label via id/for. With hideLabel, the label stays in the DOM as screen-reader-only text.
description and error chain into aria-describedby, description first. required sets aria-required, and an error sets aria-invalid.
prefix and suffix are rendered aria-hidden. Keep them decorative, and put any meaning in the label or description instead.
References: MDN: <input>