Checkbox
A small box that is on, off, or mixed. Use it to confirm an action, grant a permission, pick items from a list, or select table rows.
Variants and states
default fills with the primary color. neutral fills with the text color for filter lists. Checkbox has one 16px size.
Unchecked
Checked
Mixed
Disabled
Disabled checked
Invalid
Naming
Wrap the box in a Label, point a Label at its id, hide the text with an sr-only span, or use aria-label when a row already says what it is.
Confirm before acting
The action stays disabled until the box is checked. The box locks while the request runs and after it is approved.
Upload q3-board-summary.pdf to the Finance shared drive.
Check the box to enable Approve.
Permission grid
One box per team and permission inside a fieldset. Disabling the fieldset locks every box while a save runs.
Selection limit
Unchecked boxes disable once the limit is reached, so a checked box can still be cleared. The count is announced as a status.
2 items selected.
Row selection
A header box selects every row and shows mixed when some are selected. Row boxes use aria-label because the row already shows its content.
| Account | Owner | |
|---|---|---|
| Acme Corp | Dana Whitfield | |
| Globex | Priya Raman | |
| Initech | Sam Okafor |
1 of 3 rows selected.
File list
Managers get a box in a fixed 16px column before the icon. Long names wrap without pushing the box.
- Contracts
- Launch brief.docx
- 2026-10-14 customer interview notes with Globex procurement.md
Turn on more input
A box can enable an optional input or reveal follow-up settings.
Duplicate deliveries are skipped by payload hash.
In settings rows
SettingsRow names the box with htmlFor and places it beside the label from 36rem of group width.
Create tables
In fields
CheckboxField and ChoiceGroup from Field wrap the same box with a label, helper, and error line.
Knowledge sources
OptionalUsage
import { Checkbox } from "@tulipfarm/sprout/checkbox";
import { Label } from "@tulipfarm/sprout/label";
const [confirmed, setConfirmed] = useState(false);
<Label>
<Checkbox checked={confirmed} onCheckedChange={setConfirmed} />
I authorize this exact change
</Label>
<Checkbox id="share" defaultChecked />
<Label htmlFor="share">Share with selected workspace members</Label>
<Checkbox
aria-label="Select all rows on this page"
checked={all}
indeterminate={some}
onCheckedChange={selectAll}
/>Pass checked and onCheckedChange to control the box, or defaultChecked to let it keep its own state.onCheckedChange gets a boolean, so a state setter can be passed straight in.
Props
variant is "default" (fills with the primary color) or "neutral" (fills with the text color, for filter lists). There is one size: a 16px box with a 40 × 32px hit area.
indeterminate shows a minus sign and the mixed state, such as a "select all" box when only some rows are selected. Setaria-invalid to draw a danger border and ring while the box is unchecked. disabled dims the box and blocks changes; a disabled<fieldset> around it does the same.
The box also takes Base UI props such as id,name, value, required,readOnly, aria-describedby,inputRef, and state functions for className.
Accessibility
Checkbox renders an element with role="checkbox" andaria-checked ("mixed" when indeterminate), plus a hidden input for forms. Tab moves focus to it and Space toggles it. A focused box shows a 2px focus ring. Name every box with a wrappingLabel, a Label htmlFor, an sr-only span, oraria-label.
Color and check mark changes take 120ms and stop when reduced motion is requested. In forced colors mode the box gets a system border and a checked box is highlighted.
Theme tokens
Off uses --sprout-border on --sprout-surface, and hover uses --sprout-border-strong. On uses--sprout-primary with --sprout-on-primary, so the opt-in TulipFarm theme tints it green. Neutral uses--sprout-text with --sprout-surface. Invalid uses --sprout-danger, and focus uses--sprout-focus.