# Checkbox

> A checkbox for a choice that stands on its own. CSS component with two classes and no JavaScript.

Plain CSS on an ordinary `<input type="checkbox">`, so the keyboard, `FormData` and the browser’s own validation work as before. If the user should choose one of several options, [Radio](/components/radio.md) is right. If the choice turns a setting on or off and takes effect at once, use [Switch](/components/switch.md).

## How to use it

**Plain HTML**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/tokens/tokens.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/checkbox/checkbox.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/label/label.css">
<!-- Or everything in one file: https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/dist/lotek.css -->
```

```html
<div class="lt-checkbox-row">
<input class="lt-checkbox" type="checkbox" id="terms" />
<label class="lt-label" for="terms">I accept the terms</label>
</div>


<div class="lt-checkbox-row">
<input class="lt-checkbox" type="checkbox" id="newsletter" checked />
<label class="lt-label" for="newsletter">Send me newsletters</label>
</div>


<div class="lt-checkbox-row">
<input class="lt-checkbox" type="checkbox" id="confirm" data-state="invalid" aria-invalid="true" />
<label class="lt-label" for="confirm">I confirm that the information is correct</label>
</div>


<div class="lt-checkbox-row">
<input class="lt-checkbox" type="checkbox" id="archived" checked disabled />
<label class="lt-label" for="archived">The case is archived</label>
</div>
```

**TypeScript**

```ts
import "@lo-tek/interfaces/tokens.css"
import "@lo-tek/interfaces/checkbox.css"
import "@lo-tek/interfaces/label.css"
```

`lt` is used in two ways, and which one depends on whether you create the element or find it.

**If you create the markup,** you spread the attributes in. That is the route in React, Astro and anything else that renders:

```tsx
import { lt } from "@lo-tek/interfaces/react"


<div className={lt.checkbox.row}>
  <input {...lt.checkbox()} id="terms" />
  <label {...lt.label()} htmlFor="terms">I accept the terms</label>
</div>
```

The import goes to `/react` here, since the block is JSX. From there the keys are called `className` and `htmlFor`. In Astro and anything else without React you import `@lo-tek/interfaces` and get `class` and `for`.

**If the element is already there,** you set them on it. That is the route when you enhance HTML a server or a template has sent:

```ts
import { lt, type FieldState } from "@lo-tek/interfaces"


const box = document.querySelector<HTMLInputElement>("#confirm")
if (!box) throw new Error("element not found")


box.addEventListener("change", () => {
  const state: FieldState = box.checked ? "default" : "invalid"


  lt.setAttributes(box, lt.checkbox({ state }))
})
```

`lt.checkbox()` sets `type` for you, so the box cannot become a text field with the checkbox’s style. `lt.checkbox.row` gives the class for the row around it. See [Type-safe use](/type-safe-use.md).

| `data-state` | Used for |
| - | - |
| *(omitted)* | Normal state |
| `invalid` | Validation has failed |
| `success` | The choice is confirmed valid. Use sparingly. |

`data-state` is colour. Screen readers also need `aria-invalid="true"`. `lt.checkbox({ state: "invalid" })` and [Field](/components/field.md) both set it.

## Partly checked

“Select all” is neither on nor off when some of the options under it are checked. That state exists only as a property in JavaScript, not as an HTML attribute:

```html
<div class="lt-checkbox-row">
  <input class="lt-checkbox" type="checkbox" id="all" />
  <label class="lt-label" for="all">Select all attachments</label>
</div>
<div class="lt-checkbox-row">
  <input class="lt-checkbox attachment" type="checkbox" id="attachment-certificate" name="attachment" value="certificate" checked />
  <label class="lt-label" for="attachment-certificate">Birth certificate</label>
</div>
<div class="lt-checkbox-row">
  <input class="lt-checkbox attachment" type="checkbox" id="attachment-lease" name="attachment" value="lease" />
  <label class="lt-label" for="attachment-lease">Tenancy agreement</label>
</div>


<script type="module">
  const all = document.getElementById("all")
  const options = document.querySelectorAll(".attachment")


  function update() {
    const count = [...options].filter((box) => box.checked).length


    all.checked = count === options.length
    all.indeterminate = count > 0 && count < options.length
  }


  // "Select all" checks or unchecks everything under it.
  all.addEventListener("change", () => {
    for (const box of options) box.checked = all.checked
    update()
  })
  for (const box of options) box.addEventListener("change", update)
  update()
</script>
```

Screen readers announce the state as “partially checked” by themselves. You do not need `aria-checked="mixed"` on a real `<input>`.

## Several choices that belong together

If the boxes belong to the same question, group them in a [Fieldset](/components/fieldset.md). The question is then announced before each option.

**Plain HTML**

```html
<fieldset class="lt-fieldset">
<legend class="lt-legend">How would you like to be notified?</legend>


<div class="lt-checkbox-row">
  <input class="lt-checkbox" type="checkbox" id="notify-email" name="notify" value="email" checked />
  <label class="lt-label" for="notify-email">Email</label>
</div>


<div class="lt-checkbox-row">
  <input class="lt-checkbox" type="checkbox" id="notify-sms" name="notify" value="sms" />
  <label class="lt-label" for="notify-sms">Text message</label>
</div>
</fieldset>
```

Identical `name` values give one key with several values in `FormData`. Read them with `formData.getAll("notify")`.

## Classes you can style

| Class | What it is |
| - | - |
| `.lt-checkbox` | The box itself |
| `.lt-checkbox-row` | The row that holds the box and the text side by side |

| Variable | Default |
| - | - |
| `--lt-checkbox-size` | `var(--lt-spacing-5)` |
| `--lt-checkbox-radius` | `var(--lt-spacing-1)` |

The tick and the dash are drawn from `--lt-icon-check` and `--lt-icon-dash`, which follow the theme. See [Customising](/customising.md).

## Accessibility

- Every box needs a `<label>` with `for`. Without it the click area is only the box itself, and the screen reader reads nothing.
- The text should say what happens when the box is checked, not what the question was called. “Send me newsletters”, not “Newsletter”.
- Do not submit the form or navigate when a box changes. The user expects to be able to check several before anything happens.
- If checking is required, set `required` on the box. The browser then stops the submission itself.
