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.
---
import { Checkbox } from "@opui/astro"
---
<Checkbox checked name="checkbox" hideLabel>Checked</Checkbox>
<Checkbox name="checkbox" hideLabel>Unchecked</Checkbox>
<Checkbox indeterminate name="checkbox" hideLabel>Indeterminate</Checkbox>
<Checkbox disabled name="checkbox" hideLabel>Disabled</Checkbox>
<Checkbox checked disabled name="checkbox" hideLabel
>Checked and disabled</Checkbox
>
<label class="checkbox">
<input type="checkbox" checked name="checkbox" /><span class="sr-only"
>Checked</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox" /><span class="sr-only"
>Unchecked</span
>
</label>
<label class="checkbox">
<input type="checkbox" data-indeterminate="true" name="checkbox" /><span
class="sr-only"
>Indeterminate</span
>
</label>
<label class="checkbox">
<input type="checkbox" disabled name="checkbox" /><span class="sr-only"
>Disabled</span
>
</label>
<label class="checkbox">
<input type="checkbox" checked disabled name="checkbox" /><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.

---
import { Checkbox } from "@opui/astro"
---
<Checkbox checked name="checkbox">Choice A</Checkbox>
<Checkbox disabled name="checkbox">Disabled</Checkbox>
<Checkbox checked disabled name="checkbox">Checked and disabled</Checkbox>
<Checkbox name="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
>
</Checkbox>
<label class="checkbox">
<input type="checkbox" checked name="checkbox" /><span class="label"
>Choice A</span
>
</label>
<label class="checkbox">
<input type="checkbox" disabled name="checkbox" /><span class="label"
>Disabled</span
>
</label>
<label class="checkbox">
<input type="checkbox" checked disabled name="checkbox" /><span class="label"
>Checked and disabled</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox" /><span class="label">
<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
>
</span>
</label>

Label position

---
import { Checkbox } from "@opui/astro"
---
<Checkbox name="checkbox">Default</Checkbox>
<Checkbox stack name="checkbox">Stack</Checkbox>
<label class="checkbox">
<input type="checkbox" name="checkbox" /><span class="label">Default</span>
</label>
<label class="checkbox stack">
<input type="checkbox" name="checkbox" /><span class="label">Stack</span>
</label>

End text

---
import { Checkbox } from "@opui/astro"
---
<Checkbox name="checkbox">
Default
<Fragment slot="end-text">Supporting text</Fragment>
</Checkbox>
<Checkbox stack name="checkbox">
Stack
<Fragment slot="end-text">Supporting text</Fragment>
</Checkbox>
<label class="checkbox">
<input type="checkbox" name="checkbox" aria-describedby="end-text-1" /><span
class="label"
>
Default
</span>
<span id="end-text-1" class="end-text">Supporting text</span>
</label>
<label class="checkbox stack">
<input type="checkbox" name="checkbox" aria-describedby="end-text-2" /><span
class="label"
>
Stack
</span>
<span id="end-text-2" 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.
---
import { Checkbox } from "@opui/astro"
---
<div class="example-row spacious">
<Checkbox required name="checkbox">Default</Checkbox>
<Checkbox stack required name="checkbox">Stack</Checkbox>
</div>
<div class="example-row spacious">
<Checkbox critical checked name="checkbox">
Default
<Fragment slot="end-text">Check yourself</Fragment>
</Checkbox>
<Checkbox stack critical name="checkbox">
Stack
<Fragment slot="end-text">Before you wreck yourself</Fragment>
</Checkbox>
</div>
<div class="example-row spacious">
<label class="checkbox">
<input type="checkbox" name="checkbox" required /><span class="label"
>Default</span
>
</label>
<label class="checkbox stack">
<input type="checkbox" name="checkbox" required /><span class="label"
>Stack</span
>
</label>
</div>
<div class="example-row spacious">
<label class="checkbox" data-invalid="true">
<input
type="checkbox"
checked
name="checkbox"
aria-describedby="end-text-3"
/><span class="label"> Default </span>
<span id="end-text-3" class="end-text">Check yourself</span>
</label>
<label class="checkbox stack" data-invalid="true">
<input type="checkbox" name="checkbox" aria-describedby="end-text-4" /><span
class="label"
>
Stack
</span>
<span id="end-text-4" class="end-text">Before you wreck yourself</span>
</label>
</div>

Indeterminate

