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

€
kg
https://
Show code
<Input label="Price" prefix="€" type="number" />
<Input label="Weight" suffix="kg" type="number" />
<Input label="Site" prefix="https://" />

Password reveal

Show code
<Input label="Password" type="password" revealable autocomplete="current-password" />

States

  • Hover / focus — border/token shift on hover; a visible focus-visible ring on focus.
  • Disabled — dimmed, disabled:cursor-not-allowed disabled:opacity-50.
  • Required — a --background-critical-coloured * next to the label.
  • Error — --background-critical border, aria-invalid="true", error message replaces the hint.

Letters, numbers, and underscores only

Show 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 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 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 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 in variant="floating".

  • aria-describedby points to the hint or error message id (<id>-hint / <id>-error); error takes priority when both are set.

  • error sets aria-invalid="true" on the input and role="alert" on the message, so screen readers announce it immediately.

  • Focus turns the border primary and thickens it inward (--border-primary); buttons in the end slot (clear, reveal) show the --outline-color-focus ring.

  • variant="floating" is pure CSS (:placeholder-shown + peer) — works identically with JS off.

  • No JS.

  • prefix and suffix text is visual only; a screen reader does not read it with the field. Put units in the label or in aria-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 on placeholder alone.
  • Do use hint for guidance before an error occurs, and error to replace it once validation fails.
  • Don’t pass placeholder with variant="floating" — it’s ignored; the label fills that role.
  • Don’t mark a field required without validating it — surface real errors via error, not just the asterisk.

Content

  • Labels are short noun phrases, sentence case, no colon (“Email”, not “Email:”).
  • hint states the rule before it’s broken (“At least 8 characters”); error states what’s wrong and how to fix it (“Enter a valid email address”), never a generic “Invalid input”.