Field
Layout components for form labels, descriptions, errors and groups of controls - the building blocks every AutoForm field is made of.
Preview
Usage
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
FieldTitle,
} from '@vitnode/core/components/ui/field'<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" />
<FieldDescription>Pick something your friends can type.</FieldDescription>
</Field>Building a form? Use AutoForm. It already renders every
field with these parts, ids and ARIA included. Reach for Field in custom
AutoForm fields and settings that save
instantly.
Anatomy
| Component | Renders |
|---|---|
FieldSet | A <fieldset> grouping related fields. |
FieldLegend | The fieldset's <legend>. variant="label" makes it label-sized. |
FieldGroup | A stack of fields with even spacing. Enables responsive. |
Field | One control with its label, description and error. |
FieldContent | Keeps a label and description together beside a control. |
FieldLabel | The control's <label>. Wrap a Field in it for a choice card. |
FieldTitle | A label-styled title for content already inside a FieldLabel. |
FieldDescription | Muted helper text. |
FieldError | An error message with role="alert". |
FieldSeparator | A divider, optionally with text in the middle. |
Orientation
Field is vertical by default. Use horizontal for checkboxes and switches.
The markup order decides the side: control first puts it on the left.
<Field orientation="horizontal">
<FieldContent>
<FieldLabel htmlFor="digest">Weekly digest</FieldLabel>
<FieldDescription>A short recap, every Monday.</FieldDescription>
</FieldContent>
<Switch id="digest" />
</Field>Responsive
responsive stacks on narrow screens and goes side by side once its
FieldGroup is wide enough. It's a container query, so it follows the group's
width, not the window's.
<FieldGroup>
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="name">Display name</FieldLabel>
<FieldDescription>Shown next to your posts.</FieldDescription>
</FieldContent>
<Input id="name" />
</Field>
</FieldGroup>Fieldset and legend
Screen readers read the legend before each field inside, so "Mentions" becomes "Notifications, Mentions". Free context, zero extra pixels.
<FieldSet>
<FieldLegend>Notifications</FieldLegend>
<FieldGroup>
<Field orientation="horizontal">
<Checkbox id="mentions" />
<FieldLabel htmlFor="mentions">Mentions</FieldLabel>
</Field>
<FieldSeparator>or</FieldSeparator>
<Field>
<FieldLabel htmlFor="backup-email">Backup email</FieldLabel>
<Input id="backup-email" type="email" />
</Field>
</FieldGroup>
</FieldSet>Choice cards
Wrap a Field in a FieldLabel and the whole card becomes clickable. It
highlights when checked and shows a focus ring. Use FieldTitle inside, since
you're already in a label.
<RadioGroup defaultValue="team">
<FieldLabel htmlFor="plan-team">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Team</FieldTitle>
<FieldDescription>
Shared workspaces for up to 20 people.
</FieldDescription>
</FieldContent>
<RadioGroupItem id="plan-team" value="team" />
</Field>
</FieldLabel>
</RadioGroup>To disable a card, put data-disabled on the Field and disabled on the
control. AutoForm's Radio Group uses this for
variant="blocks".
Errors
Set data-invalid on the field, aria-invalid on the control, and point
aria-describedby at the FieldError. The preview above shows it live.
<Field data-invalid={isInvalid}>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input aria-describedby="email-error" aria-invalid={isInvalid} id="email" />
{isInvalid && <FieldError id="email-error">That is not an email.</FieldError>}
</Field>FieldError also takes errors. It drops duplicates, shows one message as
text and several as a list, and renders nothing when empty.
<FieldError errors={[{ message: 'Too short' }, { message: 'Too short' }]} />Inside a custom AutoForm field
Mix Field with AutoForm's parts and your field matches the built-in ones:
AutoFormLabelis aFieldLabelpointed at the control, with the "Optional" hint.AutoFormDescrenders the description.FormControlpassesid,aria-invalidandaria-describedbyto its child.FormMessageis aFieldErrorwith the field's validation errors.
import type { ItemAutoFormComponentProps } from '@vitnode/core/components/form/auto-form'
import { AutoFormDesc } from '@vitnode/core/components/form/common/desc'
import { AutoFormLabel } from '@vitnode/core/components/form/common/label'
import { Field, FieldContent } from '@vitnode/core/components/ui/field'
import { FormControl, FormMessage } from '@vitnode/core/components/ui/form'
import { Switch } from '@vitnode/core/components/ui/switch'
export const AutoFormSettingSwitch = ({
label,
description,
field,
otherProps: { isOptional },
}: ItemAutoFormComponentProps) => (
<>
<Field orientation="horizontal">
<FieldContent>
{label && (
<AutoFormLabel isOptional={isOptional}>{label}</AutoFormLabel>
)}
{description && <AutoFormDesc>{description}</AutoFormDesc>}
</FieldContent>
<FormControl>
<Switch
checked={field.value ?? false}
onCheckedChange={field.onChange}
/>
</FormControl>
</Field>
<FormMessage />
</>
)Use it like any field: component: props => <AutoFormSettingSwitch {...props} label="Weekly digest" />.
Accessibility
- Give every control an
idand itsFieldLabela matchinghtmlFor. Clicking the label then focuses or toggles the control. Fieldhasrole="group", so control, label and help text are announced together.FieldErrorhasrole="alert", so new messages are read out as they appear.- Choice cards keep the real radio or checkbox inside. Tab, arrow keys and Space work as usual.
Props
Every part accepts the props of the element it renders, plus:
Prop
Type