Input
A labeled text field with hint/error messaging, wired for accessibility — plus a pure-CSS floating label.
Last updated
Uses JavaScript
Only revealable uses JS: a few lines flip a password field between masked and shown when its eye button is pressed.
Without JS: The field is a plain password input; the eye button is hidden, so the value stays masked.
Show codeHide code
<Input label="Email" type="email" placeholder="you@example.com" />Add
npx @ava-ui/apogee add input
Pulls in Field (label/hint/error wiring).
Usage
---
import Input from '@/components/apogee/Input/Input.astro'
---
<Input label="Email" type="email" required />
API
@composable — exposed to Content Editors in AVA. Any other native <input>
attribute (placeholder, name, value, min/max, autocomplete,
pattern, disabled, …) passes through to the <input>.
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string |
auto-generated (apogee-<random>) |
Field id; also used to derive the aria-describedby target. |
label |
string |
— | Field label. With labelPosition="floating", doubles as the floating placeholder text. |
hint |
string |
— | Helper text below the field. Hidden when error is set. |
error |
string |
— | Error text below the field. Sets aria-invalid="true" on the input and role="alert" on the message. |
type |
'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' |
'text' |
Native input type. |
size |
'small' | 'medium' | 'large' | 'xlarge' |
'medium' |
Height: 28 / 36 / 48 / 56px. |
variant |
'outline' | 'faded' | 'ghost' | 'headless' |
'outline' |
The box style: bordered, tinted, bare, or no box at all. |
hasError |
boolean |
false |
Marks the field invalid (aria-invalid, critical border) without an error message. |
labelPosition |
'top' | 'floating' |
'top' |
floating animates the label onto the border on focus / when filled — pure CSS. Ignores a passed placeholder (it manages its own). |
prefix |
string |
— | Text before the value (currency sign, protocol); takes the start slot’s place. |
suffix |
string |
— | Text after the value (unit); takes the end slot’s place. |
grouped |
boolean |
false |
Inside an InputGroup: no label, hint or error of its own. |
revealable |
boolean |
false |
With type="password": an eye button (“Show password”, aria-pressed) in the end slot shows or hides the value. |
required |
boolean |
false |
Marks the field required (native required + a visible * next to the label). |
class |
string |
— | Extra classes, merged onto the outer field wrapper. |
Variants
Show codeHide code
<Input label="Email" type="email" placeholder="you@example.com" />
<Input label="Email" type="email" labelPosition="floating" />Sizes
Every size. Sizes interact with the floating label — the label’s inset shifts with the control’s padding — shown together:
Show codeHide code
<Input label="Small" size="small" />
<Input label="Medium" />
<Input label="Large" size="large" />
<Input label="Small" size="small" labelPosition="floating" />
<Input label="Medium" labelPosition="floating" />
<Input label="Large" size="large" labelPosition="floating" />Affixes
Show codeHide code
<Input label="Price" prefix="€" type="number" />
<Input label="Weight" suffix="kg" type="number" />
<Input label="Site" prefix="https://" />Password reveal
Show codeHide code
<Input label="Password" type="password" revealable autocomplete="current-password" />States
- Hover / focus — border/token shift on hover; a visible
focus-visiblering on focus. - Disabled — dimmed,
disabled:cursor-not-allowed disabled:opacity-50. - Required — a
--background-critical-coloured*next to the label. - Error —
--background-criticalborder,aria-invalid="true", error message replaces the hint.
Enter a valid email address
Letters, numbers, and underscores only
Show codeHide code
<Input label="Disabled" disabled value="Can't edit" />
<Input label="Email" required error="Enter a valid email address" value="not-an-email" />
<Input label="Username" hint="Letters, numbers, and underscores only" />Examples
A clear button in the end slot: a CloseButton labelled for what it does. A
button in either slot sits 4px from the field’s edge; text keeps its inset.
Show codeHide code
<Input label="Search" id="q" value="astro">
<CloseButton slot="end" size="small" label="Clear search" onclick="const i = document.getElementById('q'); i.value = ''; i.focus()" />
</Input>At least 8 characters
Show codeHide code
<Input label="Password" type="password" hint="At least 8 characters" required />
<Input label="Search" type="search" labelPosition="floating" />Date inputs
Use the native pickers: type="date", type="time" and
type="datetime-local" give every platform its own accessible picker with
zero JS. The field takes the theme: its segments highlight in the primary
tint while focused, and the calendar button fades in on hover. The popup
calendar is browser UI and cannot be styled; it follows the theme’s
color-scheme (light or dark) and, in Chromium, the primary accent. A range is two dates whose min and max point at each other on
the server.
Show codeHide code
<Input type="date" label="Start date" />
<Input type="time" label="Reminder" step="900" />
<Input type="datetime-local" label="Meeting" />
<div class="flex gap-3">
<Input type="date" label="From" value="2026-10-15" max="2026-12-31" />
<Input type="date" label="To" min="2026-10-15" />
</div>Accessibility
-
Every control is at least 16px text on viewports below
md, so focusing a field never zooms the page on a phone; desktop sizes keep their smaller type. -
Real
<label for>/<input id>pairing — always programmatically associated, including invariant="floating". -
aria-describedbypoints to the hint or error message id (<id>-hint/<id>-error); error takes priority when both are set. -
errorsetsaria-invalid="true"on the input androle="alert"on the message, so screen readers announce it immediately. -
Focus turns the border primary and thickens it inward (
--border-primary); buttons in theendslot (clear, reveal) show the--outline-color-focusring. -
variant="floating"is pure CSS (:placeholder-shown+peer) — works identically with JS off. -
No JS.
-
prefixandsuffixtext is visual only; a screen reader does not read it with the field. Put units in the label or inaria-describedby.
Tokens
| Token | Used for |
|---|---|
--border-neutral / --background-page / --foreground-neutral |
Input border, fill, text |
--foreground-neutral-faded |
Placeholder, hint text, floating label (idle) |
--foreground-critical |
Error border, error text, required * |
--foreground-primary |
Floating label colour on focus |
--border-primary / --outline-color-focus |
Field focus border / focus ring on end-slot buttons |
Do / Don’t
- Do always pass a
label— it’s the field’s accessible name; don’t rely onplaceholderalone. - Do use
hintfor guidance before an error occurs, anderrorto replace it once validation fails. - Don’t pass
placeholderwithvariant="floating"— it’s ignored; the label fills that role. - Don’t mark a field
requiredwithout validating it — surface real errors viaerror, not just the asterisk.
Content
- Labels are short noun phrases, sentence case, no colon (“Email”, not “Email:”).
hintstates the rule before it’s broken (“At least 8 characters”);errorstates what’s wrong and how to fix it (“Enter a valid email address”), never a generic “Invalid input”.