Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

Components

Checkbox

Checkboxes allow users to select one or more items from a set of options. See also: Checkbox field group.

Full support Supported since v105. Full support Supported since v121. Full support Supported since v15.4.
<!-- Checked -->
<label class="checkbox">
<input name="checkbox-variants-html" type="checkbox" checked />
<span class="sr-only">Checked</span>
</label>
<!-- Unchecked -->
<label class="checkbox">
<input name="checkbox-variants-html" type="checkbox" />
<span class="sr-only">Unchecked</span>
</label>
<!-- Indeterminate -->
<label class="checkbox">
<input name="checkbox-variants-html" type="checkbox" data-indeterminate />
<span class="sr-only">Indeterminate</span>
</label>
<!-- Disabled -->
<label class="checkbox">
<input name="checkbox-variants-html" type="checkbox" disabled />
<span class="sr-only">Disabled</span>
</label>
<!-- Checked and disabled -->
<label class="checkbox">
<input name="checkbox-variants-html" type="checkbox" checked disabled />
<span class="sr-only">Checked and disabled</span>
</label>

Visible label

Render the label text inside an element with a .label class. Also, don't miss the info on label accessibility.

<label class="checkbox">
<input name="checkbox-visible-label-html" type="checkbox" checked />
<span class="label">Choice A</span>
</label>
<label class="checkbox">
<input name="checkbox-visible-label-html" type="checkbox" disabled />
<span class="label">Disabled</span>
</label>
<label class="checkbox">
<input name="checkbox-visible-label-html" type="checkbox" checked disabled />
<span class="label">Checked and disabled</span>
</label>
<label class="checkbox">
<input name="checkbox-visible-label-html" type="checkbox" />
<span class="label">
Long text dolor amet mustache knausgaard +1, blue bottle waistcoat tbh
semiotics artisan synth stumptown gastropub cornhole
<a class="link" href="#visible-label">privacy policy ipsum</a>
</span>
</label>

Label position

<label class="checkbox">
<input name="checkbox-label-position-html" type="checkbox" />
<span class="label">Default</span>
</label>
<label class="checkbox stack">
<input name="checkbox-label-position-html" type="checkbox" />
<span class="label">Stack</span>
</label>

End text

<label class="checkbox">
<input name="checkbox-supporting-text-html" type="checkbox" />
<span class="label">Default</span>
<span class="end-text">Supporting text</span>
</label>
<label class="checkbox stack">
<input name="checkbox-supporting-text-html" type="checkbox" />
<span class="label">Stack</span>
<span class="end-text">Supporting text</span>
</label>

Validation

  • Add [required] to the <input> element to toggle required styles
  • The data-invalid attribute toggles the error styles. Make use of the end text to give extra feedback on the error.
<div class="example-row spacious">
<label class="checkbox">
<input name="checkbox-validation-html" type="checkbox" required />
<span class="label">Default</span>
</label>
<label class="checkbox stack">
<input name="checkbox-validation-html" type="checkbox" required />
<span class="label">Stack</span>
</label>
</div>
<div class="example-row spacious">
<label class="checkbox" data-invalid>
<input name="checkbox-validation-html" type="checkbox" checked />
<span class="label">Default</span>
<span class="end-text">Check yourself</span>
</label>
<label class="checkbox stack" data-invalid>
<input name="checkbox-validation-html" type="checkbox" />
<span class="label">Stack</span>
<span class="end-text">Before you wreck yourself</span>
</label>
</div>

Indeterminate

Add data-indeterminate to the <input type="checkbox"> and run a script that sets el.indeterminate = true. indeterminate is a JavaScript-only property on HTMLInputElement — the attribute alone has no effect.

JavaScript required

