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

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

> A set of checkboxes sharing one array value — the multi-select counterpart to RadioGroup.

## When to use it

Use a **CheckboxGroup** when **any number** of options in a small, visible set may be selected at once — permissions, scopes, notification channels. When exactly one option applies, use **RadioGroup**. When the option list is long or better searched than scanned, use **MultiSelect**.

## Props

### CheckboxGroup

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `aria-label` | `string` | no | — | Accessible name for the group (or label it via a wrapping Field). |
| `className` | `string` | no | — | — |
| `defaultValue` | `string[]` | no | — | Initial selection when uncontrolled. Default `[]`. |
| `disabled` | `boolean` | no | — | Disable every item in the group. |
| `name` | `string` | no | — | Shared `name` applied to every item's native input (for form submission). |
| `onValueChange` | `((value: string[]) => void)` | no | — | Fires with the next selection whenever an item toggles. |
| `orientation` | `enum` | no | `vertical` | Orientation of the item stack. Default `"vertical"`. |
| `style` | `CSSProperties` | no | — | — |
| `value` | `string[]` | no | — | Controlled list of selected values. Pair with `onValueChange`. |

### CheckboxGroup.Item

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | Extra class on the checkbox. |
| `disabled` | `boolean` | no | — | Disable just this item. |
| `label` | `ReactNode` | no | — | Text rendered beside the box (sans 14). |
| `value` | `string` | yes | — | This item's value — added to / removed from the group's array. |

## Examples

### Basics

Wrap `CheckboxGroup.Item`s in a `CheckboxGroup.Root`. Each item takes a `value` and a `label`; the group owns a `string[]` and is controllable via `value` / `defaultValue`.

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

export default function Demo() {
  return (
    <CheckboxGroup.Root defaultValue={["read"]} aria-label="Token scopes">
      <CheckboxGroup.Item value="read" label="Read — fetch resources" />
      <CheckboxGroup.Item value="write" label="Write — create and update" />
      <CheckboxGroup.Item value="delete" label="Delete — remove resources" />
      <CheckboxGroup.Item value="admin" label="Admin — manage members" />
    </CheckboxGroup.Root>
  );
}
```

### Controlled

Drive the selection from state with `value` + `onValueChange`. The callback receives the next array on every toggle.

```tsx
import { useState } from "react";
import { CheckboxGroup, Text, Stack } from "@protocore/pds";

export default function Demo() {
  const [value, setValue] = useState<string[]>(["email"]);

  return (
    <Stack gap={3}>
      <CheckboxGroup.Root value={value} onValueChange={setValue} aria-label="Notifications">
        <CheckboxGroup.Item value="email" label="Email" />
        <CheckboxGroup.Item value="sms" label="SMS" />
        <CheckboxGroup.Item value="push" label="Push" />
      </CheckboxGroup.Root>
      <Text size="sm" color="muted" mono>
        [{value.join(", ")}]
      </Text>
    </Stack>
  );
}
```

## Do & don't

**Do**

- Give the group an accessible name via a wrapping `Field` label or `aria-label`.
- Keep option labels parallel in structure and length.
- Order options by frequency or logical grouping.

**Don't**

- Don't use it for a single on/off — use a standalone Checkbox.
- Don't use it for one-of-many — use RadioGroup.
- Don't overflow a dozen options into a group — use MultiSelect.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus to the next checkbox in the group. |
| `Space` | Toggle the focused checkbox. |

**Notes**

- The container is a `role="group"`; label it with `aria-label` or a wrapping `Field`.
- Each item is a real checkbox with `aria-checked`; every item is its own Tab stop.
- A group-level `disabled` disables every item.

## Related

`checkbox`, `radio-group`, `multi-select`, `field`

---

© 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/
