Skip to main content

Field

Info:Form html svelte astro Success: coverage 32/32

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

Live demo

live · @xoji/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.

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 .xoji-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.

Props

13 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.)

Appearance

Variants

default

.xoji-field

The standard field: neutral border, accent focus ring.

invalid

.xoji-field--invalid

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

Sizes

sm

.xoji-field--sm

Compact.

md

default
.xoji-field

Default.

lg

.xoji-field--lg

Large.

States

focus-visible

.xoji-field__control:focus-within

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

invalid

.xoji-field--invalid .xoji-field__control

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

disabled

.xoji-field--disabled .xoji-field__control

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

readonly

.xoji-field--readonly .xoji-field__control

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

required

.xoji-field__required

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

Anatomy

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

field

.xoji-field

The outer column wrapper carrying the size and state modifiers.

--font-sans --space-1

label

.xoji-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

.xoji-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

.xoji-field__input

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

adornment

.xoji-field__adornment

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

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

description

.xoji-field__description

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

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

error

.xoji-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 32/32 consumed tokens produced default register: 276 tokens

Live coverage check against the xoji-default register (derive(xojiDefault, { 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-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.

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

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

<xoji-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>
</xoji-field>

<xoji-field label="Amount" name="amount" type="number" invalid error="Must be greater than zero.">
	<span slot="prefix">$</span>
</xoji-field>
<script lang="ts">
	import { Field } from "@xoji/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 "@xoji/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>