Summary
Functions
A segmented selection control: one rail of options where the pressed state
is the point. Where button_group groups actions (press one, something
happens, nothing stays lit), toggle_group holds a selection - exactly
one option (default) or any number of them (multiple).
Functions
A segmented selection control: one rail of options where the pressed state
is the point. Where button_group groups actions (press one, something
happens, nothing stays lit), toggle_group holds a selection - exactly
one option (default) or any number of them (multiple).
The component is stateless and server-driven, the LiveView way: you pass
the current value, every press sends on_change with the pressed option
in phx-value-toggle, and you assign the value back. No hook, no client
state.
Single select renders native radio inputs (the same mechanics as the
colour-scheme switch's segmented variant): real radiogroup semantics, one
tab stop, and arrow keys move the selection - the full WAI-ARIA radio
pattern with zero JavaScript. Multiple select renders aria-pressed
toggle buttons, the toolbar pattern.
Single select - a density rail
<.toggle_group aria_label="Density" value={@density} on_change="set_density">
<:item value="compact">Compact</:item>
<:item value="cozy">Cozy</:item>
<:item value="comfortable">Comfortable</:item>
</.toggle_group>
def handle_event("set_density", %{"toggle" => density}, socket) do
{:noreply, assign(socket, density: density)}
endMultiple select - formatting toggles
With multiple, value is a list and the server owns the toggle logic:
<.toggle_group multiple aria_label="Formatting" value={@formats} on_change="toggle_format">
<:item value="bold" aria-label="Bold"><.icon name="hero-bold" /></:item>
<:item value="italic" aria-label="Italic"><.icon name="hero-italic" /></:item>
</.toggle_group>
def handle_event("toggle_format", %{"toggle" => format}, socket) do
formats = socket.assigns.formats
formats =
if format in formats, do: List.delete(formats, format), else: [format | formats]
{:noreply, assign(socket, formats: formats)}
endVariants
variant="solid" (default) is the wash rail with a neutral chip - the
segmented-control look the scheme switch uses. variant="outline" is the
bordered toolbar rail with floating chips, at home next to a
button_group. variant="accent" keeps the wash rail but paints the
selection in the brand accent, for a committed setting rather than a view
preference.
Values survive the phx-value-* string round-trip: pressed comparison is
string-based, so value={2} still highlights after the server re-assigns
the "2" it received.
The single-select radios detach themselves from any surrounding form
(their form attribute points at nothing), so dropping a toggle group
inside a <.form> never posts a stray "<id>-toggle" param.
Attributes
id(:string) - Defaults tonil.aria_label(:string) (required) - the ARIA label for the group.value(:any) - the selected value - a single term, or a list whenmultiple. Defaults tonil.on_change(:any) - event name (or JS command) sent on press; the pressed option arrives in phx-value-toggle. Omit it and put phx-click on individual items instead. Defaults tonil.multiple(:boolean) - treat value as a list; any number of options can be pressed. Defaults tofalse.variant(:string) - solid is the wash rail with a neutral chip; outline is the bordered toolbar rail; accent paints the selection in the brand colour. Defaults to"solid". Must be one of"solid","outline", or"accent".size(:string) - Defaults to"md". Must be one of"sm","md", or"lg".disabled(:boolean) - disables every item. Defaults tofalse.class(:any) - extra classes for the rail. Defaults tonil.- Global attributes are accepted.
Slots
item(required) - Accepts attributes:value(:any) (required) - the option this item represents.disabled(:boolean) - disables this item only.class(:any) - extra classes for this item.aria-label(:string) - accessible name for icon-only items - reaches the radio/button itself.