# Input

> A labeled text field with hint/error messaging, wired for accessibility — plus a pure-CSS floating label.

URL: https://apogee.avaui.dev/docs/input
Last updated: 2026-10-15

A labeled text field with hint/error messaging, wired for accessibility — plus a pure-CSS floating label.

Last updated October 15, 2026

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.

```astro
<Input label="Email" type="email" placeholder="you@example.com" />
```

## Add

```sh
npx @ava-ui/apogee add input
```

Pulls in `Field` (label/hint/error wiring).

## Usage

```astro
---
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](https://apogee.avaui.dev/docs/input-group): 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

```astro
<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:

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

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

## Password reveal

```astro
<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.

```astro
<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.

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

```astro
<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.

```astro
<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”.

## Related

[

FieldThe labeled-field scaffold — label, control, and hint/error — wired for accessibility.

](https://apogee.avaui.dev/docs/field)[

TextareaA labeled multiline input, wired the same way as Input — label, hint, and error.

](https://apogee.avaui.dev/docs/textarea)[

NumberInputA labeled numeric field with styled −/+ steppers, wired like Input.

](https://apogee.avaui.dev/docs/number-input)[

PinInputA row of single-character boxes for short codes (OTP, 2FA).

](https://apogee.avaui.dev/docs/pin-input)
