Checkbox Group
v0.0.10
mix mishka.ui.gen.headless checkbox_group
Overview of the Phoenix and LiveView Headless Checkbox Group Component
The Mishka Chelekom Headless Checkbox Group ships zero opinionated styling - only the semantic markup, full WAI-ARIA wiring, and paired-presence state (data-checked/data-unchecked/data-indeterminate). Each item is a role="checkbox" button with a hidden native checkbox for form submission as name[] (rendered when the group has a name), and an optional select_all slot renders a tristate parent that reflects and toggles every child. Every part - label, item, indicator and input - is addressable through a *_class attribute, so you style it entirely with your own Tailwind classes while the accessibility and behavior keep working underneath.
The group takes class for the root and label_class for the label. item_class, indicator_class and input_class on the group reach every item, indicator box and hidden input, the select_all parent included. An item or select_all slot takes its own item_class and indicator_class on top, for one row only. The group's classes come first, then the slot's.
Base UI Checkbox Group example (Phoenix headless)
Base UI Checkbox Group example (Phoenix headless)
<.checkbox_group
id="checkbox-group-hero"
class="flex flex-col items-start gap-1 text-neutral-950 dark:text-white"
label_class="text-sm font-bold"
item_class="group flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white"
indicator_class="group/box flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-[checked]:bg-neutral-950 data-[checked]:text-white dark:data-[checked]:bg-white dark:data-[checked]:text-neutral-950 group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-neutral-950 dark:group-focus-visible:outline-white"
>
<:indicator_icon>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
class="block group-data-[unchecked]/box:hidden"
style="display: block"
>
<path d="m2.5 8.5 4 4 7-9" />
</svg>
</:indicator_icon>
<:label>Apples</:label>
<:item value="fuji-apple" checked>Fuji</:item>
<:item value="gala-apple">Gala</:item>
<:item value="granny-smith-apple">Granny Smith</:item>
</.checkbox_group>Checkbox Group example - select all (Phoenix headless)
The select_all slot adds a parent row that checks or clears every enabled item. It starts out in the right state: one of the two enabled apples is checked, so the parent is drawn mixed (data-indeterminate, aria-checked="mixed"), and the root's indicator_class greys the box and swaps the check for a minus with data-[indeterminate]. The disabled item is skipped by the parent. With name="fruit" each checked item submits as fruit[]; the select_all parent submits nothing.
Checkbox Group example - select all (Phoenix headless)
<.checkbox_group
id="select-all-group"
name="fruit"
class="flex flex-col items-start gap-1 text-neutral-950 dark:text-white"
label_class="text-sm font-bold"
item_class="group flex items-center gap-2 text-sm text-neutral-950 dark:text-white"
indicator_class="group/box flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-[checked]:bg-neutral-950 data-[checked]:text-white dark:data-[checked]:bg-white dark:data-[checked]:text-neutral-950 data-[indeterminate]:border-neutral-500 data-[indeterminate]:bg-neutral-500 data-[indeterminate]:text-white dark:data-[indeterminate]:border-neutral-400 dark:data-[indeterminate]:bg-neutral-400 dark:data-[indeterminate]:text-neutral-950 group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-neutral-950 dark:group-focus-visible:outline-white"
>
<:indicator_icon>
<.icon
name="hero-check"
class="block size-4 group-data-[unchecked]/box:hidden group-data-[indeterminate]/box:hidden"
/>
<.icon name="hero-minus" class="hidden size-4 group-data-[indeterminate]/box:block" />
</:indicator_icon>
<:label>Apples</:label>
<:select_all item_class="font-semibold">All apples</:select_all>
<:item value="fuji-apple" checked>Fuji</:item>
<:item value="gala-apple">Gala</:item>
<:item value="granny-smith-apple" disabled>Granny Smith (sold out)</:item>
</.checkbox_group>Parts & data attributes
Each part carries a stable data-part and a structural chelekom-checkbox_group__* class you can target, plus paired-presence state for checked/unchecked/indeterminate transitions.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Root | - | role="group", aria-labelledby, data-disabled | The wrapping group container. Takes class. |
| Label | label | - | Visible group label wired to aria-labelledby. Takes label_class. |
| Item | item | role="checkbox", aria-checked, data-checked, data-unchecked, data-indeterminate, data-disabled, data-parent | A single checkbox option; a select_all item carries data-parent (the tristate "select all"). Takes item_class on the group and on an item or select_all slot. |
| Indicator | indicator | aria-hidden, data-checked, data-unchecked, data-indeterminate | The visual box that renders the indicator icon. Takes indicator_class on the group and on an item or select_all slot. |
| Input | input | - | Hidden native checkbox, rendered for the items when the group has a name; submits as name[]. Takes input_class. |
Accessibility
Follows the WAI-ARIA Checkbox pattern: pressing Space or Enter toggles the focused checkbox, Tab moves between checkboxes, and toggling the select_all parent checks or unchecks every item. The parent reports its tristate state via aria-checked="mixed" when only some items are checked.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (anchors the group label + item ids) |
name
|
string
| — | — | No | Base form name; items submit as `name[]` |
disabled
|
boolean
|
false
| — | No | Disable the whole group (cascades to every item) |
on_change
|
string
| — | — | No | LiveView event pushed with the selected values ({value: [...]}) |
class
|
any
| — | — | No | Extra classes for the root |
input_class
|
any
| — | — | No | — |
item_class
|
any
| — | — | No | — |
indicator_class
|
any
| — | — | No | — |
label_class
|
any
| — | — | No | Extra classes for the group label |
rest
|
global
| — | — | No | — |
Slots
| Name | Required | Description |
|---|---|---|
indicator_icon
| No | Shared icon rendered inside every item's indicator box (e.g. a check) |
label
| No | Accessible group label (wired to aria-labelledby) |
select_all
| No | Optional tristate "select all" parent (reflects + toggles all items) |
item
| Yes | A checkbox option |
Attributes of the select_all slot
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
item_class
|
any
| — | — | No | Extra classes for the parent's item button (label row) |
indicator_class
|
any
| — | — | No | Extra classes for the parent's indicator box |
Attributes of the item slot
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
value
|
string
| — | — | Yes | — |
checked
|
boolean
| — | — | No | — |
disabled
|
boolean
| — | — | No | — |
item_class
|
any
| — | — | No | Extra classes for this item's button (label row) |
indicator_class
|
any
| — | — | No | Extra classes for this item's indicator box |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
checkbox_group
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless checkbox_group