Skip to main content

Field

Info:Form
33

A labelled text input with helper text, validation, adornments, and built-in clear and reveal actions.

input text input text field search box textbox form input

Live demo

live · @xtyle/astro

Field

A signed-in form

We'll only use this to sign you in.
At least 12 characters.

Adornments

$USD

Sizes

States

Use only letters, numbers, and dashes.
Generated once. Copy it now.

Type-ahead

Suggestions come from the field's own datalist, so they work inside the shadow root.

Code-shaped

A mono field for hex, ids, and paths; spellcheck is forwarded off.

Field is the complete single-line text input: a label, an input, a persistent description, and an error message wired together with the right accessibility relationships out of the box. The input inherits the shared .xtyle-control chrome and adds field-specific layout: a unified control box that holds leading and trailing adornment slots (icons, currency prefixes, unit suffixes) alongside the input.

It generates a stable id and links it to the label, builds aria-describedby from both the description and the error, and ships two optional built-in actions: a clear button and a password reveal toggle. In the HTML and Svelte bindings the element is form-associated, participating in native form submission and constraint validation. Sizes (sm / md / lg) match the Button padding scale, and readonly, required, disabled, and invalid are all first-class.

When to use

How this component composes with the rest of the set.

Pair with Button (type="submit") inside a native <form>; the form-associated element submits its value by name.
Use the prefix / suffix slots with any inline SVG or text node; they'll take an Icon component directly when one ships.
The clear button and password reveal are JS-driven, so they appear in the html and svelte bindings; the Astro binding is zero-JS and omits them.
For type-ahead, pass options rather than a hand-authored <datalist>; a page-level datalist can't reach the input across the shadow boundary, so Field owns the list itself.
Native input attributes (spellcheck, inputmode, autocomplete, autocapitalize, autocorrect, enterkeyhint) set on the host are forwarded to the inner input, so spellcheck="false" on a code field works without reaching through ::part.

Props

15 props, straight from the manifest.

PropTypeDefaultBindingsDescription
label string
html svelte astro
The visible label text, linked to the input. When omitted, falls back to an `aria-label` from the placeholder.
name string
html svelte astro
The form field name. Drives form submission and the form-associated value.
value string
html svelte astro
The input value. Two-way bindable in Svelte (`bind:value`).
type string text
html svelte astro
The native input type (text, email, password, number, …). `password` enables the reveal toggle.
placeholder string
html svelte astro
Placeholder text shown when empty.
size Size
sm md lg
md
html svelte astro
Control size; matches the Button padding scale.
description string
html svelte astro
Persistent helper text below the control, linked via `aria-describedby`.
error string
html svelte astro
Validation message; shown (and linked via `aria-describedby`) only while `invalid`.
invalid boolean false
html svelte astro
Marks the field invalid: danger border, error message, `aria-invalid`, and a custom validity in form-associated mode.
required boolean false
html svelte astro
Marks the field required: a `*` indicator, `required` + `aria-required`, and a valueMissing validity.
readonly boolean false
html svelte astro
Renders the input read-only with a muted background; the value still submits.
disabled boolean false
html svelte astro
Disables the input and dims the field.
clearable boolean false
html svelte
Shows a built-in clear button when the field has a value. (html / svelte: needs JS.)
options string[] | { value: string; label?: string }[]
html svelte astro
Type-ahead suggestions. Field renders them into a `<datalist>` it owns inside the input's own root and wires the input's `list` to it, so suggestions work even though the input lives in a shadow root (where a page-level `<datalist>` could never reach it). Set the JS property in html / svelte; the Astro binding takes a JSON array attribute. Needs the runtime. (html / svelte / astro on hydrate.)
mono boolean false
html svelte astro
Renders the input in the monospace stack (`--font-mono`) for code-shaped values: identifiers, paths, hex colors, expressions, env values. A presentational swap on an existing token, so it bakes into the zero-JS Astro markup too.

Appearance

Variants

default

.xtyle-field

The standard field: neutral border, accent focus ring.

invalid

.xtyle-field--invalid

Error treatment: danger border, a danger-tinted focus ring, and the error message shown.

Sizes

sm

.xtyle-field--sm

Compact.

md

default
.xtyle-field

Default.

lg

.xtyle-field--lg

Large.

States

focus-visible

.xtyle-field__control:focus-within

Keyboard focus inside the control: accent border and a token-colored ring via :focus-within.

invalid

.xtyle-field--invalid .xtyle-field__control

Invalid: danger border; the focus-within ring shifts to a danger tint.

disabled

.xtyle-field--disabled .xtyle-field__control

Non-interactive: muted ink and a disabled-tinted control.

readonly

.xtyle-field--readonly .xtyle-field__control

Read-only: a subtly recessed background; the value still submits.

required

.xtyle-field__required

Required: a danger-colored * indicator on the label.

Anatomy

The named parts that make up the component, with their selectors.

field

.xtyle-field

The outer column wrapper carrying the size and state modifiers.

--font-sans --space-1

label

.xtyle-field__label

The field label, linked to the input via for; carries the required indicator.

--text-sm --weight-medium --fg-1 --leading-normal --space-1

control

.xtyle-field__control

The bordered box that wraps the input and its adornments; paints the focus-within ring.

