Checkbox
Checkbox — one independent yes/no, built on <input type="checkbox">.
Examples
<div class="field field-inline">
<input class="check" id="demo-terms" name="terms" type="checkbox" checked />
<label class="field-label" for="demo-terms">Default — 24px, the WCAG target floor</label>
</div>
<div class="field field-inline">
<input class="check check-lg" id="demo-news" name="news" type="checkbox" />
<label class="field-label" for="demo-news">.check-lg — 28px, for a touch-first form</label>
</div>
<div class="field field-inline">
<input class="check" id="demo-mixed" type="checkbox" data-demo-indeterminate />
<label class="field-label" for="demo-mixed">Indeterminate — set from script, never from markup</label>
</div>
<div class="field field-inline">
<input class="check" id="demo-locked" type="checkbox" checked disabled />
<label class="field-label" for="demo-locked">Disabled</label>
</div>One box per row, because a checkbox is a statement you agree or disagree with and a row of them side by side reads as one sentence.
There is no .check-sm: a checkbox is square, so a smaller one is a smaller target, and every value under 24px fails SC 2.5.8. Where a form needs to be denser, --space-scale tightens the spacing and leaves the targets alone.
<fieldset class="form-section">
<legend class="form-section-title">Labels</legend>
<div class="field field-inline">
<input class="check" id="demo-all" type="checkbox" data-demo-indeterminate />
<label class="field-label" for="demo-all">Select all labels</label>
</div>
<div class="field field-inline">
<input class="check" id="demo-lbl-bug" type="checkbox" checked />
<label class="field-label" for="demo-lbl-bug">bug</label>
</div>
<div class="field field-inline">
<input class="check" id="demo-lbl-docs" type="checkbox" />
<label class="field-label" for="demo-lbl-docs">documentation</label>
</div>
</fieldset>indeterminate is an IDL property with no HTML attribute, so no markup can put a checkbox into this state — input.indeterminate = true is the only way, and it is the consumer's call.
The library styles :indeterminate and stops there. It is a third appearance, not a third value: the box still submits checked or unchecked, and clicking it clears the state. Announced as "mixed".
Tokens 12
Level 2, declared on .check itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --check-bg | var(--color-surface-sunken) |
| --check-border-color | var(--color-border-strong) |
| --check-border-width | var(--border-width) |
| --check-checked-bg | var(--color-primary) |
| --check-checked-border-color | var(--color-primary) |
| --check-invalid-border-color | var(--color-danger) |
| --check-mark-color | var(--color-on-primary) |
| --check-mark-size | 75% |
| --check-radius | var(--radius-selector) |
| --check-size | 1.5rem |
| --check-mark | url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") |
| --check-mark-indeterminate | url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E") |
Variants and states
Variants
.check-lg
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
:checked:disabled:focus:hover:indeterminate:user-invalid
Before you ship it
What you have to do 2 requirements
- Label every box —
<label for>or a wrapping<label>. - Set
indeterminatefrom script if you want the mixed state, and pair it witharia-checked="mixed"on a parent box that summarises children. The IDL property is the only way in; there is no attribute.
src/css/components/checkbox.css · npx mostlycss add checkbox