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

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="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-sr-only">Checked</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" />
<span class="ui-sr-only">Unchecked</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" data-indeterminate="true" name="checkbox" />
<span class="ui-sr-only">Indeterminate</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" />
<span class="ui-sr-only">Disabled</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-sr-only">Checked and disabled</span>
</label>

Visible label

Render the label text inside an element with a .ui-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="ui-label"
>Long text dolor amet mustache knausgaard +1, blue bottle waistcoat tbh
semiotics artisan synth stumptown gastropub cornhole <a
class="ui-link"
href="#visible-label">privacy policy ipsum</a
></span
>
</Checkbox>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-label">Choice A</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" />
<span class="ui-label">Disabled</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-label">Checked and disabled</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" />
<span class="ui-label">
<span class="ui-label"
>Long text dolor amet mustache knausgaard +1, blue bottle waistcoat tbh
semiotics artisan synth stumptown gastropub cornhole
<a class="ui-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="ui-checkbox">
<input type="checkbox" name="checkbox" />
<span class="ui-label">Default</span>
</label>
<label class="ui-checkbox ui-stack">
<input type="checkbox" name="checkbox" /> <span class="ui-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="ui-checkbox">
<input type="checkbox" aria-describedby="end-text-1" name="checkbox" />
<span class="ui-label"> Default </span>
<span id="end-text-1" class="ui-end-text"> Supporting text </span>
</label>
<label class="ui-checkbox ui-stack">
<input type="checkbox" aria-describedby="end-text-2" name="checkbox" />
<span class="ui-label"> Stack </span>
<span id="end-text-2" class="ui-end-text"> Supporting text </span>
</label>

Validation

  • Add [required] to the <input> element to toggle required styles
  • Use the error prop to toggle invalid styles. It renders data-invalid on the root element. Make use of the end text to give extra feedback on the error.
