Checkbox Group

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless checkbox_group
Source View Source

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)

Apples
heex
<.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)

Apples
heex
<.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.

Partdata-partState / ARIAPurpose
Root-role="group", aria-labelledby, data-disabledThe wrapping group container. Takes class.
Labellabel-Visible group label wired to aria-labelledby. Takes label_class.
Itemitemrole="checkbox", aria-checked, data-checked, data-unchecked, data-indeterminate, data-disabled, data-parentA 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.
Indicatorindicatoraria-hidden, data-checked, data-unchecked, data-indeterminateThe visual box that renders the indicator icon. Takes indicator_class on the group and on an item or select_all slot.
Inputinput-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.