<!-- Protocore Design System — Checkbox -->
# Checkbox

- **Category:** Inputs (`inputs`)
- **Slug:** `inputs/checkbox`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Checkbox } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/inputs/checkbox

> Boxy 16px checkbox with checked, unchecked, and indeterminate states and an optional label.

## When to use it

Reach for a **Checkbox** when each option is independent and any number can be on at once — feature flags, region opt-ins, a select-all header. For a single instant on/off that takes effect immediately (a setting, not a form value) use **Switch**. For a one-of-many choice within a small set, use **RadioGroup**. For toggling a filter over a collection, use **Chip** or **FilterBar**.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | no | — | — |
| `className` | `string` | no | — | — |
| `label` | `ReactNode` | no | — | Text rendered beside the box (sans 14). Omit for a standalone control. |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

Pass a `label` and the box wires its own `id` to the caption. Omit the label for a bare control inside a table cell or custom row.

```tsx
import { Checkbox } from "@protocore/pds";

export default function Demo() {
  return <Checkbox label="Enable audit logging for this project" defaultChecked />;
}
```

### States

`checked` accepts `true`, `false`, or `"indeterminate"` — the third state renders a dash rather than a tick. Every state has a disabled variant.

```tsx
import { Checkbox } from "@protocore/pds";

export default function Demo() {
  return (
    <div style={{ display: "grid", gap: 12 }}>
      <Checkbox label="Unchecked" />
      <Checkbox label="Checked" defaultChecked />
      <Checkbox label="Indeterminate" checked="indeterminate" />
      <Checkbox label="Disabled" disabled />
      <Checkbox label="Disabled + checked" disabled defaultChecked />
    </div>
  );
}
```

## Do & don't

**Do**

- Give every checkbox a visible `label`, or an `aria-label` when it stands alone.
- Use the `indeterminate` state for a parent that summarizes a partially-selected group.
- Keep related checkboxes in a single vertical list so they scan as one set.
- Control `checked` from state when the value participates in a larger form.

**Don't**

- Don't use a checkbox for an action that fires immediately — that's a Switch.
- Don't use a checkbox for mutually-exclusive choices — that's a RadioGroup.
- Don't rely on `indeterminate` as a persisted value; it's a visual summary only.
- Don't hide the label and leave the control unlabelled for assistive tech.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus to the checkbox. |
| `Space` | Toggle between checked and unchecked. |

**Notes**

- Built on Radix Checkbox: the control exposes `role="checkbox"` with `aria-checked` reflecting true / false / mixed.
- The label is a real `<label htmlFor>` — clicking the text toggles the box.
- Disabled checkboxes are removed from the tab order and expose `aria-disabled`.

## Related

`switch`, `radio-group`, `field`, `filter-bar`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
