# Checkbox

A native `<input type="checkbox">` styled via class + data attributes from the optional [styles layer](/ui/styles). Two variants, three sizes, color theming, high-contrast and disabled states. The checkmark is an inline SVG mask and the indeterminate dash a drawn bar, both recolored via `currentColor` — no JS.



## Install

```ts
import "elements-kit/ui/styles.css";

// pick a neutral palette for --neutral-* (must match data-neutral on root):
import "elements-kit/ui/styles/palette/gray.css";
import "elements-kit/ui/styles/neutral/gray.css";
// import any color scales you want to use for accent theming:
import "elements-kit/ui/styles/palette/mint.css";
import "elements-kit/ui/styles/accent/mint.css";
// and the checkbox itself:
import "elements-kit/ui/checkbox/checkbox.css";
```

See [Styles](/ui/styles) for the full token system, accent/gray scale lists, and theming knobs.

## API

```html
<input
  type="checkbox"
  class="unset x-checkbox"
  data-variant="surface"
  data-size="2"
/>
```

The `unset` class ([Styles → Unset native styles](/ui/styles#unset-native-styles)) clears the browser's default `<input>` rendering so `.x-checkbox` styles render predictably. Skip it and you'll see the OS-native checkbox leak through.

| Attribute                                  | Values                                                             |
| ------------------------------------------ | ------------------------------------------------------------------ |
| `data-variant`                             | `surface` (default), `soft`                                        |
| `data-size`                                | `1`, `2` (default), `3`                                            |
| `data-accent` *(on a parent or the input)* | any imported color scale (`mint`, `blue`, `iris`, …)               |
| `data-high-contrast`                       | modifier — boosts contrast against the page background             |
| `disabled` / `data-disabled`               | native `:disabled` state, or `data-disabled` on non-input elements |

## Sizing

| `data-size`     | box                          | indicator          | radius               |
| --------------- | ---------------------------- | ------------------ | -------------------- |
| `1`             | `--space-4 × 0.875` (\~14px) | `9px × --scaling`  | `--radius-1 × 0.875` |
| `2` *(default)* | `--space-4` (\~16px)         | `10px × --scaling` | `--radius-1`         |
| `3`             | `--space-4 × 1.25` (\~20px)  | `12px × --scaling` | `--radius-1 × 1.25`  |

`--scaling` on `<html>` scales the indicator with the rest of the type system.

## Variants

* **`surface`** *(default)* — 1px hairline border using `--neutral-a7`. Background fills with `--accent-9` and contrast indicator on `:checked` / `:indeterminate`.
* **`soft`** — tinted background using `--accent-a5` at every state. Indicator uses `--accent-a11` so the check reads at low emphasis.

## States

* **`:checked`** — renders a checkmark (SVG mask, sized by the indicator) in the indicator slot.
* **`:indeterminate`** — renders a horizontal bar. The browser doesn't expose this via HTML — set it from JS (`el.indeterminate = true`) and CSS picks it up.
* **`:disabled`** / `[data-disabled]` — muted border + muted indicator color, no hover changes.
* **`:focus-visible`** — 2px `--focus-8` outline at `+2px` offset.

## With a label

`.x-checkbox` is just an input — wrap it in a `<label>` (or pair via `for=`) to make text clickable too.

```html
<label style="display: inline-flex; align-items: center; gap: 0.5em;">
  <input type="checkbox" class="unset x-checkbox" />
  <span>Subscribe to updates</span>
</label>
```

For multiple options stack labels in a flex column with `gap: var(--space-1)`. The kit deliberately doesn't ship a `.x-checkbox-group` class — flex + gap is one line.

## Theming

Set `data-accent="<color>"` on the input or any ancestor (e.g. `<body>`) to theme the accent fill. Light/dark flips automatically via the `.dark` class on a parent — see [Light & dark](/ui/styles#light--dark).

```html
<body data-accent="iris">
  <input type="checkbox" class="unset x-checkbox" checked />
</body>
```

## Accessibility

Pair every checkbox with a `<label>` — either by wrapping the input or by using `<label for="…">`. Indeterminate has no built-in screen-reader announcement; if the tri-state is semantically meaningful, add `aria-checked="mixed"` alongside setting `el.indeterminate = true`.

> **Don't**
>
> Ship a bare `<input type="checkbox">` without a visible or programmatic label. Assistive tech has nothing to announce.
