Import
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
FieldTitle,
} from "@timelycare/helix-ui"
Props
interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
orientation?: "vertical" | "horizontal" | "responsive"
className?: string
children: React.ReactNode
}
interface FieldLabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
htmlFor?: string
className?: string
children: React.ReactNode
}
interface FieldDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {
className?: string
children: React.ReactNode
}
interface FieldErrorProps extends React.HTMLAttributes<HTMLParagraphElement> {
className?: string
children: React.ReactNode
}
interface FieldGroupProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string
children: React.ReactNode
}
interface FieldSetProps extends React.HTMLAttributes<HTMLFieldSetElement> {
className?: string
children: React.ReactNode
}
interface FieldLegendProps extends React.HTMLAttributes<HTMLLegendElement> {
variant?: "default" | "label"
className?: string
children: React.ReactNode
}
interface FieldContentProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string
children: React.ReactNode
}
interface FieldTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {
className?: string
children: React.ReactNode
}
interface FieldSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string
}
Styling
Typography
| Element | Font | Size | Weight | Color |
|---|---|---|---|---|
| Label | Adelle Sans Medium | 14px (text-sm) | font-medium | text-foreground |
| Legend | Adelle Sans Semibold | 16px (text-base) | font-semibold | text-foreground |
| Legend (label variant) | Adelle Sans Medium | 14px (text-sm) | font-medium | text-foreground |
| Title | Adelle Sans Medium | 14px (text-sm) | font-medium | text-foreground |
| Description | Adelle Sans Regular | 14px (text-sm) | font-normal | text-muted-foreground |
| Error | Adelle Sans Regular | 14px (text-sm) | font-normal | text-destructive |
Spacing
| Property | Value | Tailwind |
|---|---|---|
| Field gap (vertical) | 8px | gap-2 |
| FieldGroup gap | 16px | gap-4 |
| FieldSeparator | 1px border | border-t border-border |
| Label to input | 8px | Handled by Field's gap-2 |
Orientation
| Orientation | Behavior |
|---|---|
vertical (default) | Label stacks above input |
horizontal | Label and input sit side-by-side |
responsive | Vertical on mobile, horizontal on desktop (container-based) |
Sub-Components
| Component | Purpose |
|---|---|
Field | Individual field wrapper (label + control + description/error) |
FieldLabel | Form label with htmlFor association |
FieldDescription | Helper text below or near the control |
FieldError | Validation error message in text-destructive. When passed an errors array, duplicate messages are automatically deduplicated before rendering. |
FieldGroup | Groups multiple fields with consistent spacing |
FieldSet | Semantic <fieldset> wrapper for related fields |
FieldLegend | <legend> for a FieldSet with heading styling |
FieldContent | Wrapper for label + description when using horizontal/responsive layout |
FieldTitle | Title text within a selectable field (e.g., radio card) |
FieldSeparator | Visual divider between field sections |
Common Patterns
Basic Input Field
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" placeholder="Enter username" />
<FieldDescription>Choose a unique username for your account.</FieldDescription>
</Field>
With Validation Error
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input
id="email"
placeholder="m@example.com"
className="border-destructive focus-visible:ring-destructive/50"
/>
<FieldError>Please enter a valid email address.</FieldError>
</Field>
Grouped Fields with FieldSet
<FieldSet>
<FieldLegend>Address Information</FieldLegend>
<FieldDescription>We need your address to deliver your order.</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel htmlFor="street">Street Address</FieldLabel>
<Input id="street" placeholder="123 Main St" />
</Field>
<div className="grid grid-cols-2 gap-4">
<Field>
<FieldLabel htmlFor="city">City</FieldLabel>
<Input id="city" placeholder="New York" />
</Field>
<Field>
<FieldLabel htmlFor="zip">Postal Code</FieldLabel>
<Input id="zip" placeholder="90502" />
</Field>
</div>
</FieldGroup>
</FieldSet>
Horizontal Checkbox
<Field orientation="horizontal">
<Checkbox id="terms" />
<FieldLabel htmlFor="terms" className="font-normal">
I agree to the terms and conditions
</FieldLabel>
</Field>
Checkbox Group with FieldSet
<FieldSet>
<FieldLegend variant="label">Notification Preferences</FieldLegend>
<FieldDescription>Select how you'd like to be notified.</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox id="email-notif" />
<FieldLabel htmlFor="email-notif" className="font-normal">Email</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="sms-notif" />
<FieldLabel htmlFor="sms-notif" className="font-normal">SMS</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
Responsive Layout
<FieldSet>
<FieldLegend>Profile</FieldLegend>
<FieldDescription>Fill in your profile information.</FieldDescription>
<FieldSeparator />
<FieldGroup>
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="name">Name</FieldLabel>
<FieldDescription>Provide your full name</FieldDescription>
</FieldContent>
<Input id="name" placeholder="Jane Doe" required />
</Field>
<FieldSeparator />
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="bio">Bio</FieldLabel>
<FieldDescription>Keep it under 100 characters.</FieldDescription>
</FieldContent>
<Textarea id="bio" placeholder="Tell us about yourself" className="resize-none" />
</Field>
</FieldGroup>
</FieldSet>
Selectable Radio Card
<FieldSet>
<FieldLabel htmlFor="plan">Choose a Plan</FieldLabel>
<RadioGroup defaultValue="basic">
<FieldLabel htmlFor="basic">
<Field orientation="horizontal" className="rounded-lg bg-input-bg">
<FieldContent>
<FieldTitle>Basic</FieldTitle>
<FieldDescription>Great for getting started.</FieldDescription>
</FieldContent>
<RadioGroupItem value="basic" id="basic" />
</Field>
</FieldLabel>
<FieldLabel htmlFor="pro">
<Field orientation="horizontal" className="rounded-lg bg-input-bg">
<FieldContent>
<FieldTitle>Pro</FieldTitle>
<FieldDescription>For power users and teams.</FieldDescription>
</FieldContent>
<RadioGroupItem value="pro" id="pro" />
</Field>
</FieldLabel>
</RadioGroup>
</FieldSet>
Accessibility
FieldLabelrenders a<label>withhtmlForassociation to the inputFieldSetrenders a semantic<fieldset>elementFieldLegendrenders a<legend>for the fieldsetFieldDescriptionshould be linked viaaria-describedbyon the inputFieldErrorshould be linked viaaria-describedbyand the input should havearia-invalid="true"- Keyboard: Tab moves between fields; Space/Enter activates checkboxes and radios
Gotchas
| Problem | Solution |
|---|---|
| Label not linked to input | Ensure htmlFor on FieldLabel matches the input id |
| Error message not announced | Add aria-describedby pointing to the error element |
| Responsive layout not switching | Use orientation="responsive" — requires container query support |
| Checkbox label too bold | Add className="font-normal" to FieldLabel for checkbox/radio fields |
| Description above input | Place FieldDescription before or after the input — order in JSX determines visual order |
| Multi-column fields misaligned | Wrap in a div with grid grid-cols-2 gap-4 inside FieldGroup |
See Also
- Related Components: Form (React Hook Form integration), Input, Checkbox, Radio Group, Select, Label
- Tokens: Colors — Destructive color for errors, muted-foreground for descriptions
Last updated: February 24, 2026