--field-bg --field-border --border-thin --radius-md --accent --ring --border-normal

input

.xtyle-field__input

The native input, styled by .xtyle-control with its own chrome neutralized so the control box owns the border.

adornment

.xtyle-field__adornment

A leading (prefix) or trailing (suffix) slot for icons, currency symbols, or units.

--space-3 --fg-2 --text-sm --leading-tight

description

.xtyle-field__description

Persistent helper text below the control, linked via aria-describedby.

--text-sm --leading-normal --fg-2

error

.xtyle-field__error

The validation message shown when invalid, linked via aria-describedby.

--text-sm --leading-normal --danger

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 33/33 consumed tokens produced default register: 299 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --bg-1 --border-normal --border-thin --danger --danger-bg --danger-text --duration-fast --ease-standard --fg-0 --fg-1 --fg-2 --fg-disabled --field-bg --field-border --font-mono --font-sans --leading-normal --leading-tight --radius-md --radius-sm --ring --space-1 --space-2 --space-3 --space-4 --state-disabled --state-hover --state-press --text-lg --text-sm --weight-medium --weight-semibold

Slots

prefix
html svelte astro

A leading adornment: icon, currency symbol, or unit.

suffix
html svelte astro

A trailing adornment: icon, unit, or status marker.

clear-icon
html

Overrides the built-in clear-button glyph.

reveal-icon
html

Overrides the built-in password-reveal glyph.

Accessibility

Always generates a stable input id and links the label with for, so clicking the label focuses the input.
aria-describedby is built from both the description and the error, so assistive tech announces helper text and validation together.
invalid sets aria-invalid; required sets aria-required and a non-color * indicator so requirement is not conveyed by color alone.
When no label is given, the placeholder (or an explicit aria-label) becomes the accessible name; the binding warns when none of the three is present.
Focus is shown on the control via :focus-within with a token ring plus the transparent-outline pattern that forced-colors mode promotes to a real outline.
Form-associated (html / svelte): participates in native submission and constraint validation via setFormValue and setValidity.

Code

Labels, validation, and adornments

A required email, a password with helper text, a clearable search with a leading icon, and an invalid amount with a currency prefix.

<xtyle-field label="Email" name="email" type="email" placeholder="you@example.com" required></xtyle-field>

<xtyle-field
	label="Password"
	name="password"
	type="password"
	description="At least 12 characters."
	required
></xtyle-field>

<xtyle-field
	label="Search"
	name="q"
	placeholder="Search…"
	clearable
>
	<svg slot="prefix" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
		<path fill="currentColor" d="M10 4a6 6 0 1 0 3.5 10.9l4.3 4.3 1.4-1.4-4.3-4.3A6 6 0 0 0 10 4Zm0 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z" />
	</svg>
</xtyle-field>

<xtyle-field label="Amount" name="amount" type="number" invalid error="Must be greater than zero.">
	<span slot="prefix">$</span>
</xtyle-field>
<script lang="ts">
	import { Field } from "@xtyle/svelte";
	let email = $state("");
</script>

<Field label="Email" name="email" type="email" placeholder="you@example.com" required bind:value={email} />

<Field
	label="Password"
	name="password"
	type="password"
	description="At least 12 characters."
	required
/>

<Field label="Search" name="q" placeholder="Search…" clearable>
	{#snippet prefix()}
		<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
			<path fill="currentColor" d="M10 4a6 6 0 1 0 3.5 10.9l4.3 4.3 1.4-1.4-4.3-4.3A6 6 0 0 0 10 4Zm0 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z" />
		</svg>
	{/snippet}
</Field>

<Field label="Amount" name="amount" type="number" invalid error="Must be greater than zero.">
	{#snippet prefix()}<span>$</span>{/snippet}
</Field>
---
import { Field } from "@xtyle/astro";
---

<Field label="Email" name="email" type="email" placeholder="you@example.com" required />

<Field
	label="Password"
	name="password"
	type="password"
	description="At least 12 characters."
	required
/>

<Field label="Search" name="q" placeholder="Search…">
	<svg slot="prefix" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
		<path fill="currentColor" d="M10 4a6 6 0 1 0 3.5 10.9l4.3 4.3 1.4-1.4-4.3-4.3A6 6 0 0 0 10 4Zm0 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z" />
	</svg>
</Field>

<Field label="Amount" name="amount" type="number" invalid error="Must be greater than zero.">
	<span slot="prefix">$</span>
</Field>

Type-ahead suggestions

Pass options and Field renders a <datalist> in the input's own root, so suggestions work across the shadow boundary where a page-level datalist can't reach.

<xtyle-field id="branch" label="Branch" name="branch" placeholder="Type to filter…"></xtyle-field>
<script>
	document.getElementById("branch").options = ["main", "develop", "release/1.0", "hotfix"];
</script>
<script lang="ts">
	import { Field } from "@xtyle/svelte";
	const branches = ["main", "develop", "release/1.0", "hotfix"];
</script>

<Field label="Branch" name="branch" placeholder="Type to filter…" options={branches} />
---
import { Field } from "@xtyle/astro";
const branches = ["main", "develop", "release/1.0", "hotfix"];
---

<Field label="Branch" name="branch" placeholder="Type to filter…" options={branches} />