HyCheckbox
Usage examples on this page are written for Lit / plain HTML (
<hy-checkbox>). The same component ships asHyCheckboxin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A form-associated tri-state checkbox component that supports unchecked, checked, and indeterminate states. Provides comprehensive form integration via the ElementInternals API, accessibility features, and customizable styling.
Key Behavioral Model
This checkbox implements a clear separation between user interactions and programmatic control:
Limited to 2 states: Only toggle between
unchecked↔checkedNever access indeterminate: Indeterminate state is invisible to users
Standard behavior: Follows native HTML checkbox conventions
Predictable UX: Users get consistent toggle behavior regardless of settings
Full 3-state access: Can set
unchecked,checked, orindeterminateMethod-controlled: Use
setState(),setIndeterminate(),toggle(), etc.Indeterminate is a display state: only code sets it (a "select all" box whose children are partly checked);
toggle()and a click go checked ↔ unchecked, and an indeterminate box toggles to checkedEvent tracking: All changes include
reasonfield ('user' | 'programmatic')
Form Integration
This component implements the Form-Associated Custom Elements API, allowing it to participate in form submission just like native checkboxes. The checkbox's value will be included in FormData when checked and the form is submitted. Indeterminate state is treated as unchecked for form purposes.
Keyboard Navigation
- Space key: Toggles between
unchecked↔checked(standard behavior) - Tab: Moves focus to/from the checkbox
- Enter: No effect (standard checkbox behavior - doesn't change state)
- Indeterminate access: Only via programmatic methods like
setState('indeterminate', 'programmatic')
State Management Philosophy
The component treats indeterminate as a display-only state for users:
- Users see it visually but can't directly set it
- User interactions "exit" indeterminate by going to checked first
- Applications control indeterminate programmatically for complex UI patterns
- Clear event tracking helps distinguish user intent from application logic
When to use
- One option that is on or off and saved with a form: accept, subscribe, remember me.
- Selecting items in a list or a table, with a mixed state for a partial selection.
Do
- Word the label as the on state: "Send me updates", not "Don't send me updates".
When not to use
- Exactly one option from a visible set:
hy-radio-group. - A setting that applies the moment it changes, with no save step:
hy-switch. - An on/off tool in a toolbar (bold, mute, snap to grid):
hy-toggle. - Several picks from dozens of options:
hy-multi-combobox. - Several checkboxes answering one question: put them in an
hy-checkbox-group, which gives the set one name, one message line and onerequired.
Accessibility: your part
- Name every checkbox:
label, oraria-label/aria-labelledbyon the host when a table header or nearby text already names it.
Examples
Simple Two-State Preferences
<form>
<hy-checkbox name="notifications" value="enabled" label="Enable notifications"> </hy-checkbox>
<button type="submit">Save</button>
</form>Form Validation with Required Fields
<form>
<hy-checkbox
name="terms"
value="accepted"
label="I accept the terms"
required
error-message="Accept the terms to continue"
>
</hy-checkbox>
<button type="submit">Submit</button>
</form>Helper text, a warning and an error
<hy-checkbox label="Share usage data" helper-text="Anonymous, once a week"></hy-checkbox>
<hy-checkbox
label="Auto-delete old files"
warning-message="Deleted files cannot be recovered"
></hy-checkbox>
<hy-checkbox label="I accept the terms" error-message="Accept the terms to continue"></hy-checkbox>Value states
One line shows under the checkbox: the error message, else the warning message, else the helper text. error paints the danger color and sets aria-invalid; warning paints the warning color and never does. Neither touches form validity — that comes from required. Both message lines start with an icon, so the two differ by shape as well as color.
API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
state | state | 'unchecked' | 'checked' | 'indeterminate' | 'unchecked' | — |
label | label | string | '' | — |
helperText | helper-text | string | '' | Helper text shown under the checkbox. An error or warning message replaces it. |
topLabel | top-label | string | '' | — |
disabled | disabled | boolean | false | — |
required | required | boolean | false | — |
name | name | string | '' | — |
value | value | string | '' | — |
error | error | boolean | false | Error state: the value is not accepted. Paints the danger color and sets aria-invalid="true"; it never blocks submission (validity comes from required). Set on its own it colors the checkbox and keeps the helper text — for a message shown elsewhere. Setting error-message raises it. |
errorMessage | error-message | string | '' | Error message shown under the checkbox (replaces the helper text). Setting it raises error, clearing it lowers it. |
warning | warning | boolean | false | Warning state: the value is accepted but worth a second look. Paints the warning color and shows warning-message; never sets aria-invalid and never blocks submission. error wins when both are set. |
warningMessage | warning-message | string | '' | Warning message shown under the checkbox (replaces the helper text). Setting it raises warning. |
size | size | 'small' | 'medium' | 'large' | 'medium' | — |
ariaDescribedby | aria-describedby | string | '' | — |
checkboxId | checkbox-id | string | '' | Id for the checkbox input element; empty uses the fixed input (components.md "Ids"). |
helperTextId | helper-text-id | string | '' | Id for the helper text element; empty uses the fixed helper. |
topLabelId | top-label-id | string | '' | Id for the top label element; empty uses the fixed top-label. |
autofocus | autofocus | boolean | false | Marks this checkbox as the auto-focus target. Read by hy-dialog (and other overlay containers) on show to choose initial focus. Also focuses on first connect when used outside an overlay, mirroring native HTML autofocus but routed through JS so SSR'd HTML doesn't pull focus during hydration. |
checked | checked | boolean | — | — |
indeterminate | indeterminate | boolean | — | — |
form | — | HTMLFormElement | null | — | — |
validity | — | ValidityState | — | — |
validationMessage | — | string | — | — |
Events
| Event | Detail | Description |
|---|---|---|
change | — | Fired when state changes. Detail: { state: CheckboxState, checked: boolean, indeterminate: boolean, value: string, reason: StateChangeReason } |
input | — | Standard form input event |
CSS Parts
| Part | Description |
|---|---|
base | The checkbox's base wrapper element |
helper-text | The helper text element |
error-message | The error message element |
warning-message | The warning message element |
message-icon | The icon starting the error or warning message |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-checkbox-text-transform | none | Text transform for checkbox label |
--hy-checkbox-label-md-font-family | var(--hy-label-md-font-family) | Font family of the top label (the checkbox label reads the body role, the hint the caption role) |
--hy-checkbox-border-default-rest | var(--hy-border-default-rest) | Border of the unchecked box. (rest stays own-role; checked escalates to the indicator fill) |
--hy-checkbox-background-brand-on-rest | var(--hy-background-brand-on-rest) | Fill of the checked/indeterminate box — the on token (C6) |
--hy-checkbox-background-danger-on-rest | var(--hy-background-danger-on-rest) | Fill of the checked/indeterminate box in error state |
--hy-checkbox-background-warning-on-rest | var(--hy-background-warning-on-rest) | Fill of the checked/indeterminate box in warning state |
--hy-checkbox-foreground-brand-on-rest | var(--hy-foreground-brand-on-rest) | Check icon on the checked/indeterminate box |
--hy-checkbox-border-danger-rest | var(--hy-border-danger-rest) | Border of the unchecked box in error state. |
--hy-checkbox-border-warning-rest | var(--hy-border-warning-rest) | Border of the unchecked box in warning state. |
--hy-checkbox-foreground-danger-rest | var(--hy-foreground-danger-rest) | Error message text and icon color. |
--hy-checkbox-foreground-warning-rest | var(--hy-foreground-warning-rest) | Warning message text and icon color. |
--hy-checkbox-foreground-danger-on-rest | var(--hy-foreground-danger-on-rest) | Check icon on the error-state box |
--hy-checkbox-foreground-warning-on-rest | var(--hy-foreground-warning-on-rest) | Check icon on the warning-state box |
--hy-checkbox-visual-background-neutral-disabled | var(--hy-background-neutral-disabled) | Fill of the disabled box, checked or not |
--hy-checkbox-gap-layout-2xs | var(--hy-gap-layout-2xs) | Gap between the message icon and its text. |
--hy-checkbox-container-indicator-size-sm | var(--hy-indicator-size-sm) | Row floor (min-height) for the small size variant. The row is sized to its content: by default the floor is the size's indicator-size rung, so stacked checkboxes and their top-label / helper text stay tight. The indicator is centered in the row. To line a checkbox up with an input in a form row, set this to a control height (e.g. var(--hy-control-height-sm)) per instance or per design system. Coarse pointers keep a touch-target floor. |
--hy-checkbox-container-indicator-size-md | var(--hy-indicator-size-md) | Row floor (min-height) for the medium size variant. |
--hy-checkbox-container-indicator-size-lg | var(--hy-indicator-size-lg) | Row floor (min-height) for the large size variant. |
--hy-checkbox-gap-form-sm | var(--hy-gap-form-sm) | The only spacing between the top-label, the row and the helper text or message. |
--hy-checkbox-motion-transition-hover-duration | var(--hy-motion-transition-hover-duration) | Transition duration for hover and focus state feedback |
--hy-checkbox-motion-transition-hover-easing | var(--hy-motion-transition-hover-easing) | Transition easing for hover and focus state feedback |
--hy-checkbox-icon-inset | — | How much smaller than the box the check / dash glyph is drawn (a length, 2px unless set) |
Methods
toggle()
Checked ↔ unchecked, as a click does; an indeterminate box becomes checked.
setIndeterminate()
Shows the dash — for a parent box whose children are partly checked.