Field
The labeled-field scaffold — label, control, and hint/error — wired for accessibility.
Last updated
We'll never share it.
Show codeHide code
<Field id="email" label="Email" hint="We'll never share it.">
<input id="email" type="email" class="w-full rounded-md border border-neutral bg-page px-3 py-2 text-sm" />
</Field>Add
npx @ava-ui/apogee add field
No Apogee dependencies. Input, Textarea, and Select render Field
internally with a matching id — use Field standalone only for a custom
control.
Usage
---
import Field from '@/components/apogee/Field/Field.astro'
---
<Field id="name" label="Name" required>
<input id="name" class="w-full rounded-md border border-neutral bg-page px-3 py-2 text-sm" />
</Field>
Anatomy
- Label — rendered when
labelis passed,for={id}; appends aaria-hidden*whenrequiredis true. - Control — the default slot. Pass your own input/textarea/select with a
matching
id, and set itsaria-describedbyto${id}-hintor${id}-errorto wire the association yourself. - Hint — rendered at
${id}-hintwhenhintis set and there’s noerror. - Error — rendered at
${id}-errorwithrole="alert"whenerroris set (takes priority overhint).
API
@composable — exposed to Content Editors in AVA. Any other attribute passes
through to the root <div>.
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string |
— | Required. Matches the control’s id; used to derive ${id}-hint / ${id}-error. |
label |
string |
— | Optional label text. |
tooltip |
string |
— | Help text behind an info icon after the label. The icon stays focusable when the control is disabled. |
hint |
string |
— | Optional helper text. Shown with the error when both are set. |
error |
string |
— | Optional error message, rendered with role="alert" after the hint. |
disabled |
boolean |
false |
Dims the label and marks the wrapper aria-disabled; disable the control too. |
size |
'small' | 'medium' | 'large' | 'xlarge' |
'medium' |
Label and hint text size, matching the control’s size. Also set as data-size on the root. |
required |
boolean |
false |
Shows a * next to the label (visual only — set required/aria-required on the control itself). |
bare |
boolean |
false |
Renders only the slot, no wrapper; the parent owns label, hint and error. InputGroup uses it. |
hideRequiredIndicator |
boolean |
false |
Hides the * while you keep required on the control (a form where every field is required). |
class |
string |
— | Extra classes on the root, merged last. |
States
- Error —
errorshows the message under the control in critical text; Input, Select and Textarea also setaria-invalid. Hint and error render together when both are set. - Disabled — the label switches to the disabled colour and the root gets
aria-disabled. Atooltiphelp icon stays focusable. - Required — an asterisk after the label;
hideRequiredIndicatorkeeps the semantics without it.
Examples
With a hint:
Letters, numbers, and dashes only.
Show codeHide code
<Field id="username" label="Username" hint="Letters, numbers, and dashes only.">
<input id="username" class="w-full rounded-md border border-neutral bg-page px-3 py-2 text-sm" />
</Field>With an error:
Must be at least 8 characters.
Show codeHide code
<Field id="pw" label="Password" required error="Must be at least 8 characters.">
<input id="pw" type="password" class="w-full rounded-md border border-critical bg-page px-3 py-2 text-sm" />
</Field>Hint and error together (the control lists both ids in aria-describedby):
At least 8 characters.
Too short.
Show codeHide code
<Field id="pw2" label="Password" hint="At least 8 characters." error="Too short.">
<input id="pw2" type="password" aria-describedby="pw2-hint pw2-error" aria-invalid="true" class="…" />
</Field>A help tooltip after the label. The icon is a button outside the <label>, so
keyboard users reach it even when the control is disabled:
Rotates every 90 days.
Show codeHide code
<Field id="api" label="API key" tooltip="Found under Settings → Developers." hint="Rotates every 90 days.">
<input id="api" disabled class="…" />
</Field>
<Field id="all" label="Email" required hideRequiredIndicator>
<input id="all" type="email" required class="…" />
</Field>Custom control (a Field wrapping something other than a native input) — set
aria-describedby yourself since Field only renders the ids, it doesn’t wire them:
<Field id="plan" label="Plan">
<select id="plan" aria-describedby="plan-hint" class="w-full rounded-md border border-neutral bg-page px-3 py-2 text-sm">
<option>Free</option>
<option>Pro</option>
</select>
</Field>
Sizes and disabled
Hint text scales with small.
Hint text scales with medium.
Hint text scales with large.
Hint text scales with xlarge.
The control is disabled too.
Show codeHide code
<Field id="f-s" label="Small" size="small"><input id="f-s" class="…" /></Field>
… medium, large, xlarge …
<Field id="f-d" label="Disabled" disabled><input id="f-d" disabled class="…" /></Field>Accessibility
labelis a real<label for={id}>— clicking it focuses the control.- The error message uses
role="alert"so screen readers announce it when it appears. Fieldrenders the hint/error ids but doesn’t setaria-describedbyon your control — since the control is an opaque slot, wire that attribute yourself (Input/Textarea/Selectdo this for you).- The
*forrequiredisaria-hidden(decorative) — set the realrequired/aria-requiredattribute on the control.hideRequiredIndicatorremoves only the glyph. tooltiprenders abuttonwitharia-label="More information"beside the label, outside it, so it stays in the tab order when the control is disabled; Tooltip describes the button, not the control.
Tokens
| Token | Used for |
|---|---|
--foreground-neutral |
Label text |
--foreground-neutral-faded |
Hint text |
--foreground-critical |
Required * and error text |
Do / Don’t
- Do keep the
idonFieldand your control in sync — it’s how hint/error ids line up. - Do use
errorinstead ofhintonce a field is invalid; don’t show both. - Don’t nest more than one control per
Field— one label should describe one input. - Don’t rely on the visual
*alone to mark a field required — set the attribute on the control too.
Content
- Label is a short noun phrase (1–3 words), sentence case, no ending colon or period.
- Hint is one short sentence explaining format or constraints, not a duplicate of the label.
- Error names the specific problem and, where possible, the fix (“Must be at least 8 characters”), one sentence.