GitHub

Checkbox

A custom box with checked, indeterminate, and disabled states, Space/Enter keyboard toggling, and an inlined Lucide check icon. Form-associated; emits a composed change event.

Stable wc import '@esa/ecology/esa-checkbox';

Preview

<esa-checkbox label="Subscribe to updates" checked></esa-checkbox>

States

<esa-checkbox label="Unchecked"></esa-checkbox>
<esa-checkbox label="Checked" checked></esa-checkbox>
<esa-checkbox label="Indeterminate" indeterminate></esa-checkbox>
<esa-checkbox label="Disabled" disabled></esa-checkbox>
<esa-checkbox label="Disabled + checked" disabled checked></esa-checkbox>

Sizes

<esa-checkbox label="Extra small" size="xs" checked></esa-checkbox>
<esa-checkbox label="Small" size="sm" checked></esa-checkbox>
<esa-checkbox label="Medium" size="md" checked></esa-checkbox>
<esa-checkbox label="Large" size="lg" checked></esa-checkbox>

No label

Omit label for a bare box — supply an aria-label for an accessible name.

<esa-checkbox checked aria-label="Select row"></esa-checkbox>

Form participation

Checked boxes submit on under their name; unchecked boxes submit nothing.

<form>
  <esa-checkbox label="Subscribe" name="subscribe" checked></esa-checkbox>
  <button type="submit">Submit</button>
</form>

API

PropTypeDefaultDescription
label string '' Text rendered beside the box.
checked boolean false Whether the box is checked.
indeterminate boolean false Renders the mixed/partial state with a dash.
size 'xs' | 'sm' | 'md' | 'lg' 'md' Box and label size.
disabled boolean false Disables interaction.

Events

EventTypeDefaultDescription
change CustomEvent<{ checked: boolean }> Fired when toggled. Composed and bubbling; detail.checked is the new state.

Accessibility

  • The box has role="checkbox" with aria-checked reflecting checked or mixed (indeterminate).
  • Focusable via tabindex and toggles on Space or Enter.
  • aria-disabled and a -1 tabindex are applied when disabled.
  • Without a label, provide an aria-label for an accessible name.

Theming surface

Every public token this component reads, extracted from its source at build time. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

TokenTierResolves to
--focus-ring-color component #65ba74 via --color-border-focus--color-grass-8
--focus-ring-width component 2px
--form-bg component #fcfcfc via --color-surface--color-gray-1
--form-border-color component #cecece via --color-border--color-gray-7
--form-border-color-focus component #65ba74 via --color-border-focus--color-grass-8
--form-border-width component 1px
--form-font-size-lg component clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) via --type-size-300
--form-font-size-md component clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --type-size-200
--form-font-size-sm component clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --type-size-100
--form-font-size-xs component clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) via --type-size-050
--form-radius-lg component 0.5rem via --radius-200
--form-radius-md component 0.5rem via --radius-200
--form-radius-sm component 0.25rem via --radius-100
--form-radius-xs component 0.25rem via --radius-100
--color-primary semantic #46a758 via --color-grass-9
--color-text-inverse semantic #fcfcfc via --color-gray-1
--color-text-primary semantic #202020 via --color-gray-12
--font-sans semantic 'DM Sans', sans-serif
--transition-fast semantic 150ms ease
--spacing-200 primitive 0.5rem