# Checkbox

**When to use:** Boolean toggle input — settings, multi-select lists, accept terms, feature flags.

**Keywords:** tick box, select all, tri-state, partial, mixed, dash

**Import:** `import { Checkbox } from '@ahrowe/ui'`

```tsx
import { Checkbox, FormValidator } from '@ahrowe/ui';

// Uncontrolled with callback
<Checkbox
  label="I agree to the terms"
  selected={agreed}
  onToggle={(isChecked) => setAgreed(isChecked)}
/>

// With FormValidator
const agreedValidator = new FormValidator(false, [Validators.required()]);
<Checkbox label="Accept terms" formValidator={agreedValidator} />

// Loading state (skeleton)
<Checkbox label="Loading..." isLoading />

// Select all over a partial selection: a dash, announced as "mixed"
const checkedCount = items.filter((item) => item.checked).length;
<Checkbox
  label="All"
  selected={checkedCount === items.length}
  indeterminate={checkedCount > 0 && checkedCount < items.length}
  onToggle={(isToggled) => setAll(isToggled)}
/>
```

`indeterminate` wins over `selected` in what is shown and announced, but a click still reports `!selected`. Pass `selected={false}` while the selection is partial, as above, and a click on the dash selects everything.

**Key props:**

| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Text beside the checkbox |
| `selected` | `boolean` | Checked state |
| `indeterminate` | `boolean` | Partly checked: a dash and `aria-checked="mixed"`, over `selected` |
| `onToggle` | `(isToggled: boolean, event?) => void` | Change handler |
| `formValidator` | `FormValidator` | Connects to form validation |
| `disabled` | `boolean` | |
| `isLoading` | `boolean` | Shows a [Loader](Loader.md) in place of the box and ignores clicks |
| `children` | `ReactNode` | Custom label content |
| `cursorDefault` | `boolean` | Use default cursor instead of pointer |
| `ref` | `Ref<HTMLDivElement>` | Ref to the root element |

**Slots:** `icon` `checkmark` `label`

## Sizing

The control is sized by its font, so it stays in proportion with the label it sits next to. `--checkbox-size` overrides the box and `--checkbox-gap` the space to the label; they fall back to `1.375em` and `0.5em`, i.e. 22px and 8px at a 16px base. The border and the tick's padding are `em` too, so nothing has to be retuned when the box changes.

**Checked and unchecked are the same size.** The fill is the box's own background rather than an oversized tick laid over it, so the two states occupy identical space. `background` sets that fill, and applies to the border as well.

The `width` and `height` props still win where they are given, for a checkbox that must match a fixed layout rather than the text around it.
