Photon Design System

Checkbox

Selects one or more options whose values are confirmed on submit. Built on Base UI Checkbox, with checked, unchecked, and indeterminate states. For an on/off setting that takes effect immediately, use Switch instead.

Import

import {
  Checkbox,
  CheckboxGroup,
  CheckboxCard,
} from "@photon-ai/pho-ui/components/checkbox";

States

Pass label for a clickable text row. indeterminate renders a dash — use it for a parent whose children are partially selected.

<Checkbox label="Unchecked" />
<Checkbox label="Checked" defaultChecked />
<Checkbox label="Indeterminate" indeterminate />
<Checkbox label="Disabled" disabled />

Sizes

Two sizes — sm and md (default). size scales the box and its glyph; the label keeps the body text size.

<Checkbox size="sm" label="Small" defaultChecked />
<Checkbox size="md" label="Medium" defaultChecked />

Group

CheckboxGroup tracks several checkboxes as one value. Each child sets a name; control the group with value / onValueChange or defaultValue.

<CheckboxGroup defaultValue={["email"]}>
  <Checkbox name="email" label="Email" />
  <Checkbox name="sms" label="SMS" />
  <Checkbox name="push" label="Push notifications" />
</CheckboxGroup>

Card

CheckboxCard wraps the same Checkbox in a bordered, selectable tile — pass title and an optional description instead of label. Use it when each option carries enough weight (a plan add-on, a notification channel) to deserve more visual presence than a plain labeled box. The whole tile is the hit target and carries the press feedback.

<CheckboxCard
  title="Email"
  description="Order updates and receipts."
  defaultChecked
/>
<CheckboxCard title="SMS" description="Delivery and shipping alerts." />

Card with icon

Pass icon when the option has a glyph — it stacks above the title and replaces the checkbox box.

<CheckboxCard
  title="Email"
  description="Order updates and receipts."
  icon={<IconMail />}
  defaultChecked
/>
<CheckboxCard
  title="SMS"
  description="Delivery and shipping alerts."
  icon={<IconMessage />}
/>

Props

Checkbox (<span role="checkbox"> + a hidden native input)

  • label — ReactNode — clickable text beside the box; the whole row toggles it (omit and pass aria-label for a bare box)
  • size — sm · md (default md) — box and glyph size
  • checked — boolean — controlled checked state
  • defaultChecked — boolean (default false) — initial state when uncontrolled
  • indeterminate — boolean (default false) — render the partial (dash) state; announces as mixed
  • onCheckedChange — (checked: boolean, details) => void — fires on toggle
  • name — string — value key contributed to a parent CheckboxGroup
  • value — string (default 'on') — form-submission value when checked
  • parent — boolean (default false) — mark this the “select all” summary box of a CheckboxGroup
  • disabled — boolean (default false) — non-interactive and dimmed
  • readOnly — boolean (default false) — focusable but not toggleable
  • required — boolean (default false) — must be checked to submit a form
  • className — string — merged onto the box

CheckboxGroup (<div>)

  • value — string[] — controlled set of checked child names
  • defaultValue — string[] — initial set when uncontrolled
  • onValueChange — (value: string[], details) => void — fires when the set changes
  • allValues — string[] — every child value, so a parent box can drive select-all
  • disabled — boolean (default false) — disable every child at once
  • className — string

CheckboxCard

  • title (required) — tile heading
  • description — supporting copy shown below the title
  • icon — ReactNode — glyph stacked above the title; when set, it replaces the checkbox box
  • checked / defaultChecked / onCheckedChange — same as Checkbox
  • indeterminate — boolean (default false) — render the partial (dash) state
  • disabled — boolean (default false) — non-interactive and dimmed
  • className — string — merged onto the tile

Accessibility

  • Semantics — the box renders as a <span role="checkbox"> with aria-checked set to true, false, or mixed for indeterminate; a hidden native input carries the value into forms.
  • Labelling — passing label wraps the box and text in a <label>, so the text names the control and clicking anywhere on the row toggles it. Without a label, pass aria-label — a bare box is announced as an unnamed checkbox. CheckboxCard works the same way — title and description sit inside the tile’s <label>, so they compose into the option’s accessible name.
  • Keyboard — Space toggles the focused checkbox; Tab / Shift+Tab move between them. disabled drops it from the tab order; readOnly keeps it focusable but ignores the toggle.
  • Focus — keyboard focus shows the brand ring (focus-visible only, offset from the box); pointer clicks don’t.
  • Groups — inside a CheckboxGroup, a parent box given the group’s allValues reports mixed when only some children are checked. Give the set its own accessible name — a heading tied with aria-labelledby, or a Fieldset — so the group reads as a unit.

Best practices

  • Use a checkbox for values confirmed on submit (filters, terms, multi-select); use a Switch for a setting that applies the moment it flips.
  • Give every checkbox a label or aria-label — a lone box is ambiguous.
  • Reserve indeterminate for a “select all” parent reflecting a partial child selection; it isn’t a third user-settable value.