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.
<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<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
>
</template>
<!--[--><label class="ui-checkbox"
><input type="checkbox" name="checkbox" /><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" disabled name="checkbox" /><span class="ui-sr-only"
><!--[-->Disabled<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" disabled name="checkbox" /><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.

<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<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
>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>
</template>
<!--[--><label class="ui-checkbox"
><input type="checkbox" name="checkbox" /><span class="ui-label"
><!--[-->Choice A<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" disabled name="checkbox" /><span class="ui-label"
><!--[-->Disabled<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" disabled name="checkbox" /><span class="ui-label"
><!--[-->Checked and disabled<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" name="checkbox" /><span class="ui-label"
><!--[--><span
>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

<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<Checkbox name="checkbox">Default</Checkbox>
<Checkbox stack name="checkbox">Stack</Checkbox>
</template>
<!--[--><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

<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<Checkbox name="checkbox">
Default
<template #end-text>Supporting text</template>
</Checkbox>
<Checkbox stack name="checkbox">
Stack
<template #end-text>Supporting text</template>
</Checkbox>
</template>
<!--[--><label class="ui-checkbox"
><input type="checkbox" name="checkbox" aria-describedby="s3-0" /><span
class="ui-label"
><!--[-->
Default
<!--]--></span
><span id="s3-0" class="ui-end-text"
><!--[-->Supporting text<!--]--></span
></label
><label class="ui-checkbox ui-stack"
><input type="checkbox" name="checkbox" aria-describedby="s3-1" /><span
class="ui-label"
><!--[-->
Stack
<!--]--></span
><span id="s3-1" class="ui-end-text"
><!--[-->Supporting text<!--]--></span
></label
><!--]-->

Validation

  • Add the required attribute on the component. It is forwarded to the underlying <input>.
  • 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.
<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<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
<template #end-text>Check yourself</template>
</Checkbox>
<Checkbox stack error name="checkbox">
Stack
<template #end-text>Before you wreck yourself</template>
</Checkbox>
</div>
</template>
<!--[-->
<div class="example-row ui-spacious">
<label class="ui-checkbox"
><input type="checkbox" required name="checkbox" /><span class="ui-label"
><!--[-->Default<!--]--></span
><!----></label
><label class="ui-checkbox ui-stack"
><input type="checkbox" required name="checkbox" /><span class="ui-label"
><!--[-->Stack<!--]--></span
><!----></label
>
</div>
<div class="example-row ui-spacious">
<label class="ui-checkbox" data-invalid="true"
><input type="checkbox" name="checkbox" aria-describedby="s4-2" /><span
class="ui-label"
><!--[-->
Default
<!--]--></span
><span id="s4-2" class="ui-end-text"
><!--[-->Check yourself<!--]--></span
></label
><label class="ui-checkbox ui-stack" data-invalid="true"
><input type="checkbox" name="checkbox" aria-describedby="s4-3" /><span
class="ui-label"
><!--[-->
Stack
<!--]--></span
><span id="s4-3" 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.

<script setup lang="ts">
import { computed, ref } from "vue"
import { Checkbox, FieldGroup, FieldLegend, FieldSet } from "opui-css/vue"
const items = ["Apples", "Bananas", "Cherries"]
const checked = ref([true, false, false])
const allChecked = computed(() => checked.value.every(Boolean))
const someChecked = computed(() => checked.value.some(Boolean))
const indeterminate = computed(() => someChecked.value && !allChecked.value)
function toggleAll() {
const next = !allChecked.value
checked.value = checked.value.map(() => next)
}
</script>
<template>
<FieldSet>
<FieldLegend>
<Checkbox
:model-value="allChecked"
:indeterminate="indeterminate"
@update:model-value="toggleAll"
>Select all</Checkbox
>
</FieldLegend>
<FieldGroup name="indeterminate-children">
<Checkbox
v-for="(item, index) in items"
:key="item"
v-model="checked[index]"
>{{ item }}</Checkbox
>
</FieldGroup>
</FieldSet>
</template>
<fieldset class="ui-fieldset">
<!--[-->
<legend class="">
<!--[--><label class="ui-checkbox"
><input type="checkbox" data-indeterminate="true" /><span class="ui-label"
><!--[-->Select all<!--]--></span
><!----></label
><!--]-->
</legend>
<div class="ui-field-group" role="group">
<!--[--><!--[--><label class="ui-checkbox"
><input type="checkbox" checked /><span class="ui-label"
><!--[-->Apples<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Bananas<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><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.

<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<Checkbox spread>
Accept Terms & Conditions
<template #end-text>I have read and agree to the privacy policy.</template>
</Checkbox>
<Checkbox spread required>
Required
<template #end-text>You must accept this to continue.</template>
</Checkbox>
<Checkbox spread disabled>
Disabled
<template #end-text>This checkbox is disabled.</template>
</Checkbox>
<Checkbox spread error>
Invalid Checkbox
<template #end-text>There is an error with this checkbox.</template>
</Checkbox>
</template>
<!--[--><label class="ui-checkbox ui-spread"
><input type="checkbox" aria-describedby="s6-0" /><span class="ui-label"
><!--[-->
Accept Terms & Conditions
<!--]--></span
><span id="s6-0" class="ui-end-text"
><!--[-->I have read and agree to the privacy policy.<!--]--></span
></label
><label class="ui-checkbox ui-spread"
><input type="checkbox" required aria-describedby="s6-1" /><span
class="ui-label"
><!--[-->
Required
<!--]--></span
><span id="s6-1" class="ui-end-text"
><!--[-->You must accept this to continue.<!--]--></span
></label
><label class="ui-checkbox ui-spread"
><input type="checkbox" disabled aria-describedby="s6-2" /><span
class="ui-label"
><!--[-->
Disabled
<!--]--></span
><span id="s6-2" class="ui-end-text"
><!--[-->This checkbox is disabled.<!--]--></span
></label
><label class="ui-checkbox ui-spread" data-invalid="true"
><input type="checkbox" aria-describedby="s6-3" /><span class="ui-label"
><!--[-->
Invalid Checkbox
<!--]--></span
><span id="s6-3" class="ui-end-text"
><!--[-->There is an error with this checkbox.<!--]--></span
></label
><!--]-->

Sizes

<script setup lang="ts">
import { Checkbox } from "opui-css/vue"
</script>
<template>
<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>
</template>
<!--[-->
<div class="example-row">
<label class="ui-checkbox ui-small"
><input type="checkbox" name="checkbox" /><span class="ui-sr-only"
><!--[-->Label<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" name="checkbox" /><span class="ui-sr-only"
><!--[-->Label<!--]--></span
><!----></label
><label class="ui-checkbox ui-large"
><input type="checkbox" name="checkbox" /><span class="ui-sr-only"
><!--[-->Label<!--]--></span
><!----></label
>
</div>
<div class="example-row">
<label class="ui-checkbox ui-small"
><input type="checkbox" name="checkbox" /><span class="ui-label"
><!--[-->Small<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" name="checkbox" /><span class="ui-label"
><!--[-->Default<!--]--></span
><!----></label
><label class="ui-checkbox ui-large"
><input type="checkbox" name="checkbox" /><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
<script setup lang="ts">
import { Checkbox, FieldGroup, FieldLegend, FieldSet, Form } from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset">
<!--[-->
<legend class=""><!--[-->Legend<!--]--></legend>
<div class="ui-field-group" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 3<!--]--></span
><!----></label
><!--]-->
</div>
<!--]-->
</fieldset>
<!--]-->
</form>

Direction

Legend
<script setup lang="ts">
import { Checkbox, FieldGroup, FieldLegend, FieldSet, Form } from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset">
<!--[-->
<legend class=""><!--[-->Legend<!--]--></legend>
<div class="ui-field-group ui-row" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><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

<script setup lang="ts">
import {
Checkbox,
FieldDescription,
FieldGroup,
FieldLegend,
FieldSet,
Form,
} from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset">
<!--[-->
<legend class=""><!--[-->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" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 3<!--]--></span
><!----></label
><!--]-->
</div>
<!--]-->
</fieldset>
<fieldset class="ui-fieldset">
<!--[-->
<legend class=""><!--[-->Legend<!--]--></legend>
<div class="ui-field-group ui-row" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><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
<script setup lang="ts">
import { Checkbox, FieldGroup, FieldLegend, FieldSet, Form } from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset" disabled>
<!--[-->
<legend class=""><!--[-->Legend<!--]--></legend>
<div class="ui-field-group ui-row" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><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!
<script setup lang="ts">
import { Checkbox, FieldGroup, FieldLegend, FieldSet, Form } from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset">
<!--[-->
<legend class=""><!--[-->These are required!<!--]--></legend>
<div class="ui-field-group ui-row" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" required /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" required /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" 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!
<script setup lang="ts">
import { Checkbox, FieldGroup, FieldLegend, FieldSet, Form } from "opui-css/vue"
</script>
<template>
<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>
</template>
<form class="ui-form">
<!--[-->
<fieldset class="ui-fieldset" data-invalid>
<!--[-->
<legend class=""><!--[-->Legend<!--]--></legend>
<div class="ui-field-group ui-row" role="group">
<!--[--><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 1<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 2<!--]--></span
><!----></label
><label class="ui-checkbox"
><input type="checkbox" /><span class="ui-label"
><!--[-->Checkbox 3<!--]--></span
><!----></label
><!--]-->
</div>
<span class="ui-end-text">Something went wrong!</span
><!--]-->
</fieldset>
<!--]-->
</form>

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.
  1. Container
  2. Input
  3. Label (optional)
  4. End text (optional)

API

Checkbox API

Field group API

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