Skip to content

Checkbox

A native-feeling checkbox built on a role="checkbox" element. It generates a hidden native <input type="checkbox"> for form submission, supports indeterminate state, and emits change events on user interaction.

import { Checkbox } from "@areia/slots";

document.querySelector("#root")!.innerHTML = `
  <label>
    <span
      data-slot="checkbox"
      class="win95-check mr-2 data-[checked]:bg-blue-900"
    >
      <span data-slot="checkbox-indicator" class="win95-check-mark"></span>
    </span>
    Accept terms and conditions
  </label>
`;

const root = document.querySelector('[data-slot="checkbox"]')!;
const controller = Checkbox.createCheckbox(root, {
  defaultChecked: false,
  name: "terms",
});

Import

import { Checkbox } from "@areia/slots";

Usage

import { Checkbox } from "@areia/slots";

// Auto-bind a single root
const root = document.querySelector('[data-slot="checkbox"]');
const checkbox = Checkbox.createCheckbox(root, {
  name: "accept",
  value: "yes",
});

// Listen for state changes
root.addEventListener("checkbox:change", (e) => {
  console.log(e.detail.checked);
});

// Auto-bind all unbound roots in scope
const controllers = Checkbox.create();

Expected Markup

The root element receives role="checkbox" and keyboard handling. A hidden native <input type="checkbox"> is generated and inserted after the root for form participation. The optional checkbox-indicator slot is shown or hidden based on checked/indeterminate state.

<label>
  <span data-slot="checkbox">
    <span data-slot="checkbox-indicator">
      <!-- Icon or custom indicator markup -->
    </span>
  </span>
  Accept terms and conditions
</label>

Wrapping the checkbox in a <label> is recommended — the controller detects wrapping labels and label[for] associations so that clicking the label text toggles the checkbox.

Minimal Markup (no indicator)

<span data-slot="checkbox"></span>

Generated Hidden Input

The controller creates a visually hidden native checkbox and inserts it immediately after the root element:

<input
  type="checkbox"
  tabindex="-1"
  aria-hidden="true"
  data-checkbox-generated="input"
  style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;pointer-events:none"
/>

When uncheckedValue is configured, a second hidden input is generated to submit a value even when the checkbox is unchecked.

Data Slots

SlotRequiredDescription
checkboxYesRoot element. Receives role="checkbox" and ARIA attrs.
checkbox-indicatorNoVisual indicator shown when checked or indeterminate. Hidden otherwise unless data-keep-mounted is set.

Generated Attributes

AttributeElement(s)Description
data-checkedRoot, indicatorPresent when checked.
data-uncheckedRoot, indicatorPresent when unchecked and not indeterminate.
data-indeterminateRoot, indicatorPresent when in mixed state.
data-disabledRoot, indicatorPresent when disabled.
data-readonlyRoot, indicatorPresent when read-only.
data-requiredRoot, indicatorPresent when required.
roleRootSet to "checkbox".
aria-checkedRoot"true", "false", or "mixed".
aria-disabledRoot"true" or removed.
aria-readonlyRoot"true" or removed.
aria-requiredRoot"true" or removed.
aria-labelledbyRootMerged from wrapping <label> and [for] associations.

Events

Outbound

EventDetailDescription
checkbox:change{ checked: boolean }Fired on the root when the checked state changes via user interaction or the controller.

Inbound

EventDetailDescription
checkbox:setboolean or { checked: boolean } or { checked: boolean; indeterminate?: boolean }Dispatch on the root to set the checked (and optionally indeterminate) state. Supports flat boolean shorthand.

API Reference

Options

OptionTypeDefaultDescription
defaultCheckedbooleanfalseInitial checked state.
indeterminatebooleanfalseInitial mixed state. Cleared when the user toggles the checkbox.
disabledbooleanfalseDisable user interaction and exclude from form submission.
readOnlybooleanfalsePrevent user interaction while keeping the value submittable.
requiredbooleanfalseRequire a checked value for native form validation.
namestringForm field name.
formstringForm owner ID for generated inputs.
valuestringSubmitted value when checked. Defaults to the native checkbox "on".
uncheckedValuestringSubmitted value when unchecked. Creates an additional hidden input.
onCheckedChange(checked: boolean) => voidCalled when the checked state changes.

Controller

MemberTypeDescription
checkedboolean (getter)Current checked state.
indeterminateboolean (getter)Current indeterminate state.
toggle()() => voidToggle the checked state and clear indeterminate.
check()() => voidSet checked to true and clear indeterminate.
uncheck()() => voidSet checked to false and clear indeterminate.
setChecked(checked, ind?)(checked: boolean, indeterminate?: boolean) => voidSet checked and optionally indeterminate state.
setIndeterminate(ind)(indeterminate: boolean) => voidSet indeterminate without changing checked.
destroy()() => voidRemove listeners and generated inputs.

Controller

The controller is returned by Checkbox.createCheckbox() and provides imperative control over the checkbox state.

const checkbox = Checkbox.createCheckbox(root, {
  name: "terms",
});

// Toggle
checkbox.toggle();

// Force check
checkbox.check();

// Force uncheck
checkbox.uncheck();

// Set checked and indeterminate together
checkbox.setChecked(true, true); // indeterminate

// Set only indeterminate
checkbox.setIndeterminate(true);

// Read state
console.log(checkbox.checked); // true
console.log(checkbox.indeterminate); // true

// Clean up
checkbox.destroy();

Form Integration

The generated hidden input participates in standard form submission and FormData. When uncheckedValue is set, an extra hidden input ensures a value is submitted even when the checkbox is unchecked. The controller also listens for the form's reset event and synchronizes state accordingly.

<form>
  <span
    data-slot="checkbox"
    data-name="accept"
    data-value="yes"
    data-unchecked-value="no"
  >
    <span data-slot="checkbox-indicator"></span>
  </span>
  Accept terms
</form>