---
import { Checkbox } from "@opui/astro"
---
<div class="example-row ui-spacious">
<Checkbox required name="checkbox">Default</Checkbox>
<Checkbox stack required name="checkbox">Stack</Checkbox>
</div>
<div class="example-row ui-spacious">
<Checkbox error checked name="checkbox">
Default
<Fragment slot="end-text">Check yourself</Fragment>
</Checkbox>
<Checkbox stack error name="checkbox">
Stack
<Fragment slot="end-text">Before you wreck yourself</Fragment>
</Checkbox>
</div>
<div class="example-row ui-spacious">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" required />
<span class="ui-label">Default</span>
</label>
<label class="ui-checkbox ui-stack">
<input type="checkbox" name="checkbox" required />
<span class="ui-label">Stack</span>
</label>
</div>
<div class="example-row ui-spacious">
<label class="ui-checkbox" data-invalid="true">
<input
type="checkbox"
aria-describedby="end-text-3"
name="checkbox"
checked
/>
<span class="ui-label"> Default </span>
<span id="end-text-3" class="ui-end-text"> Check yourself </span>
</label>
<label class="ui-checkbox ui-stack" data-invalid="true">
<input type="checkbox" aria-describedby="end-text-4" name="checkbox" />
<span class="ui-label"> Stack </span>
<span id="end-text-4" class="ui-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="ui-fieldset indeterminate-demo">
<legend>
<label class="ui-checkbox parent">
<input type="checkbox" /> <span class="ui-label">Select all</span>
</label>
</legend>
<div class="ui-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="ui-checkbox child">
<input type="checkbox" name="indeterminate-children-astro" checked />
<span class="ui-label">Apples</span>
</label>
<label class="ui-checkbox child">
<input type="checkbox" name="indeterminate-children-astro" />
<span class="ui-label">Bananas</span>
</label>
<label class="ui-checkbox child">
<input type="checkbox" name="indeterminate-children-astro" />
<span class="ui-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 error>
Invalid Checkbox
<Fragment slot="end-text">There is an error with this checkbox.</Fragment>
</Checkbox>
<label class="ui-checkbox ui-spread">
<input
type="checkbox"
aria-describedby="end-text-5"
name="indeterminate-children-astro"
/>
<span class="ui-label"> Accept Terms & Conditions </span>
<span id="end-text-5" class="ui-end-text">
I have read and agree to the privacy policy.
</span>
</label>
<label class="ui-checkbox ui-spread">
<input
type="checkbox"
aria-describedby="end-text-6"
name="indeterminate-children-astro"
required
/>
<span class="ui-label"> Required </span>
<span id="end-text-6" class="ui-end-text">
You must accept this to continue.
</span>
</label>
<label class="ui-checkbox ui-spread">
<input
type="checkbox"
aria-describedby="end-text-7"
name="indeterminate-children-astro"
/>
<span class="ui-label"> Disabled </span>
<span id="end-text-7" class="ui-end-text"> This checkbox is disabled. </span>
</label>
<label class="ui-checkbox ui-spread" data-invalid="true">
<input
type="checkbox"
aria-describedby="end-text-8"
name="indeterminate-children-astro"
/>
<span class="ui-label"> Invalid Checkbox </span>
<span id="end-text-8" class="ui-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="ui-checkbox ui-small">
<input type="checkbox" name="checkbox" checked />
<span class="ui-sr-only">Label</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-sr-only">Label</span>
</label>
<label class="ui-checkbox ui-large">
<input type="checkbox" name="checkbox" checked />
<span class="ui-sr-only">Label</span>
</label>
</div>
<div class="example-row">
<label class="ui-checkbox ui-small">
<input type="checkbox" name="checkbox" checked />
<span class="ui-label">Small</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox" checked />
<span class="ui-label">Default</span>
</label>
<label class="ui-checkbox ui-large">
<input type="checkbox" name="checkbox" checked />
<span class="ui-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="ui-form">
<fieldset class="ui-fieldset">
<legend>Legend</legend>
<div class="ui-field-group" role="group">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-astro" checked />
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-astro" />
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-astro" />
<span class="ui-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="ui-form">
<fieldset class="ui-fieldset">
<legend>Legend</legend>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-direction-astro" checked />
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-direction-astro" />
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-direction-astro" />
<span class="ui-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="ui-form">
<fieldset class="ui-fieldset">
<legend>Legend</legend>
<p class="ui-field-description">Field description above fields</p>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-1-astro"
checked
/>
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-1-astro"
/>
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-1-astro"
/>
<span class="ui-label">Checkbox 3</span>
</label>
</div>
</fieldset>
<fieldset class="ui-fieldset">
<legend>Legend</legend>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-2-astro"
checked
/>
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-2-astro"
/>
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input
type="checkbox"
name="checkbox-group-field-description-2-astro"
/>
<span class="ui-label">Checkbox 3</span>
</label>
</div>
<p class="ui-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="ui-form">
<fieldset disabled class="ui-fieldset">
<legend>Legend</legend>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-disabled-astro" checked />
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-disabled-astro" />
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-disabled-astro" />
<span class="ui-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="ui-form">
<fieldset class="ui-fieldset">
<legend>These are required!</legend>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-required-astro" required />
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-required-astro" required />
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-required-astro" required />
<span class="ui-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="ui-end-text">Something went wrong!</span>
</FieldSet>
</Form>
<form class="ui-form">
<fieldset data-invalid="true" class="ui-fieldset">
<legend>Legend</legend>
<div class="ui-field-group ui-row" role="group">
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-validation-astro" checked />
<span class="ui-label">Checkbox 1</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-validation-astro" />
<span class="ui-label">Checkbox 2</span>
</label>
<label class="ui-checkbox">
<input type="checkbox" name="checkbox-group-validation-astro" />
<span class="ui-label">Checkbox 3</span>
</label>
</div>
<span class="ui-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.
error 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
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.ui-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) {
.ui-label:after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
/* Label */
.ui-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(.ui-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 */
&.ui-spread {
align-items: center;
column-gap: var(--size-4);
grid-auto-flow: unset;
grid-template-columns: 1fr auto;
inline-size: 100%;
.ui-label {
font-weight: 600;
grid-column: 1;
grid-row: 1;
inline-size: fit-content;
}
input {
grid-column: 2;
grid-row: 1;
}
:where(.ui-end-text) {
grid-column: 1;
grid-row: 2;
}
}
/* Stacked layout */
&.ui-stack {
justify-items: center;
grid-auto-columns: unset;
.ui-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;
}
}
.ui-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 */
&.ui-small {
input[type="checkbox"] {
--_input-size: var(--size-3);
}
}
&.ui-large {
input[type="checkbox"] {
--_input-size: var(--size-5);
}
}
/* Validation */
&[data-invalid],
&:has(:user-invalid) {
--primary: var(--critical);
:where(.ui-end-text) {
color: var(--critical);
}
}
/* 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(.ui-form) {
display: grid;
gap: var(--size-8);
hr {
margin-block: 0;
}
}
/* Fieldset */
:where(.ui-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, .ui-legend) {
all: unset;
color: var(--text-primary);
font-weight: 600;
margin-block-end: var(--size-3);
padding: 0;
&:has(+ :where(.ui-field-description)) {
margin-block-end: 0;
}
}
/* Field description / supporting text */
:where(.ui-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(.ui-end-text) {
color: var(--text-muted);
font-size: var(--font-size-0);
line-height: var(--font-lineheight-3);
}
&:has(.ui-text-field.ui-row) {
row-gap: var(--size-7);
}
/* Disabled */
&[disabled] {
opacity: 0.64;
user-select: none;
input,
label,
.ui-label {
cursor: not-allowed;
}
}
/* Error / validation */
&[data-invalid] {
:where(.ui-end-text) {
color: var(--critical);
}
:where(.ui-checkbox, .ui-radio, .ui-switch) {
--primary: var(--critical);
:where(.ui-end-text) {
color: var(--critical);
}
}
:where(.ui-switch) {
input {
border-radius: var(--radius-round);
outline: 2px solid var(--critical);
}
}
}
/* Required */
&:has(:invalid) {
:where(legend, .ui-legend) {
padding-inline-end: 1ex;
position: relative;
&::after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
}
}
/* Field group */
:where(.ui-field-group) {
display: flex;
flex-direction: column;
gap: var(--size-4);
/* If it only has checkboxes, radios, or switches */
&:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)):not(
:has(> :not(.ui-checkbox, .ui-radio, .ui-switch))
) {
gap: var(--size-2);
}
/* If it only has buttons */
&:has(.ui-button):not(:has(*:not(.ui-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 */
&.ui-row {
flex-direction: row;
flex-wrap: wrap;
&:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)) {
gap: var(--size-4);
}
}
}
}