The indeterminate state cannot be set with HTML or CSS alone. The browser only exposes it as a property on HTMLInputElement, so a small script is needed to flip el.indeterminate = true after the element is in the DOM. The :indeterminate CSS pseudo-class then matches and the dash glyph appears.
<fieldset class="fieldset indeterminate-demo-html">
<legend>
<label class="checkbox parent">
<input type="checkbox" />
<span class="label">Select all</span>
</label>
</legend>
<div class="field-group" role="group">
<label class="checkbox child">
<input name="indeterminate-children-html" type="checkbox" checked />
<span class="label">Apples</span>
</label>
<label class="checkbox child">
<input name="indeterminate-children-html" type="checkbox" />
<span class="label">Bananas</span>
</label>
<label class="checkbox child">
<input name="indeterminate-children-html" type="checkbox" />
<span class="label">Cherries</span>
</label>
</div>
</fieldset>
<script>
function setupIndeterminateDemoHtml() {
document.querySelectorAll(".indeterminate-demo-html").forEach((root) => {
const parent = root.querySelector('.parent input[type="checkbox"]');
const children = Array.from(
root.querySelectorAll('.child input[type="checkbox"]'),
);
if (!parent || children.length === 0) return;
const sync = () => {
const checkedCount = children.filter((c) => c.checked).length;
parent.checked = checkedCount === children.length;
parent.indeterminate =
checkedCount > 0 && checkedCount < children.length;
};
parent.addEventListener("change", () => {
children.forEach((c) => (c.checked = parent.checked));
parent.indeterminate = false;
});
children.forEach((c) => c.addEventListener("change", sync));
sync();
});
}
setupIndeterminateDemoHtml();
document.addEventListener("astro:after-swap", setupIndeterminateDemoHtml);
</script>

Spread

Add the .spread class to the <label.checkbox> to push the label to the left and the checkbox to the right. This is useful for full-width items like lists and menus.

<label class="checkbox spread">
<input name="checkbox-spread-html" type="checkbox" />
<span class="label">Accept Terms & Conditions</span>
<span class="end-text">I have read and agree to the privacy policy.</span>
</label>
<label class="checkbox spread">
<input name="checkbox-spread-html" type="checkbox" required />
<span class="label">Required</span>
<span class="end-text">You must accept this to continue.</span>
</label>
<label class="checkbox spread">
<input name="checkbox-spread-html" type="checkbox" disabled />
<span class="label">Disabled</span>
<span class="end-text">This checkbox is disabled.</span>
</label>
<label class="checkbox spread critical">
<input name="checkbox-spread-html" type="checkbox" />
<span class="label">Invalid Checkbox</span>
<span class="end-text">There is an error with this checkbox.</span>
</label>

Sizes

<div class="example-row">
<label class="checkbox small">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="sr-only">Label</span>
</label>
<label class="checkbox">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="sr-only">Label</span>
</label>
<label class="checkbox large">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="sr-only">Label</span>
</label>
</div>
<div class="example-row">
<label class="checkbox small">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="label">Small</span>
</label>
<label class="checkbox">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="label">Default</span>
</label>
<label class="checkbox large">
<input name="checkbox-sizes-html" type="checkbox" checked />
<span class="label">Large</span>
</label>
</div>

Field group

Use field groups to group related checkboxes.

Give every <input type="checkbox"> in the group the same name attribute so they're submitted together.

See also: Form documentation.

Legend
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group" role="group">
<label class="checkbox">
<input name="checkbox-group-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>

Direction

Legend
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-direction-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-direction-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-direction-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>

Field description

Can be placed above and below the fields.

Legend Field description above fields
Legend
Field description below fields
<fieldset class="fieldset">
<legend>Legend</legend>
<span class="field-description">Field description above fields</span>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-field-description-1-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-field-description-1-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-field-description-1-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-field-description-2-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-field-description-2-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-field-description-2-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
<span class="field-description">Field description below fields</span>
</fieldset>

Disabled

Attach the disabled attribute to the <fieldset> element.

Legend
<fieldset class="fieldset" disabled>
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-disabled-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-disabled-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-disabled-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>

Required

Attach the required attribute to at least one of your <input> elements.

These are required!
<fieldset class="fieldset">
<legend>These are required!</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-required-html" type="checkbox" required />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-required-html" type="checkbox" required />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-required-html" type="checkbox" required />
<span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>

Validation

Attach the data-invalid attribute to your fieldset.fieldset element

Legend
Something went wrong!
<fieldset class="fieldset" data-invalid>
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input name="checkbox-group-validation-html" type="checkbox" checked />
<span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input name="checkbox-group-validation-html" type="checkbox" />
<span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input name="checkbox-group-validation-html" type="checkbox" />
<span class="label">Checkbox 3</span>
</label>
</div>
<span class="end-text">Something went wrong!</span>
</fieldset>

