Checkbox
Independent choices saved with a form.
Installation
pnpm --filter your-app add "@commenda/pathfinder@workspace:*"Import @commenda/pathfinder/styles.css once at the app root. The stylesheet is precompiled with Tailwind v4; your application does not need Tailwind or source scanning. Event handlers belong in a client component in Next.js App Router.
Usage
import { Checkbox } from '@commenda/pathfinder/checkbox';
<Checkbox
label="Send me a copy"
description="We email the filing receipt to you as well."
checked={sendReceipt}
onCheckedChange={setSendReceipt}
name="receipt"
value="email"
/>;| Prop | Type | Default / behavior |
|---|---|---|
label | string | Required, visible text and accessible name |
hideLabel | boolean | false; retains the accessible name for dense/table controls |
description | string | Optional supporting text, associated with the control |
checked | boolean | Controlled selection; pair with onCheckedChange or readOnly |
defaultChecked | boolean | false; initial uncontrolled selection |
onCheckedChange | (checked: boolean) => void | Receives the next boolean only |
indeterminate | boolean | false; mixed parent selection, separate from checked |
disabled | boolean | Blocks interaction and submission |
disabledReason | string | Visible explanation while disabled, included in accessible description |
readOnly | boolean | Prevents changes while retaining value |
required | boolean | Requires selection for form validation |
variant | 'default' | 'card' | 'default'; card retains checkbox semantics |
details | readonly string[] | Optional explanatory bullet lines, intended for cards |
name | string | Name used when the control submits its value with a form. |
value | string | Value submitted when checked; defaults to 'on'. |
uncheckedValue | string | Value submitted when unchecked; omitted when not configured. |
form | string | ID of the form associated with the control. |
id | string | ID of the underlying form input. |
inputRef | Ref<HTMLInputElement> | Access to the underlying form input. |
className | string | Additional class names for the outer layout. |
ref | Ref<HTMLElement> | Access to the interactive root element. |
| Native events and ARIA | Base UI root props | Typed passthrough; arbitrary children/render overrides are intentionally hidden |
A checkbox saves with a form. Its label is part of the target. indeterminate is a parent summary, not a third answer: the application derives it from children and updates the children on change. Do not pass checked and defaultChecked together. Use positive labels such as “Send me a copy”. A visible description is preferable to a tooltip-only disabled reason for form controls.
The Figma control is one size: a 16px box, 4px radius, 12px check/minus mark and 8px label gap. There is no invented size or cosmetic hover/focus state prop. Checkbox cards reuse the same component with variant="card".
Examples
Checked, unchecked and mixed
Description
Parent and children
Checkbox card
Disabled states
Read the Figma choice-control rules for design guidance, source discrepancies and accessibility requirements.
Design rules
Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.
Checkbox
Source: 199:2, variants 199:90, guidance 203:226.
- CHECKBOX-01 — Use independent checkboxes for choices saved with a form. An immediate setting uses Switch. The label is part of the target. Use positive labels such as “Send me a copy”, not questions.
- CHECKBOX-02 — Mixed/indeterminate summarizes a parent whose children are partly selected. Activating it selects all its children. It is never a third answer. Applications own the child selection and derive the parent's checked/mixed flags.
- CHECKBOX-03 — Box 16px, radius 4px, 1px soft-pearl-400 border. Hover uses soft-pearl-500. Checked/mixed uses blue-700 and a white 12px check/minus; hover blue-800. Keyboard focus adds a white 2px gap and blue 2px ring. Disabled uses soft-pearl-100 surface, soft-pearl-300 border and soft-pearl-600 mark/text.
- CHECKBOX-04 — Label/13 regular sits 8px from the box; Copy/12 description uses soft-pearl-800, 2px below. Child rows indent 24px. Long content wraps and must not clip the box or focus ring.
Radio and cards
Source: 199:3, radio variants 200:46, card variants 201:413, guidance 205:320.
- RADIO-01 — One choice from two to five visible options. Use Select for longer lists or limited space. A lone boolean option uses Checkbox/Switch. Arrow keys move selection and skip disabled items; Tab enters/leaves the group.
- RADIO-02 — Prefer a sensible default. Leave the group unanswered only when deliberate choice is required. Never silently choose the first value or replace a removed controlled value. The application reconciles dynamic options.
- RADIO-03 — Order options by frequency, size or time rather than alphabetically by default. Circle 16px, selected white dot 6px; colors and focus match Checkbox. Group row gap 8px.
- CARD-01 — Use a card when each option needs title and description. Keep cards in one row equal-height. The “Radio card” Figma collection contains both radio and checkbox controls; Pathfinder exposes
RadioGroup variant="card"for one choice andCheckbox variant="card"for independent choices. - CARD-02 — Card padding 16px, control/text gap 12px, radius 8px. Title Label/13 Strong; description Copy/12 with 4px gap. Detail bullets have 2px gap and 4px top padding. Surface remains white when selected; border becomes blue-700. Focus surrounds the whole card. Disabled surface/border/text use soft-pearl-100/300/600. The 320px specimen width adapts to the container.
Implementation decisions and source differences
The complete prop tables are on Checkbox, RadioGroup and Switch. Source rule IDs are also kept in the component implementation comments and exported through llms.txt, llms-full.txt and the Markdown routes.
-
CHOICE-01 — The sheets describe 8px list gaps but some documentation frames place rows 32px apart. Use the documented 8px component-list gap; documentation specimen whitespace is not a new size token.
-
CHOICE-02 — Built-in text is plain, wrapping content. Do not place links, buttons or nested interactive controls inside labels or cards. Disabled reasons are visible descriptions, not tooltip-only explanations on controls removed from keyboard navigation.
-
CHOICE-03 — Visual control size is not the hit target. Coarse-pointer rows provide at least 44px height, while preserving 16px marks and the 36×20px switch track. Cards already exceed this height.
-
CHOICE-04 — Hover/focus are real browser states, not public cosmetic props. Retain native
name,value,form,required,readOnly, disabled semantics and input refs.hideLabelpreserves the required accessible label. -
CHOICE-05 — Use Pathfinder's existing Check/Minus optical artwork at 12px. Boxes, circles, tracks and thumbs are interactive primitive geometry, styled with the Figma tokens; they are not flattened screenshots. This keeps focus, RTL and switch movement functional.