Skip to content

Checkbox

A control that allows the user to toggle between checked and not checked.

Updated View as Markdown

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

Slot Required Description
checkbox Yes Root element. Receives role="checkbox" and ARIA attrs.
checkbox-indicator No Visual indicator shown when checked or indeterminate. Hidden otherwise unless data-keep-mounted is set.

Generated Attributes

Attribute Element(s) Description
data-checked Root, indicator Present when checked.
data-unchecked Root, indicator Present when unchecked and not indeterminate.
data-indeterminate Root, indicator Present when in mixed state.
data-disabled Root, indicator Present when disabled.
data-readonly Root, indicator Present when read-only.
data-required Root, indicator Present when required.
role Root Set to "checkbox".
aria-checked Root "true", "false", or "mixed".
aria-disabled Root "true" or removed.
aria-readonly Root "true" or removed.
aria-required Root "true" or removed.
aria-labelledby Root Merged from wrapping <label> and [for] associations.

Events

Outbound

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

Inbound

Event Detail Description
checkbox:set boolean 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

Option Type Default Description
defaultChecked boolean false Initial checked state.
indeterminate boolean false Initial mixed state. Cleared when the user toggles the checkbox.
disabled boolean false Disable user interaction and exclude from form submission.
readOnly boolean false Prevent user interaction while keeping the value submittable.
required boolean false Require a checked value for native form validation.
name string Form field name.
form string Form owner ID for generated inputs.
value string Submitted value when checked. Defaults to the native checkbox "on".
uncheckedValue string Submitted value when unchecked. Creates an additional hidden input.
onCheckedChange (checked: boolean) => void Called when the checked state changes.

Controller

Member Type Description
checked boolean (getter) Current checked state.
indeterminate boolean (getter) Current indeterminate state.
toggle() () => void Toggle the checked state and clear indeterminate.
check() () => void Set checked to true and clear indeterminate.
uncheck() () => void Set checked to false and clear indeterminate.
setChecked(checked, ind?) (checked: boolean, indeterminate?: boolean) => void Set checked and optionally indeterminate state.
setIndeterminate(ind) (indeterminate: boolean) => void Set indeterminate without changing checked.
destroy() () => void Remove 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>
Navigation

Type to search…

↑↓ navigate↵ selectEsc close