Checkbox

A binary selection control. Supports uncontrolled and controlled usage, three sizes, and an indeterminate state for hierarchical selection.

Preview

Delivered every Monday morning.

This field must be checked.

Sizes

SizeDimensionsBorder radius
sm14 × 14px4px
md16 × 16px5px
lg20 × 20px6px

Indeterminate

Use indeterminate on a parent checkbox to indicate a partial selection within a group.

Implementation

Checkbox.tsx
tsx
import { Checkbox } from "@/components/ui/Checkbox";
import { useState, useRef } from "react";

// Uncontrolled
<Checkbox label="Accept terms" defaultChecked />

// Controlled
const [checked, setChecked] = useState(false);
<Checkbox
  label="Subscribe to newsletter"
  checked={checked}
  onChange={(e) => setChecked(e.target.checked)}
/>

// Indeterminate (partial parent selection)
const allChecked = items.every((i) => i.checked);
const someChecked = items.some((i) => i.checked);
<Checkbox
  label="Select all"
  checked={allChecked}
  indeterminate={someChecked && !allChecked}
  onChange={(e) => setItems(items.map((i) => ({ ...i, checked: e.target.checked })))}
/>

// With helper text
<Checkbox
  label="Enable notifications"
  helperText="You can change this in settings at any time."
/>

// Error state
<Checkbox label="I agree to the terms" error="You must accept the terms to continue." />

Props

All native <input type="checkbox"> attributes are forwarded (except size and type).

PropTypeDefaultDescription
labelstringVisible label rendered to the right of the checkbox.
helperTextstringSupplemental hint rendered below the label.
errorstringError message. Applies error styling to the checkbox.
size"sm" | "md" | "lg""md"Controls the checkbox dimensions and label font size.
indeterminatebooleanfalseShows a minus icon indicating partial selection. Used for parent checkboxes in a group.

ARIA roles

ElementRoleKey attributes
Inputcheckbox (implicit)aria-checked (true/false/mixed), aria-describedby
Groupgroup (fieldset)aria-labelledby (legend)

Keyboard

KeyAction
SpaceToggle the checked state
TabMove focus to the next focusable element
Shift+TabMove focus to the previous focusable element

Accessibility

  • Built on a native <input type="checkbox"> — fully keyboard and screen-reader accessible.
  • The visual box is purely CSS; the real input is sr-only and receives focus.
  • Focus ring appears via focus-visible — keyboard-only, not shown on mouse click.
  • Indeterminate state is set via element.indeterminate = true, which assistive technology announces as 'mixed'.
  • Error text should be linked with aria-describedby pointing to the error message id.
  • In a checkbox group, wrap items in a <fieldset> with a <legend> to provide group context.