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>