# Checkbox

> A box people tick to pick an option, with a built-in label, tones, sizes, and a mixed state.

- Name: `checkbox`
- Status: beta
- Categories: forms
- File: the install writes `checkbox.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`, `class-variance-authority`. The install adds them.
- Gallery: https://arbokui.com/components/checkbox
- Registry item: https://arbokui.com/r/checkbox.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/checkbox
   ```

## Use it for

- Picking any number of options from a list, like topics to follow.
- Agreeing to something before a form is sent, like the terms of service.
- Pass the label text as children. The box and the text share one label, so clicking either one toggles it.
- When the text has to sit apart from the box, give the box an id and point a label's htmlFor at it.
- A 'select all' box above a list. Set indeterminate when only some items are picked, and it shows the mixed state.

## Avoid it for

- A setting that takes effect right away. Use a switch.
- Picking exactly one option from a list. Use a radio group.
- Options with a hint, a price, or a consequence. Use SelectionGroup multiple with a SelectionCard for each.
- An id with no label pointing at it. TypeScript accepts the id, but the box then has no name.

## Props

- `children`: `ReactNode`. Visible label text. Without it, TypeScript requires aria-label, aria-labelledby, or an id.
- `id`: `string`. Links a separate label. A label whose htmlFor matches names the box, and clicking it toggles the box.
- `tone`: `"neutral" | "brand" | "danger" | "success" | "warning"`. Default `"neutral"`. Color of the checked box.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Box size and label text size. Density scales them.
- `checked`: `boolean`. Controlled state. Use it with onCheckedChange.
- `defaultChecked`: `boolean`. Default `false`. Starting state when you do not control it.
- `onCheckedChange`: `(checked: boolean, details) => void`. Called with the new state when someone toggles the box.
- `indeterminate`: `boolean`. Default `false`. Shows a dash and sets aria-checked to mixed.
- `disabled`: `boolean`. Default `false`. Blocks toggling and removes the box from the tab order. The label text dims too.
- `name`: `string`. Form field name. The box sends its value with the form when checked.
- `aria-invalid`: `boolean`. Shows the danger border. Set it when validation fails, and link the message with aria-describedby.
- `className`: `string`. Classes for the box. Later classes win, so className='\[--checkbox-size:1.5rem\]' makes it bigger.

## Accessibility

- Renders a span with role="checkbox" plus a hidden native checkbox, so the value is sent with the form.
- Space toggles it. Enter does not toggle it. Enter submits the form, as it does on a native checkbox.
- TypeScript requires visible children, aria-label, aria-labelledby, or an id. It cannot check that a label points at the id.
- In server HTML, only aria-label and aria-labelledby name the box. A label from children or an id is linked once JavaScript runs.
- Clicking the label text also toggles the box, so the target is bigger than the box.
- The unchecked border uses --input and the focus ring uses --ring. The theme tests hold both to 3:1 against the page and cards.
- The check mark uses the tone's foreground on its fill, which the theme tests hold to 4.5:1.
- Press scale and the check mark's pop-in follow --motion-\* tokens. Reduced motion turns both off and keeps the fade.

## Examples

### With a label

```tsx
<Checkbox name="terms" tone="brand">
  I accept the{" "}
  <a href="/terms" className="underline">
    terms of service
  </a>
</Checkbox>
```

### A list of options

```tsx
<fieldset className="grid gap-3">
  <legend className="mb-1 text-sm font-medium">Email me about</legend>
  <Checkbox name="topics" value="releases" defaultChecked>
    New releases
  </Checkbox>
  <Checkbox name="topics" value="tips">
    Tips and guides
  </Checkbox>
  <Checkbox name="topics" value="events">
    Events near me
  </Checkbox>
</fieldset>
```

### Select all, with the mixed state

```tsx
"use client";
import { useState } from "react";

const files = ["report.pdf", "notes.txt", "photo.png"];

function FilePicker() {
  const [picked, setPicked] = useState(["report.pdf"]);
  const all = picked.length === files.length;
  return (
    <div role="group" aria-labelledby="files-label" className="grid gap-3">
      <p id="files-label" className="text-sm font-medium">Files</p>
      <Checkbox
        checked={all}
        indeterminate={picked.length > 0 && !all}
        onCheckedChange={(checked) => setPicked(checked ? files : [])}
      >
        Select all
      </Checkbox>
      <div className="grid gap-3 ps-6">
        {files.map((file) => (
          <Checkbox
            key={file}
            checked={picked.includes(file)}
            onCheckedChange={(checked) => setPicked((now) => (checked ? [...now, file] : now.filter((f) => f !== file)))}
          >
            {file}
          </Checkbox>
        ))}
      </div>
    </div>
  );
}
```

### Controlled

```tsx
"use client";
import { useState } from "react";

function MarketingConsent() {
  const [checked, setChecked] = useState(false);
  return (
    <Checkbox checked={checked} onCheckedChange={setChecked}>
      Send me product news
    </Checkbox>
  );
}
```

### No visible text, in a table row

```tsx
<Checkbox aria-label="Select row: Invoice 1042" size="sm" />
```

### Named by a separate label

```tsx
<div className="flex items-center gap-2">
  <Checkbox id="beta" />
  <label htmlFor="beta" className="text-sm">
    Join the beta
  </label>
</div>
```
