Field & Label
Field wraps one control with its label, hint and error and wires them together: for/id, aria-describedby, aria-invalid. Use it for every form control.
Example
Type something invalid and leave the field.
We send your ticket here.
<Field label="Email" hint="We send your ticket here." :error="emailError">
<Input v-model="email" type="email" autocomplete="email" />
</Field>Anatomy
So the teacher can tag you.
Use your handle, e.g. @ana.dances- Label — text-sm bold, above. "(optional)" when not required.
- Control — Input, Select, Textarea or Checkbox.
- Hint — text-xs muted. Stays visible while the error shows.
- Error — FieldError, text-xs bold red, at
${id}-error.
With useFormValidation
Pass the same id to Field and to fieldAttrs. The control merges both aria-describedby values without duplicates.
<script setup lang="ts">
const { errors, validate, fieldAttrs } = useFormValidation(BookingSchema, form)
</script>
<Field id="book-email" label="Email" :error="errors.email">
<!-- fieldAttrs registers the id so a failed submit focuses this field -->
<Input v-model="form.email" type="email" v-bind="fieldAttrs('email', 'book-email-error')" />
</Field>Custom controls
Anything else can read the wiring from the slot.
Used on your festival page.
<Field label="Colour" v-slot="{ id, describedBy, invalid }">
<input :id="id" type="color" :aria-describedby="describedBy" :aria-invalid="invalid || undefined">
</Field>Label on its own
Field uses Label internally. Use Label directly only for layouts Field can't express (e.g. a label beside a checkbox without the slot).
<Label for="promo">Promo code</Label>
<Input id="promo" />Props
Field
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Visible label above the control. |
| id | string | auto | Control id. Error id = `${id}-error`, hint id = `${id}-hint`. Pass one when using useFormValidation. |
| hint | string | — | Help text below the control, always visible, linked via aria-describedby. |
| error | string | — | Error message. Truthy = the control gets aria-invalid and the red border. |
| optional | boolean | false | Adds a quiet "(optional)" to the label. |
| required | boolean | false | Sets aria-required on the control. No visual mark — we mark optional fields instead. |
| disabled | boolean | false | Disables the control inside. |
| hideLabel | boolean | false | Keeps the label for screen readers only. Rare — e.g. a search field next to a "Search" button. |
| slot props | { id, describedBy, invalid } | — | For custom controls that are not Input/Select/Textarea/Checkbox. |
Label
| Prop | Type | Default | Description |
|---|---|---|---|
| for | string | — | id of the control. |
| optional | boolean | false | Appends "(optional)". |
Do & don't
With country code, e.g. +49…
Add the country code.Do — Keep the hint visible when an error appears — it often explains how to fix it.
Something went wrong
Don't — Put the error in a toast or at the top only. It has to sit next to the field it is about.
Accessibility
- The label is a real
label for— clicking it focuses the control. aria-describedbylists the error first, then the hint, so the problem is read out before the help.- The error is plain text next to the field; the red border is a second signal, never the only one.
- useFormValidation focuses the first invalid field on submit via the error id.