Checkbox

Turns one option on or off, or a whole set at once.

Examples

Select all

Confirm before deleting

Email me when

Checked

  • falsedefault

  • true

  • indeterminate

States

  • rest

  • hover

  • pressed

  • focus-visible

  • ticked

  • mixed

  • invalid

  • disabled

  • read-only

Usage

When to use it
Use
A form asks for a yes or no that applies when the form is saved.
Use
The person picks any number from a short visible list, including none.
Use
Table rows are selected for a bulk action, with a 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
More than eight options, or a long searchable list. Use Combobox.

Rules

8 rules
Must
Build select-all as <Checkbox parent/> inside a CheckboxGroup with allValues; never compute mixed by hand. Because a hand toggle from mixed clears or fills everything, while parent cycles back to the set the person picked.
Must
Wrap the checkbox and its text in one <label>, or use Field. Because a loose span is not the accessible name and not a click target, so a screen reader hears "checkbox" and only the 16px box toggles.
Must
Set aria-invalid after a submit attempt, never on first render. Because aria-invalid turns the edge to the fail line and announces invalid before the person has done anything wrong.
Should
State what ticking does, in the positive; no "Don't" labels. Because ticking a negative label makes the person undo a double negative to know what happens.
Must
Show mixed with the minus glyph, never a lighter or partial fill. Because a lighter fill reads as disabled, and forced colors drop it so mixed looks unticked.
Never
Color a ticked checkbox; it stays ink. Because hue names a kind or a status in Quartz, so a colored tick reads as a warning or an agent setting.
Should
Switch to a multiple Combobox past 8 options. Because a long list of boxes has no search and pushes the rest of the form off the screen.
Should
Use items-start on a row whose label can wrap. Because centered, the box drifts to the middle of a two-line label instead of its first line.

Accessibility

6 checks
The box is role="checkbox" with aria-checked true, false or mixed, named by the wrapping label.
Space toggles; a parent cycles all, none, then the old mix.
The glyph is aria-hidden; mixed reads without color through the minus.
A group is role="group" named by aria-labelledby.
The unticked edge is --border-control, 3:1 on paper, and the ticked fill carries the state besides.
The box is 16px with a 28px hit area; the label makes the whole line the target.

Edge cases

11 edge cases
Long wrapping label
Use items-start so the box aligns to the first line.
No label
Announced as "checkbox" with no name; pass aria-label.
Select-all over 500 values
The parent reads the value array, not rendered boxes.
Right to left
The box moves to the start with the label.
Zoom 200%
The box stays 16px, centred on the first line.
Invalid after submit
The fail line on the edge and the error text it describes; ticking clears it.
Inside a clickable row
The box stops its click from bubbling.
Double click on the label
Toggles twice and ends where it began.
Space held down
Toggles once, on key up.
Touch
Tapping the label toggles; the tap highlight is off.
Forced colors
CanvasText border; ticked and mixed use Highlight with HighlightText; disabled is GrayText.

API

6 props
value
string
parent
boolean default false
indeterminate
boolean default false
aria-invalid
boolean default false
disabled, readOnly, required, name
Base UI
CheckboxGroup value, onValueChange, allValues
string[]

Tokens

12 tokens
--surface-page
var(--paper)
--border-control
oklch(var(--ink-c) / 0.50)
--fail-line
oklch(60% 0.236 26)
--radius-soft
6px
--ink
oklch(var(--ink-c))
--text-inverse
var(--paper)
--surface-pressed
oklch(var(--ink-c) / 0.08)
--text-disabled
oklch(var(--ink-c) / 0.40)
--line-focus
2px
--border-focus
var(--ink)
--focus-offset
2px
--ease-step
cubic-bezier(0.2, 0, 0, 1)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.