HyToggleGroup
Usage examples on this page are written for Lit / plain HTML (
<hy-toggle-group>). The same component ships asHyToggleGroupin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A form-associated container component that manages a group of toggle buttons. Provides centralized selection state management, keyboard navigation, accessibility features, and form integration via ElementInternals API.
Toggle group, tab group or tab nav?
Pick by what changes, never by look. A value is picked — a view mode (list, map, grid), a density, a unit — and the same content is shown another way: a toggle group. Panels of different content switch in place: hy-tab-group. Each item is its own page and the URL changes: hy-tab-nav.
Supports two selection modes:
- Single mode (default): Radio-group pattern — one toggle selected at a time. Arrow keys move focus and select. Uses
role="radiogroup". - Multiple mode: Toolbar pattern — multiple toggles can be selected. Arrow keys move focus only. Space/Enter toggles selection. Uses
role="toolbar".
Form Integration
Implements Form-Associated Custom Elements API via ElementInternals. In single mode, submits a single string value. In multiple mode, submits pipe-delimited values (e.g. "bold|italic").
Keyboard Navigation
- Single mode: Arrow keys move focus and auto-select. Space selects focused item.
- Multiple mode: Arrow keys move focus only. Space/Enter toggles focused item.
- Home/End: Move focus to first/last item.
- Tab: Enters/exits the group (roving tabindex).
When to use
- Choosing one option from two to five that people switch between often: a view (list, grid), a range (day, week, month), an alignment.
- Turning several filters of one kind on and off at once, with
selection-mode="multiple".
Do
- Order the options in a meaningful way: by size, by time or as a sequence.
When not to use
- More than about five options:
hy-select, orhy-comboboxwhen people should type to filter. - Buttons that run actions and hold no selected state: a row of
hy-buttonelements. - One on/off setting:
hy-switchwhen it applies at once,hy-checkboxwhen a form submits it. - A view switcher, where one option is always shown: set
disallow-empty. Leave it off for filters, where nothing selected is meaningful.
Accessibility: your part
- Give every
hy-togglea name. An icon-only toggle takesaria-label.
Examples
Basic single selection
<hy-toggle-group value="center" label="Text alignment">
<hy-toggle value="left">Left</hy-toggle>
<hy-toggle value="center">Center</hy-toggle>
<hy-toggle value="right">Right</hy-toggle>
</hy-toggle-group>Error and warning
<!-- Not accepted: danger color, announced as an alert -->
<hy-toggle-group label="Delivery" error-message="Choose a delivery option">
<hy-toggle value="pickup">Pickup</hy-toggle>
<hy-toggle value="courier">Courier</hy-toggle>
</hy-toggle-group>
<!-- Accepted, but worth a second look: warning color, announced politely,
never blocks submission -->
<hy-toggle-group label="Quality" value="max" warning-message="Max quality doubles the file size">
<hy-toggle value="standard">Standard</hy-toggle>
<hy-toggle value="max">Max</hy-toggle>
</hy-toggle-group>Inline layout
<style>
.alignment-form {
--hy-toggle-group-label-column-width: 6rem;
}
</style>
<div class="alignment-form">
<hy-toggle-group layout="inline" label="Alignment" value="left">
<hy-toggle value="left">Left</hy-toggle>
<hy-toggle value="center">Center</hy-toggle>
<hy-toggle value="right">Right</hy-toggle>
</hy-toggle-group>
</div>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
selectionMode | selection-mode | 'single' | 'multiple' | 'single' | Selection mode: 'single' (radio-like) or 'multiple' (checkbox-like). |
value | value | string | '' | The currently selected value(s). In single mode: a single string value. In multiple mode: pipe-delimited string (e.g. "bold|italic"). |
orientation | orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction of the toggle items. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size of all toggle items in the group. |
variant | variant | 'neutral' | 'brand' | 'neutral' | Visual style (role) of the toggle items. |
emphasis | emphasis | 'soft' | 'outlined' | 'soft' | Visual chrome weight propagated to every child toggle. Orthogonal to variant (role). |
disabled | disabled | boolean | false | Disables all toggle items in the group. |
required | required | boolean | false | At least one value must be selected for form validation. |
disallowEmpty | disallow-empty | boolean | false | Prevents deselecting the last selected item. |
name | name | string | '' | Form submission name. |
label | label | string | '' | Visible group label (also sets the accessible name via aria-labelledby). Without one, aria-label / aria-labelledby on the host name the group. |
helperText | helper-text | string | '' | Helper text below the toggle list. Replaced by error-message / warning-message while one of them shows. |
errorMessage | error-message | string | '' | Error message below the toggle list (replaces the helper text). Setting it raises error; clearing it lowers it. |
error | error | boolean | false | Error state: the selection is not accepted and has to change. Shows error-message in the danger color. Visual and for screen readers only — form validity comes from required and setCustomValidity(). |
warningMessage | warning-message | string | '' | Warning message below the toggle list (replaces the helper text). Setting it raises warning; clearing it lowers it. |
warning | warning | boolean | false | Warning state: the selection is accepted but worth a second look. Shows warning-message in the warning color; never touches form validity. error wins when both are set. |
layout | layout | 'block' | 'inline' | 'block' | Row layout: block (default) stacks label above the toggle list; inline puts label on the left and list on the right. Helper/error always sit under the list (col 2–end), never under the label. Set --hy-toggle-group-label-column-width on a wrapping container so stacked groups align. |
fullWidth | full-width | boolean | false | Stretch the group to fill its container's inline size, distributing the toggle segments equally (flex: 1 each) — a true segmented control rather than a content-width strip. Off by design; opt in for full-bleed rows. |
loop | loop | boolean | true | Whether keyboard navigation wraps around at the edges. |
form | — | HTMLFormElement | null | — | Returns the form element this group is associated with. |
validity | — | ValidityState | — | Returns the ValidityState for this group. |
validationMessage | — | string | — | Returns the validation message for this group. |
Events
| Event | Detail | Description |
|---|---|---|
change | — | Fired when selection changes. Detail: { selectedValues: string[], reason: 'user' | 'programmatic', name: string } |
input | — | Standard form input event |
Slots
| Slot | Description |
|---|---|
default | hy-toggle elements |
CSS Parts
| Part | Description |
|---|---|
base | Outer wrapper containing label, toggle list, and helper/error text |
label | The visible group label element |
toggle-list | The container that holds the toggle items (role=radiogroup|toolbar) |
helper-text | Helper text below the list |
error-message | Error message below the list (shown while error is set) |
warning-message | Warning message below the list (shown while warning is set and error is not) |
message-icon | The icon starting the error or warning message |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-toggle-group-foreground-danger-rest | var(--hy-foreground-danger-rest) | Error message text and icon color. |
--hy-toggle-group-foreground-warning-rest | var(--hy-foreground-warning-rest) | Warning message text and icon color. |
--hy-toggle-group-foreground-icon-default-disabled | var(--hy-foreground-icon-default-disabled) | The message line's icon color when the group is disabled |
--hy-toggle-group-gap-layout-2xs | var(--hy-gap-layout-2xs) | Gap between the message icon and its text. |
--hy-toggle-group-radius-action-md | var(--hy-radius-action-md) | Border radius for child toggle items (the action radius hy-toggle reads) |
--hy-toggle-group-stroke-sm | var(--hy-stroke-sm) | Border width of the child toggles, which the group sets on each toggle's stroke hook and uses as the seam: neighbors overlap by it when outlined single-select, and stand apart by it when soft or multiple-select |
--hy-toggle-group-gap-form-sm | var(--hy-gap-form-sm) | Gap between toggle items in print |
--hy-toggle-group-label-column-width | — | Width of the label column in layout="inline" mode. No default — set on a wrapping container so stacked groups align. |
Methods
checkValidity()
Checks validity against constraints.
reportValidity()
Checks validity and shows browser validation UI if invalid.
setCustomValidity()
Sets a custom validation message.
focus()
Sets focus on the selected toggle, or the first toggle if none selected.