Toggle Group
v0.0.10
mix mishka.ui.gen.headless toggle_group
Overview of the Phoenix and LiveView Headless Toggle Group Component
The Mishka Chelekom Headless Toggle Group is a row of toggle buttons for Phoenix LiveView with no styling of its own. It renders a role="group" container of buttons with the WAI-ARIA wiring and a roving tabindex: arrow keys move focus along the orientation, looping when loop is on, and Home and End jump to the first and last item. Each item shows its state as aria-pressed and data-pressed. Without multiple the group is single-select: pressing an item presses only it, and pressing the pressed item clears it. With multiple, several items can be pressed at once.
The root takes class and every item takes item_class, so you style both with your own Tailwind classes. An :item slot also takes its own class, for that one item. Both apply, item_class first.
Single-select example (text alignment)
Pressing another alignment moves the pressed state to it. Pressing the pressed one clears the group.
Single-select example (text alignment)
<.toggle_group
id="toggle-group-hero"
aria-label="Text alignment"
value="left"
class="inline-flex gap-px p-px border border-neutral-950 dark:border-white"
item_class="flex size-8 items-center justify-center border-none rounded-none bg-transparent text-neutral-950 dark:text-white select-none hover:not-data-[disabled]:bg-neutral-100 dark:hover:not-data-[disabled]:bg-neutral-800 active:not-data-[disabled]:not-data-[pressed]:bg-neutral-200 dark:active:not-data-[disabled]:not-data-[pressed]:bg-neutral-700 data-[pressed]:bg-neutral-950 data-[pressed]:text-white dark:data-[pressed]:bg-white dark:data-[pressed]:text-neutral-950 data-[pressed]:hover:not-data-[disabled]:bg-neutral-950 data-[pressed]:hover:not-data-[disabled]:text-white dark:data-[pressed]:hover:not-data-[disabled]:bg-white dark:data-[pressed]:hover:not-data-[disabled]:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
>
<:item value="left" aria-label="Align left"><.icon name="hero-bars-3-bottom-left" class="size-4" /></:item>
<:item value="center" aria-label="Align center"><.icon name="hero-bars-3" class="size-4" /></:item>
<:item value="right" aria-label="Align right"><.icon name="hero-bars-3-bottom-right" class="size-4" /></:item>
</.toggle_group>Multi-select example (text formatting)
With multiple, each item flips on its own. value takes a list of the values that start pressed.
Multi-select example (text formatting)
<.toggle_group
id="toggle-group-multiple"
multiple
value={["bold", "italic"]}
aria-label="Text formatting options"
class="inline-flex gap-px p-px border border-neutral-950 dark:border-white"
item_class="flex size-8 items-center justify-center border-none rounded-none bg-transparent text-neutral-950 dark:text-white select-none hover:bg-neutral-100 dark:hover:bg-neutral-800 data-[pressed]:bg-neutral-950 data-[pressed]:text-white dark:data-[pressed]:bg-white dark:data-[pressed]:text-neutral-950 data-[pressed]:hover:bg-neutral-950 data-[pressed]:hover:text-white dark:data-[pressed]:hover:bg-white dark:data-[pressed]:hover:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
>
<:item value="bold" aria-label="Bold"><.icon name="hero-bold" class="size-4" /></:item>
<:item value="italic" aria-label="Italic"><.icon name="hero-italic" class="size-4" /></:item>
<:item value="underline" aria-label="Underline"><.icon name="hero-underline" class="size-4" /></:item>
</.toggle_group>Attributes and slots
id is required. value is the pressed value, or a list of them, on render. multiple allows several pressed items. disabled disables the whole group and sets data-disabled. orientation is "horizontal" (the default) or "vertical", which also picks the arrow keys. loop is on by default.
Give the group a name to render hidden inputs that hold the pressed values. The hook rewrites them as the selection changes and fires an input event, so an enclosing <.form phx-change> reacts. form names the form they belong to when the group is rendered outside it. on_change is a LiveView event pushed with %{"value" => values}, the list of pressed values, on every press. It goes to the LiveView or LiveComponent that renders the group.
Each :item takes value, disabled, class and aria-label. A disabled item is skipped by the arrow keys.
Parts & data attributes
Each part carries a stable data-part and a structural chelekom-toggle-group__* class you can target, plus the pressed state of the items.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Root | - | role="group", aria-orientation, aria-disabled, data-orientation, data-multiple, data-disabled | Carries the ToggleGroup hook; takes class. |
| Item | item | aria-pressed, aria-disabled, data-pressed, data-disabled, data-value | A single toggle button in the group; takes item_class for every item and the slot's class for one. |
| Value inputs | value-inputs | - | Hidden input(s) carrying the pressed value(s) for form submission. |
Accessibility
Follows the WAI-ARIA Toolbar pattern: arrow keys move focus between toggles along the configured orientation (looping when enabled), Home and End jump to the first and last toggle, and Enter or Space toggles the pressed state. Only one item is in the tab sequence (roving tabindex): the first pressed one, or the first enabled one when none is pressed. An item that holds only an icon needs an aria-label, and the group itself needs one too, as the examples show.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | — |
name
|
string
| — | — | No | Form field name (single → name, multiple → name[]) |
value
|
any
| — | — | No | Pressed value(s) — a list, or a single string (Phoenix form value) |
multiple
|
boolean
|
false
| — | No | Allow several items pressed at once (data-multiple) |
disabled
|
boolean
|
false
| — | No | Disable the whole group (data-disabled) |
orientation
|
string
|
horizontal
| — | No | horizontal | vertical (arrow-key axis) |
loop
|
boolean
|
true
| — | No | Loop arrow-key focus past the ends |
form
|
string
| — | — | No | Form id owning the hidden input(s) |
on_change
|
string
| — | — | No | LiveView event pushed on change ({value}) |
class
|
any
| — | — | No | — |
item_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Slots
| Name | Required | Description |
|---|---|---|
item
| Yes | A toggle button |
Attributes of the item slot
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
value
|
string
| — | — | No | The value this toggle represents |
disabled
|
boolean
| — | — | No | Disable just this item |
class
|
any
| — | — | No | Additional CSS classes for this item's button (data-part=item) |
aria-label
|
string
| — | — | No | Accessible label for this item's button |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
toggle_group
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless toggle_group