Skip to main content
Graphite UI

Checkbox

A box the reader ticks to choose an option that stands on its own, where any number can be chosen, including none. When only one of several can be chosen, use a radio button group, and when the change applies the moment it is made, use a toggle.

Contract 3.1.1Kit · 2 sets · 27 variantsOpen in Figma

Live preview

Rendered by the component itself from the same generated tokens as the rest of the site.

Installation

Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.

npx shadcn@latest add https://www.graphite-ui.com/r/checkbox.json
import { Checkbox } from '@/components/ui/checkbox'

Anatomy

Slots come from the contract, so this list and the code cannot disagree about what the component is made of.

Used for every order until you change it.
  1. LabelRequiredAlways paired, never a bare checkbox. The control renders it itself. There is no wrapper to take it from — the kit builds label text into each form control.
  2. Supporting textOptionalHelp, warning or error text, 8 below the row. The kit calls this Helper / Warning / Error text and builds it into the control the same way; warning and error lead with the shared 16px status icon.

Variants

Selection is two props, checked and indeterminate. Indeterminate is a dash in the same box, a different glyph rather than a different color, so it reads apart from both of the others. Indented steps a child in under its parent.

Unchecked
Checked
Indeterminate
With help text
Supporting copy.
Indented

States

Focus is a pseudo-class in the code and a variant in the kit, so the page forces it here. The kit draws no hover state for a checkbox and the code has none. Error is not a prop of its own: passing errorText is what puts the control in it; the box’s ring turns danger, over the fill when checked, and the message leads with the status icon. Warning leaves the box alone. Read-only draws the empty box in the disabled tone with the mark still on it.

Enabled
Focus
Disabled
Error
Accept the terms to continue.Checked keeps its fill under the ring.
Warning
Updates go to an address you have not confirmed.
Read-only

API reference

Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.

Checkbox props
PropTypeDefaultNotes
checked——
indeterminate——
disabled——The glyph, the label and the help text all take the disabled tone; a checked box stays filled.
readOnlyboolean—The kit's Read-only. Native checkboxes ignore readonly, so the box stays focusable, carries aria-readonly and never reports a change. It draws the empty box in the disabled tone with the mark on it.
indentedboolean—The kit's Indented, a 28 step in (one 20px frame and its 8 gap), for a child under an indeterminate parent.
label——Required. There is no shape in which this control exists unlabelled, and no wrapper left to supply one.
helpText——Supporting copy. Suppressed while errorText or warningText is present.
warningText——Resolves the kit's Warning, outranked by an error. The box is unchanged; the warning is carried by the message's status icon.
invalidboolean—The error state without a message of its own, for a box whose group carries the message (Checkbox group, #273). The ring and aria-invalid as for errorText.
errorText——Its presence resolves the error state, so error text and error styling cannot be shown apart. This was Field's guarantee and it survives Field.

Design tokens

Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.

  • on-surfaceThe box, its ring when unchecked and its fill when checked or indeterminate, which the kit binds to icon-primary; the label text; warning text. The check and dash are knocked out of the fill, so they show what the box sits on.
  • primaryThe focus ring, through the family's focus step, a 2px stroke centred on the 20px frame; and the disabled and read-only tone, through its disabled-content step.
  • on-surface-variantHelper text, which the kit binds to onSurfaceVariant rather than onSurface.
  • dangerThe error ring, at the ring's own weight and over the fill when checked; the error text; the error status icon's triangle. One role for all three, so they cannot drift apart.
  • warningThe warning status icon's triangle.
  • on-warningThe "!" on the warning status icon.
  • backgroundThe "!" on the error status icon.
  • textThe label at Body/3.
  • spacingThe row and stack gaps, and the 32px touch target around the 20px frame.

Usage

The contract's prohibitions, written as the choices you will actually face.

Do

  • Use a checkbox when each option stands alone and any number of them can be on, including none.
  • Show indeterminate on a parent whose children are only partly selected, and let the next click resolve it.
  • Show an error by passing errorText, so the message and the error state arrive together.
  • Write the label as the thing being chosen (“Save as default address”), not as a question.

Don’t

  • Render a bare box and let nearby text stand in for its label. The label prop is required for exactly this reason.
  • Shrink the hit area to the painted box. The box is 15px in a 20px frame but the control keeps a 32px target, the same rule as Button.
  • Mark indeterminate by recoloring the check. It is a dash, so it still reads when color does not.
  • Use a checkbox for a setting that takes effect the moment it changes. That is a Toggle.

Accessibility

What the component does for you, and what it leaves to you.

Keyboard
It is a native checkbox, so Tab reaches it and Space toggles it. Clicking the label toggles it too.
Roles
A native input of type checkbox. Indeterminate is written to the DOM property, so assistive tech announces it as mixed rather than unchecked. In error the input carries aria-invalid; read-only carries aria-readonly and stays focusable, but does not change.
Labels
The label is a real label tied to the input. Help and error text are linked with aria-describedby, and error text is announced as it appears.
Focus
A 2px ring in --graphite-primary-focus on the 20px frame, 1.5px clear of the box, on keyboard focus only. In error it sits outside the danger ring rather than replacing it.
Target
The painted box is 15px in a 20px frame. The input underneath covers a 32px square, so the target does not shrink with the drawing.

Figma parity

The kit’s Checkbox page has two sets: the single Checkbox, with Selection and State axes, and a Checkbox group. The code has the single control on every axis and no group. This table is where those facts are reconciled instead of quietly diverging.

Kit axes and how they map to code
Kit axisValuesCodeHow it maps
SelectionUnchecked · Checked · Indeterminatechecked, indeterminateTwo booleans. When both are set, the dash wins.
Glyphicon-primary ring and filled box—The kit’s own glyphs, in on-surface, with the check and dash knocked out. They were a primary fill on a 20px bordered box until 3.0.0.
StateEnabled · Focus · DisableddisabledFocus is :focus-visible, a pseudo-class (governance rule 7). Disabled takes the glyph, label and help text to the disabled tone.
StateInvaliderrorTextIts presence is the error state. The ring turns danger at its own weight; a checked box keeps its fill under it.
StateWarningwarningTextThe box is unchanged; the message leads with the warning status icon and stays on-surface, as the kit draws it.
StateRead-onlyreadOnlyThe box in the disabled tone with the mark on it, no fill. Focusable, never changes.
StateSkeleton—No counterpart by rule.
IndentedFalse · TrueindentedA 28 step in: one 20px frame and its 8 gap.
LabelGroup label above the box—The kit’s Label belongs to the Checkbox group; on a single checkbox the label prop is the value.
SetCheckbox group—No counterpart: no current demand under rule 6. Lay checkboxes out in your own container; the set is carried on the plan for unclaimed sets.