Field
A labeled row for one form control, with a required or optional marker, a counter, and a helper or error line. ChoiceGroup and CheckboxField cover radios and checkboxes. FieldsDisabled locks a whole form while it saves.
Agent details
Required and optional markers, a helper, a counter, a Select, and errors that appear after submit and move focus to the first problem.
Choice groups
Radios pick one value and checkboxes pick many, in a list or two columns from 640px. Options can carry descriptions and be disabled one at a time.
Reply mode
Repeat
Tools
OptionalChanges dataSkills to import
RequiredRun on
OptionalCheckbox fields
One checkbox with its label and a helper or error line. The neutral variant fills with the text color for filter lists.
Calendars
Sign in
Large inputs for auth and onboarding, with a "or" separator before the other sign-in option.
Disabled and locked
FieldsDisabled locks every field below it while a form saves. A single Field can also be disabled with a reason in its helper.
Screen reader labels
When a nearby heading already names the rows, labels and legends can be read by screen readers only. The required or optional marker stays in the description.
Invite by email
GitHub knowledge
Repositories
RequiredCustom and wrapped controls
Any control that calls useFieldControl joins the field, even inside a wrapper. Fields without an id generate one.
58 / 2,000
182 / 2,000
Usage
import {
CheckboxField,
ChoiceGroup,
Field,
FieldSeparator,
FieldsDisabled,
} from "@tulipfarm/sprout/field";
import { Input } from "@tulipfarm/sprout/input";
<FieldsDisabled value={saving}>
<form noValidate onSubmit={save}>
<Field label="Name" required error={errors.name}>
<Input value={name} onChange={(event) => setName(event.target.value)} />
</Field>
<Field
label="Instructions"
required
counter="120 / 8,000"
helper="Tell the agent what to do."
>
<Textarea rows={4} maxLength={8000} />
</Field>
<ChoiceGroup
type="radio"
legend="Repeat"
required
layout="columns"
options={[
{ value: "daily", label: "Every day", description: "Runs at 9:00 AM" },
{ value: "weekly", label: "Every week" },
]}
value={repeat}
onValueChange={setRepeat}
/>
<CheckboxField
label="Notify me when it runs"
checked={notify}
onCheckedChange={setNotify}
/>
<FieldSeparator>or</FieldSeparator>
</form>
</FieldsDisabled>Every Field, ChoiceGroup, and CheckboxField says whether it isrequired or optional; there is no default. The marker sits next to the label. Pass id to name the control, or leave it out to generate one.
Field
label names the control. labelVisibility is"visible" (default) or "screen-reader", which keeps the label, marker, and counter for assistive tech only.helper is a line of help below the control.error replaces it and marks the control invalid.counter is short status at the end of the label row, such as"120 / 8,000". disabled dims the label and disables the control.
The child is one control, or one element that wraps it. SprõutInput and SelectTrigger join the field on their own. For any other control, call useFieldControl(props) and spread the result on the focusable element:
function Textarea(rest: ComponentProps<"textarea">) {
const props = useFieldControl(rest);
return <textarea {...props} className="my-textarea" />;
}It adds the field's id, disabled,aria-describedby, aria-invalid, andaria-required. Outside a Field, or when the control has its ownaria-label or aria-labelledby, props pass through unchanged.
ChoiceGroup
type="radio" picks one value and type="checkbox"picks many. options are { value, label, description?, disabled? }. layout is "list" (default) or"columns", two columns from 640px. It also takeslegend, legendVisibility, helper,error, disabled, id, andwarning, a badge after the legend such as "Changes data".
CheckboxField
One checkbox with a label and a helper or error line. variantis "default", filled with the primary color, or"neutral", filled with the text color for filter lists. Theid goes on the wrapper, so a form can scroll to it and focus the checkbox inside.
FieldSeparator and FieldsDisabled
FieldSeparator is a labeled Separator that pulls in the stack gap, so the line sits evenly between fields. Text such as "or" is optional. FieldsDisabled is a context provider:<FieldsDisabled value={saving}> disables every field below it.
Accessibility
Labels point at their control with for. The control is described by the error or helper, then the optional marker, then the counter. Required controls get aria-required; invalid ones getaria-invalid. A checkbox group is a labelledrole="group"; a radio group is a Base UI radiogroup with arrow key movement. Each option row is clickable, and option descriptions describe their checkbox or radio.
Checkboxes and radios show a 2px focus ring. Rows are at least 36px tall, and 44px on touch screens. Colors change in 120ms and stop when reduced motion is requested. Forced colors mode uses system colors.
Theme tokens
Labels use --sprout-text; markers, counters, and helpers use--sprout-muted; errors use --sprout-danger. Checked boxes use --sprout-primary and--sprout-on-primary, and checked rows use--sprout-primary-subtle. Focus rings use--sprout-focus.