Set the indeterminate prop to render a partially-selected state. indeterminate is a JavaScript-only property on HTMLInputElement, so the component renders data-indeterminate and applies the property at runtime.

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.
---
import { Checkbox, FieldGroup, FieldLegend, FieldSet } from "@opui/astro"
---
<FieldSet class="indeterminate-demo">
<FieldLegend>
<Checkbox class="parent">Select all</Checkbox>
</FieldLegend>
<FieldGroup name="indeterminate-children-astro">
<Checkbox class="child" checked>Apples</Checkbox>
<Checkbox class="child">Bananas</Checkbox>
<Checkbox class="child">Cherries</Checkbox>
</FieldGroup>
</FieldSet>
<script>
function setupIndeterminateDemo() {
document
.querySelectorAll<HTMLElement>(".indeterminate-demo")
.forEach((root) => {
const parent = root.querySelector<HTMLInputElement>(
'.parent input[type="checkbox"]',
)
const children = Array.from(
root.querySelectorAll<HTMLInputElement>(
'.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()
})
}
setupIndeterminateDemo()
document.addEventListener("astro:after-swap", setupIndeterminateDemo)
</script>
<script type="module">
function d() {
document.querySelectorAll(".indeterminate-demo").forEach((c) => {
const t = c.querySelector('.parent input[type="checkbox"]'),
n = Array.from(c.querySelectorAll('.child input[type="checkbox"]'));
if (!t || n.length === 0) return;
const r = () => {
const e = n.filter((a) => a.checked).length;
((t.checked = e === n.length),
(t.indeterminate = e > 0 && e < n.length));
};
(t.addEventListener("change", () => {
(n.forEach((e) => (e.checked = t.checked)), (t.indeterminate = !1));
}),
n.forEach((e) => e.addEventListener("change", r)),
r());
});
}
d();
document.addEventListener("astro:after-swap", d);
</script>
<fieldset class="fieldset indeterminate-demo">
<legend>
<label class="checkbox parent">
<input type="checkbox" /><span class="label">Select all</span>
</label>
</legend>
<div class="field-group" role="group">
<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 child">
<input type="checkbox" checked name="indeterminate-children-astro" /><span
class="label"
>Apples</span
>
</label>
<label class="checkbox child">
<input type="checkbox" name="indeterminate-children-astro" /><span
class="label"
>Bananas</span
>
</label>
<label class="checkbox child">
<input type="checkbox" name="indeterminate-children-astro" /><span
class="label"
>Cherries</span
>
</label>
</div>
</fieldset>

Spread

Use the spread prop to push the label to the left and the checkbox to the right. This is useful for full-width items like lists and menus.

---
import { Checkbox } from "@opui/astro"
---
<Checkbox spread>
Accept Terms & Conditions
<Fragment slot="end-text"
>I have read and agree to the privacy policy.</Fragment
>
</Checkbox>
<Checkbox spread required>
Required
<Fragment slot="end-text">You must accept this to continue.</Fragment>
</Checkbox>
<Checkbox spread disabled>
Disabled
<Fragment slot="end-text">This checkbox is disabled.</Fragment>
</Checkbox>
<Checkbox spread critical>
Invalid Checkbox
<Fragment slot="end-text">There is an error with this checkbox.</Fragment>
</Checkbox>
<label class="checkbox spread">
<input
type="checkbox"
name="indeterminate-children-astro"
aria-describedby="end-text-5"
/><span class="label"> Accept Terms & Conditions </span>
<span id="end-text-5" class="end-text"
>I have read and agree to the privacy policy.</span
>
</label>
<label class="checkbox spread">
<input
type="checkbox"
name="indeterminate-children-astro"
required
aria-describedby="end-text-6"
/><span class="label"> Required </span>
<span id="end-text-6" class="end-text"
>You must accept this to continue.</span
>
</label>
<label class="checkbox spread">
<input
type="checkbox"
disabled
name="indeterminate-children-astro"
aria-describedby="end-text-7"
/><span class="label"> Disabled </span>
<span id="end-text-7" class="end-text">This checkbox is disabled.</span>
</label>
<label class="checkbox spread" data-invalid="true">
<input
type="checkbox"
name="indeterminate-children-astro"
aria-describedby="end-text-8"
/><span class="label"> Invalid Checkbox </span>
<span id="end-text-8" class="end-text"
>There is an error with this checkbox.</span
>
</label>

Sizes

---
import { Checkbox } from "@opui/astro"
---
<div class="example-row">
<Checkbox hideLabel size="small" checked name="checkbox">Label</Checkbox>
<Checkbox hideLabel checked name="checkbox">Label</Checkbox>
<Checkbox hideLabel size="large" checked name="checkbox">Label</Checkbox>
</div>
<div class="example-row">
<Checkbox size="small" checked name="checkbox">Small</Checkbox>
<Checkbox checked name="checkbox">Default</Checkbox>
<Checkbox size="large" checked name="checkbox">Large</Checkbox>
</div>
<div class="example-row">
<label class="checkbox small">
<input type="checkbox" checked name="checkbox" /><span class="sr-only"
>Label</span
>
</label>
<label class="checkbox">
<input type="checkbox" checked name="checkbox" /><span class="sr-only"
>Label</span
>
</label>
<label class="checkbox large">
<input type="checkbox" checked name="checkbox" /><span class="sr-only"
>Label</span
>
</label>
</div>
<div class="example-row">
<label class="checkbox small">
<input type="checkbox" checked name="checkbox" /><span class="label"
>Small</span
>
</label>
<label class="checkbox">
<input type="checkbox" checked name="checkbox" /><span class="label"
>Default</span
>
</label>
<label class="checkbox large">
<input type="checkbox" checked name="checkbox" /><span class="label"
>Large</span
>
</label>
</div>

Field group

Use field groups to group related checkboxes.

The name prop will get passed down to each checkbox in the group.

See also: Form documentation.

Legend
---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup name="checkbox-group-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group" role="group">
<label class="checkbox">
<input type="checkbox" checked name="checkbox-group-astro" /><span
class="label"
>Checkbox 1</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-astro" /><span class="label"
>Checkbox 2</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-astro" /><span class="label"
>Checkbox 3</span
>
</label>
</div>
</fieldset>
</form>

Direction

Legend
---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="checkbox-group-direction-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input
type="checkbox"
checked
name="checkbox-group-direction-astro"
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-direction-astro" /><span
class="label"
>Checkbox 2</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-direction-astro" /><span
class="label"
>Checkbox 3</span
>
</label>
</div>
</fieldset>
</form>

Field description

Can be placed above and below the fields.

Legend

Field description above fields

Legend

Field description below fields

---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldDescription } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldDescription>Field description above fields</FieldDescription>
<FieldGroup direction="row" name="checkbox-group-field-description-1-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
</FieldSet>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="checkbox-group-field-description-2-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
<FieldDescription>Field description below fields</FieldDescription>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<p class="field-description">Field description above fields</p>
<div class="field-group row" role="group">
<label class="checkbox">
<input
type="checkbox"
checked
name="checkbox-group-field-description-1-astro"
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-1-astro"
/><span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-1-astro"
/><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
type="checkbox"
checked
name="checkbox-group-field-description-2-astro"
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-2-astro"
/><span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-2-astro"
/><span class="label">Checkbox 3</span>
</label>
</div>
<p class="field-description">Field description below fields</p>
</fieldset>
</form>

Disabled

Attach the disabled attribute to the <fieldset> element.

Legend
---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet disabled>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="checkbox-group-disabled-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset disabled class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input
type="checkbox"
checked
name="checkbox-group-disabled-astro"
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-disabled-astro" /><span
class="label"
>Checkbox 2</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-disabled-astro" /><span
class="label"
>Checkbox 3</span
>
</label>
</div>
</fieldset>
</form>

Required

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

These are required!
---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>These are required!</FieldLegend>
<FieldGroup direction="row" name="checkbox-group-required-astro">
<Checkbox required>Checkbox 1</Checkbox>
<Checkbox required>Checkbox 2</Checkbox>
<Checkbox required>Checkbox 3</Checkbox>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>These are required!</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-required-astro"
required
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-required-astro"
required
/><span class="label">Checkbox 2</span>
</label>
<label class="checkbox">
<input
type="checkbox"
name="checkbox-group-required-astro"
required
/><span class="label">Checkbox 3</span>
</label>
</div>
</fieldset>
</form>

Validation

Attach the data-invalid attribute to your Fieldset component.

Legend
Something went wrong!
---
import { Checkbox } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet data-invalid>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="checkbox-group-validation-astro">
<Checkbox checked>Checkbox 1</Checkbox>
<Checkbox>Checkbox 2</Checkbox>
<Checkbox>Checkbox 3</Checkbox>
</FieldGroup>
<span class="end-text">Something went wrong!</span>
</FieldSet>
</Form>
<form class="form">
<fieldset data-invalid="true" class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="checkbox">
<input
type="checkbox"
checked
name="checkbox-group-validation-astro"
/><span class="label">Checkbox 1</span>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-validation-astro" /><span
class="label"
>Checkbox 2</span
>
</label>
<label class="checkbox">
<input type="checkbox" name="checkbox-group-validation-astro" /><span
class="label"
>Checkbox 3</span
>
</label>
</div>
<span class="end-text">Something went wrong!</span>
</fieldset>
</form>

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)

API

Checkbox API

Prop Type Default Description
checked boolean false Input checked state.
disabled boolean - Input disabled state.
critical boolean - Input error state.
hideLabel boolean - Visually hides the label.
indeterminate boolean false Visual partial-selection state. Applied via JS at runtime; does not affect form submission.
required boolean false Input required state.
size "small" | "large" - The size of the input.
spread boolean false Spreads the label and input to opposite ends.
stack boolean false Stacks the label under the input.

Slots

Slot Description
default The label text for the component.
end-text Optional supporting text displayed after the label.

Field group API

Prop Type Default Description
critical boolean false Field group error state.
legend string - The text for the legend element.
direction "row" - The orientation of the field group.

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);
}
}
}
}