Accessibility

Labels

Accessible checkboxes must have a label. You can choose between three approaches:

Approach Usage in Checkbox component
Provide a label text inside the label/role="checkbox" element Default
Add a aria-label on the input element Not used
Have a visible label that you reference with aria-labelledby Not used

Keyboard support

Key Function
Space When Checkbox is focused it changes its state.
Enter (Optional) When Checkbox is focused it changes its state.

Anatomy

  1. Container
  2. Input
  3. Label (optional)
  4. End text (optional)
<script type="module">
function e(t = document) {
t.querySelectorAll('input[type="checkbox"][data-indeterminate]').forEach(
(n) => {
n.indeterminate = !0;
},
);
}
function a() {
(e(), document.addEventListener("astro:after-swap", () => e()));
}
a();
</script>
<label class="checkbox anatomy">
<input type="checkbox" checked aria-describedby="end-text-1" /><span
class="label"
>
Label
</span>
<span id="end-text-1" class="end-text">End text</span>
</label>

API

Checkbox API

Type Modifiers Default Description
Children .label, .end-text - Internal element classes.
Layout .spread, .stack, default - The layout of the component. .spread pushes label and input to opposite ends. .stack stacks the label under the input.
Sizes .small, .large - Size modifiers.
Validation .critical - Error state modifier.
State data-indeterminate - Marker attribute on the <input>. Pair with a script that sets el.indeterminate = true — the property is JS-only and the attribute alone has no effect.

Field group API

Type Modifiers Default Description
Children <legend>, .legend, .fields, .end-text, checkbox, radio, switch - Supported child elements.
Direction default, .row - The orientation of the element.
Disabled [disabled] - When applied, disabled styles are shown.
Validation .critical - When applied, error styles are shown.

Browser support

Full support Supported since v105. Full support Supported since v121. Full support Supported since v15.4.

See also the full browser support guide.

Installation

See also

