cv-checkbox
Two-state or three-state (indeterminate) toggle control with a visual indicator.
Headless: createCheckbox
Cross-Spec Consistency
This document is the UIKit surface contract for Checkbox.
- The canonical state model, invariants, and user-driven transitions are defined by the headless spec.
- Any intentional divergence between UIKit and headless MUST be explicitly documented in both specs to prevent drift.
Usage
Anatomy
<cv-checkbox> (host)
└── <div part="base" role="checkbox">
├── <span part="indicator">
│ └── <span part="checkmark">
└── <slot> ← labelAttributes
| Attribute | Type | Default | Description |
|---|---|---|---|
name | String | "" | Form field name |
value | String | "on" | Submitted form value when checked |
checked | Boolean | false | Checked state |
indeterminate | Boolean | false | Indeterminate state (takes precedence over checked visually) |
disabled | Boolean | false | Prevents interaction |
read-only | Boolean | false | Visible but not toggleable |
required | Boolean | false | Requires checked state for form validity |
aria-label | String | "" | Accessible label forwarded to the interactive checkbox element |
aria-labelledby | String | "" | Label reference forwarded to the interactive checkbox element |
aria-describedby | String | "" | Description reference forwarded to the interactive checkbox element |
tabindex | String | 0 | Tab order hint forwarded to the interactive checkbox element |
Slots
| Slot | Description |
|---|---|
(default) | Label text or content next to the indicator |
CSS Parts
| Part | Element | Description |
|---|---|---|
base | <div> | Root interactive element with role="checkbox" |
indicator | <span> | Box that contains the checkmark |
checkmark | <span> | Visual mark inside the indicator (square when checked, line when indeterminate) |
CSS Custom Properties
No component-specific custom properties. Styling uses design tokens:
--cv-space-2— gap between indicator and label--cv-radius-sm— indicator border radius--cv-color-border— indicator border color--cv-color-surface— indicator background--cv-color-primary— checked/indeterminate accent color--cv-color-text— label text color--cv-duration-fast— transition duration--cv-easing-standard— transition easing
Visual States
| Host selector | Description |
|---|---|
:host([checked]) | Primary-tinted indicator border and background, solid checkmark |
:host([indeterminate]) | Horizontal line checkmark (2px height, full width) |
:host([disabled]) | Reduced opacity (0.55), cursor: not-allowed |
:host([read-only]) | Non-toggleable state with normal visibility |
:host([required]) | Required semantic state with aria-required="true" |
:host(:focus-visible) | Focus ring on the interactive checkbox element |
ARIA
- When
checked=trueandindeterminate=false,aria-checked="true". - When
checked=falseandindeterminate=true,aria-checked="mixed". - When
checked=falseandindeterminate=false,aria-checked="false".
State Invariants and Transitions
- Canonical conceptual states are exactly:
unchecked,checked,indeterminate. - If represented as booleans,
indeterminate=trueimplieschecked=false. - User toggle transition:
indeterminate->checked. - Disabled or read-only checkboxes do not respond to toggle actions.
Form Behavior
cv-checkboxis form-associated.- The control contributes
name=valueonly whenchecked=trueandindeterminate=false. - If
valueis empty, the submitted value falls back to"on". indeterminate=trueis treated as unchecked for form submission and validity.requiredis valid only when the checkbox is checked and not indeterminate.- Form reset restores the initially captured
checked/indeterminatestate.
Events
| Event | Detail | Description |
|---|---|---|
cv-input | { value: boolean | "mixed", checked: boolean, indeterminate: boolean } | Fires on toggle |
cv-change | { value: boolean | "mixed", checked: boolean, indeterminate: boolean } | Fires when state commits |
Migration Notes (Non-normative)
This section documents known terminology/payload changes and the breaking-change communication policy.
Terminology change: mixed -> indeterminate
indeterminateis the canonical third-state term.mixedremains an ARIA token only (used exclusively inaria-checked="mixed").
Payload compatibility: legacy value plus canonical booleans
- Runtime events expose
{ value: boolean | "mixed", checked: boolean, indeterminate: boolean }. checkedandindeterminateare the canonical fields for new consumers.valueis a compatibility state token, not the string form value from the hostvalueproperty.
Mappings:
value === "mixed"->indeterminate=trueandchecked=false.value === true->checked=trueandindeterminate=false.value === false->checked=falseandindeterminate=false.
Breaking-change communication policy
Any future breaking change for consumers that rely on legacy terminology (mixed) or compatibility event fields must be called out here.
When this contract changes in a breaking way, this section MUST explicitly document:
- terminology changes (old term -> new term)
- payload shape changes (old shape -> new shape)
- a short statement that the change is breaking and requires consumer migration
Parity matrix (Headless vs UIKit)
This matrix is intentionally short and exists to prevent drift between headless-ui/specs/components/checkbox.md and uikit/specs/components/checkbox.md.
| Surface | Headless | UIKit |
|---|---|---|
| Canonical third-state term | indeterminate | indeterminate attribute + event detail |
| ARIA token for third state | aria-checked="mixed" only | aria-checked="mixed" only |
| State representation | checked:boolean, indeterminate:boolean | checked/indeterminate attributes |
| User toggle transition | indeterminate -> checked | indeterminate -> checked |
| Disabled/read-only semantics | cannot toggle | cannot toggle |
| Payload on user interaction | N/A (actions/state API) | { value, checked, indeterminate } |
| Form primitives | specified (see headless spec) | form-associated via name, value, required |