Checkbox

Turns one option on or off.

Checked

  • falsedefault

  • true

  • indeterminate

States

  • rest

  • hover

  • held

  • focus-visible

  • ticked

  • mixed

  • invalid

  • disabled

Usage

When to use it
Use
A form asks a yes or no that applies when the form is saved.
Use
The person picks any number from a short visible list, none included.
Use
Rows are selected for a bulk action, with select-all in the head.
Not for
The setting applies the moment it changes. Use Switch.
Not for
Exactly one option can be chosen. Use Radio group.
Not for
Two to five views of the same thing. Use Segmented control.

Rules

7 rules
Must
Wrap the checkbox and its text in one <label> (or Label), or use a horizontal Field. Because a loose span is neither the accessible name nor the target, so a screen reader hears "checkbox" and only the 16px box toggles.
Must
Build select-all as <Checkbox parent/> inside a CheckboxGroup with allValues; never work out mixed by hand. Because a hand toggle from mixed clears or fills everything, where parent cycles back to the set the person picked.
Never
Fill a ticked box by hand, in yellow or a status colour; the checked recipe already fills it. Because a hand-set fill drops the ink tick, the gold edge and the light, and green or red reads as a job's status; the checked recipe is yellow under an ink tick in light and the yellow tint in dark.
Must
Set aria-invalid only after a submit or a press on the action, never on the first render. Because a red box before the person has done anything blames them for nothing.
Must
Show mixed with indeterminate (the bar), never a lighter fill. Because a lighter fill reads as disabled, and forced colors drop it so mixed looks unticked.
Should
Say what ticking does, in the positive. Because a negative label makes the person undo a double negative to know what happens.
Should
Use items-start on a row whose label can wrap or carries a description.

State order

8 states, highest first
1
disabled
2
invalid
3
mixed
4
ticked
5
held
6
focus-visible
7
hover
8
rest

Accessibility

6 checks
role="checkbox" with aria-checked true, false or mixed, named by the wrapping label.
The unticked edge is --line-control, 3.5:1 on its body and 3.7:1 on it lifted to white (WCAG 1.4.11).
Ticked is sodium yellow with a gold edge and an ink tick, 12.8:1, in light; the dark yellow tint with a yellow edge and tick, 9.0:1, in dark. The box is lit from below.
The mark is aria-hidden; mixed reads without colour through the bar.
The box is 16px with a 28px hit area, and the label makes the whole row the target.
Focus is the 2px --focus ring, 2px out.

Keyboard

1 key
Space
Toggles; a parent cycles all, none, then the set the person picked.

Edge cases

6 edge cases
Long wrapping label
Use items-start so the box sits on the first line.
No label
Announced as "checkbox" with no name; pass aria-label.
Inside a clickable row
The box stops its click from bubbling, so the row does not open.
Select-all over hundreds of values
The parent reads the value array, not the rendered boxes.
Forced colors
Ticked takes Highlight with a HighlightText tick and no light; unticked draws a CanvasText edge.
Reduced motion
The fill and its light cut in with no fade.

API

7 props
checked, defaultChecked, onCheckedChange
Base UI Checkbox
indeterminate
boolean default false
parent
boolean default false
value
string
aria-invalid
boolean default false
disabled, readOnly, required, name
Base UI
CheckboxGroup value, onValueChange, allValues
string[]

Tokens

15 tokens
--check-off
var(--secondary-bg)
--check-off-hover
#ffffff
--well
#f4f3ee
--line-control
#8a8478
--line
#e2dfd5
--fail
#b3261e
--check-bg
#ffd400
--check-edge
#d9b400
--check-mark
#161512
--primary-light
rgb(255 255 255 / 0.55)
--primary-light-hover
rgb(255 255 255 / 0.85)
--focus
#7a5d00
--dur-light
240ms
--dur-glow
320ms
--ease-soft
cubic-bezier(0.22, 0.8, 0.24, 1)

Install

For your coding agent

GitHub

Please star me pookie

198

A new design system every week

Leave your email before you copy the prompt.