@layer components.root {
label.checkbox {
--_input-size: var(--size-4);
align-items: center;
color: var(--text-primary);
cursor: pointer;
display: inline-grid;
gap: 0 var(--size-2);
grid-auto-columns: auto;
grid-auto-flow: column;
inline-size: fit-content;
line-height: 1.5;
transform: translateZ(0);
user-select: none;
/* Disabled */
&:has([disabled]) {
cursor: not-allowed;
opacity: 0.64;
user-select: none;
input {
cursor: not-allowed;
}
}
/* Required dot */
&:has(:invalid) {
.label:after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
/* Label */
.label {
color: var(--text-primary);
font-size: var(--font-size-05);
grid-column: 2;
grid-row: 1;
position: relative;
padding-inline: 0 1ex;
}
/* End text */
:where(.end-text) {
color: var(--text-muted);
font-size: var(--font-size-0);
grid-column: 2;
grid-row: 2;
line-height: 1.5;
z-index: 1;
}
/* Spread layout */
&.spread {
align-items: center;
column-gap: var(--size-4);
grid-auto-flow: unset;
grid-template-columns: 1fr auto;
inline-size: 100%;
.label {
font-weight: 600;
grid-column: 1;
grid-row: 1;
inline-size: fit-content;
}
input {
grid-column: 2;
grid-row: 1;
}
:where(.end-text) {
grid-column: 1;
grid-row: 2;
}
}
/* Stacked layout */
&.stack {
justify-items: center;
grid-auto-columns: unset;
.label {
grid-column: 1/-1;
grid-row: 2;
margin-block-start: var(--size-1);
padding-inline: 1ex;
/* Required dot */
&::after {
inset: 0 -0.25ex auto auto;
}
}
.end-text {
grid-column: 1/-1;
grid-row: 3;
}
}
/* Input */
input[type="checkbox"] {
--_input-size: var(--size-4);
appearance: none;
aspect-ratio: 1;
background-color: var(--surface-default);
block-size: var(--_input-size);
border-radius: var(--radius-1);
border: 1px solid var(--border-color);
box-sizing: border-box;
cursor: pointer;
display: grid;
font-size: var(--_input-size);
inline-size: var(--_input-size);
margin: 0;
padding: 0;
place-items: center;
position: relative;
&:checked,
&:indeterminate {
background-color: var(--primary);
border-color: var(--primary);
}
/* Checkmark */
&::after {
background-color: var(--primary-contrast);
clip-path: polygon(15% 52%, 40% 77%, 85% 32%, 75% 22%, 40% 57%, 25% 42%);
content: "";
inset: 0;
opacity: 0;
position: absolute;
}
&:checked::after,
&:indeterminate::after {
opacity: 1;
}
/* Indeterminate dash */
&:indeterminate::after {
clip-path: polygon(20% 45%, 80% 45%, 80% 55%, 20% 55%);
}
&::before {
--highlight-size: 175%;
}
}
/* Sizes */
&.small {
input[type="checkbox"] {
--_input-size: var(--size-3);
}
}
&.large {
input[type="checkbox"] {
--_input-size: var(--size-5);
}
}
/* Validation */
&[data-invalid],
&:has(:user-invalid) {
--palette-source: oklch(0.58 0.21 var(--hue-red));
:where(.end-text) {
color: var(--primary);
}
}
/* Touch devices */
@media (pointer: coarse) {
input {
--_input-size: var(--size-4);
}
}
@media (forced-colors: active) {
input {
border: 1px solid CanvasText;
&:checked,
&:indeterminate {
background-color: SelectedItem;
border-color: SelectedItem;
&::after {
background-color: SelectedItemText;
}
}
}
}
}
}
@layer components.extended {
/* Form */
:where(.form) {
display: grid;
gap: var(--size-8);
hr {
margin-block: 0;
}
}
/* Fieldset */
:where(.fieldset) {
all: unset;
border: 0;
border-radius: 0;
display: grid;
gap: var(--size-1);
margin: 0;
min-inline-size: 0;
padding: 0;
/* Legend */
:where(legend, .legend) {
all: unset;
color: var(--text-primary);
font-weight: 600;
margin-block-end: var(--size-3);
padding: 0;
&:has(+ :where(.field-description)) {
margin-block-end: 0;
}
}
/* Field description / supporting text */
:where(.field-description) {
color: var(--text-muted);
font-size: var(--font-size-05);
line-height: var(--font-lineheight-3);
&:has(+ *) {
margin-block-end: var(--size-3);
}
}
/* End text */
:where(.end-text) {
color: var(--text-muted);
font-size: var(--font-size-0);
line-height: var(--font-lineheight-3);
}
&:has(.text-field.row) {
row-gap: var(--size-7);
}
/* Disabled */
&[disabled] {
opacity: 0.64;
user-select: none;
input,
label,
.label {
cursor: not-allowed;
}
}
/* Error / validation */
&[data-invalid] {
:where(.end-text) {
color: var(--primary);
}
:where(.checkbox, .radio, .switch) {
--palette-source: oklch(0.58 0.21 var(--hue-red));
:where(.end-text) {
color: var(--primary);
}
}
:where(.switch) {
input {
border-radius: var(--radius-round);
outline: 2px solid var(--primary);
}
}
}
/* Required */
&:has(:invalid) {
:where(legend, .legend) {
padding-inline-end: 1ex;
position: relative;
&::after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
}
}
/* Field group */
:where(.field-group) {
display: flex;
flex-direction: column;
gap: var(--size-4);
/* If it only has checkboxes, radios, or switches */
&:has(> :where(.checkbox, .radio, .switch)):not(:has(> :not(.checkbox, .radio, .switch))) {
gap: var(--size-2);
}
/* If it only has buttons */
&:has(.button):not(:has(*:not(.button))) {
align-items: center;
flex-direction: row;
gap: var(--size-2);
/* If it's not preceeded by an hr */
&:not(hr + &) {
margin-block-start: var(--size-4);
}
}
&+& {
margin-block-start: var(--size-5);
}
/* Directions */
&.row {
flex-direction: row;
flex-wrap: wrap;
&:has(> :where(.checkbox, .radio, .switch)) {
gap: var(--size-4);
}
}
}
}