Skip to content

HyCheckbox ​

Usage examples on this page are written for Lit / plain HTML (<hy-checkbox>). The same component ships as HyCheckbox in @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 ↔ checked

  • Never 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, or indeterminate

  • Method-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 checked

  • Event tracking: All changes include reason field ('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 one required.

Accessibility: your part ​

  • Name every checkbox: label, or aria-label / aria-labelledby on the host when a table header or nearby text already names it.

Examples ​

Simple Two-State Preferences ​

html
<form>
  <hy-checkbox name="notifications" value="enabled" label="Enable notifications"> </hy-checkbox>
  <button type="submit">Save</button>
</form>

Form Validation with Required Fields ​

html
<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 ​

html
<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 ​

PropertyAttributeTypeDefaultDescription
statestate'unchecked' | 'checked' | 'indeterminate''unchecked'—
labellabelstring''—
helperTexthelper-textstring''Helper text shown under the checkbox. An error or warning message replaces it.
topLabeltop-labelstring''—
disableddisabledbooleanfalse—
requiredrequiredbooleanfalse—
namenamestring''—
valuevaluestring''—
errorerrorbooleanfalseError 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.
errorMessageerror-messagestring''Error message shown under the checkbox (replaces the helper text). Setting it raises error, clearing it lowers it.
warningwarningbooleanfalseWarning 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.
warningMessagewarning-messagestring''Warning message shown under the checkbox (replaces the helper text). Setting it raises warning.
sizesize'small' | 'medium' | 'large''medium'—
ariaDescribedbyaria-describedbystring''—
checkboxIdcheckbox-idstring''Id for the checkbox input element; empty uses the fixed input (components.md "Ids").
helperTextIdhelper-text-idstring''Id for the helper text element; empty uses the fixed helper.
topLabelIdtop-label-idstring''Id for the top label element; empty uses the fixed top-label.
autofocusautofocusbooleanfalseMarks 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.
checkedcheckedboolean——
indeterminateindeterminateboolean——
form—HTMLFormElement | null——
validity—ValidityState——
validationMessage—string——

Events ​

EventDetailDescription
change—Fired when state changes. Detail: { state: CheckboxState, checked: boolean, indeterminate: boolean, value: string, reason: StateChangeReason }
input—Standard form input event

CSS Parts ​

PartDescription
baseThe checkbox's base wrapper element
helper-textThe helper text element
error-messageThe error message element
warning-messageThe warning message element
message-iconThe 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.

PropertyDefaultDescription
--hy-checkbox-text-transformnoneText transform for checkbox label
--hy-checkbox-label-md-font-familyvar(--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-restvar(--hy-border-default-rest)Border of the unchecked box. (rest stays own-role; checked escalates to the indicator fill)
--hy-checkbox-background-brand-on-restvar(--hy-background-brand-on-rest)Fill of the checked/indeterminate box — the on token (C6)
--hy-checkbox-background-danger-on-restvar(--hy-background-danger-on-rest)Fill of the checked/indeterminate box in error state
--hy-checkbox-background-warning-on-restvar(--hy-background-warning-on-rest)Fill of the checked/indeterminate box in warning state
--hy-checkbox-foreground-brand-on-restvar(--hy-foreground-brand-on-rest)Check icon on the checked/indeterminate box
--hy-checkbox-border-danger-restvar(--hy-border-danger-rest)Border of the unchecked box in error state.
--hy-checkbox-border-warning-restvar(--hy-border-warning-rest)Border of the unchecked box in warning state.
--hy-checkbox-foreground-danger-restvar(--hy-foreground-danger-rest)Error message text and icon color.
--hy-checkbox-foreground-warning-restvar(--hy-foreground-warning-rest)Warning message text and icon color.
--hy-checkbox-foreground-danger-on-restvar(--hy-foreground-danger-on-rest)Check icon on the error-state box
--hy-checkbox-foreground-warning-on-restvar(--hy-foreground-warning-on-rest)Check icon on the warning-state box
--hy-checkbox-visual-background-neutral-disabledvar(--hy-background-neutral-disabled)Fill of the disabled box, checked or not
--hy-checkbox-gap-layout-2xsvar(--hy-gap-layout-2xs)Gap between the message icon and its text.
--hy-checkbox-container-indicator-size-smvar(--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-mdvar(--hy-indicator-size-md)Row floor (min-height) for the medium size variant.
--hy-checkbox-container-indicator-size-lgvar(--hy-indicator-size-lg)Row floor (min-height) for the large size variant.
--hy-checkbox-gap-form-smvar(--hy-gap-form-sm)The only spacing between the top-label, the row and the helper text or message.
--hy-checkbox-motion-transition-hover-durationvar(--hy-motion-transition-hover-duration)Transition duration for hover and focus state feedback
--hy-checkbox-motion-transition-hover-easingvar(--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.

Built with Lit. Documented with VitePress.