Button Group
import { ButtonGroup } from '@absoluteui/components/button-group';import '@absoluteui/components/button-group/style.css';Using Angular? See Button Group for Angular.
What it does
Section titled “What it does”A labelled role="group" of related native buttons, visually attached (shared borders, no inner radii) or spaced, horizontal or vertical. ButtonGroup.create renders Buttons from data; ButtonGroup.enhance groups buttons that already exist in the markup. Keyboard interaction stays native: Tab moves through every button. It is not a toolbar; a Toolbar component with arrow-key navigation is planned.
When to use
Section titled “When to use”- A small set of related actions that belong together, such as Previous / Next or Copy / Paste.
- Grouping buttons so assistive technologies announce their shared purpose.
When not to use
Section titled “When not to use”- Choosing one or several options: use Radio Group, Checkbox Group, or a segmented control.
- Large sets of commands with arrow-key navigation: use Toolbar (planned).
- Navigation between pages or views: use links or Tabs.
Examples
Section titled “Examples”Attached group with an action handler
Section titled “Attached group with an action handler”import { ButtonGroup, type ButtonGroupActionDetail } from '@absoluteui/components/button-group';
const host = document.createElement('div');document.body.append(host);ButtonGroup.create(host, { label: 'Pagination', size: 'sm', buttons: [ { icon: 'chevron-left', label: 'Previous page', value: 'prev' }, { icon: 'chevron-right', label: 'Next page', value: 'next' }, ],});host.addEventListener('aui-action', (event) => { const { value } = (event as CustomEvent<ButtonGroupActionDetail>).detail; host.dataset.last = value;});Enhance existing native buttons, spaced and wrapping
Section titled “Enhance existing native buttons, spaced and wrapping”import { ButtonGroup } from '@absoluteui/components/button-group';
const element = document.createElement('div');for (const text of ['Copy', 'Paste', 'Duplicate']) { const button = document.createElement('button'); button.type = 'button'; button.textContent = text; element.append(button);}document.body.append(element);ButtonGroup.enhance(element, { label: 'Clipboard', attached: false });Spaced groups by default
Section titled “Spaced groups by default”import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { buttonGroup: { attached: false } } });import { ButtonGroup } from '@absoluteui/components/button-group';import '@absoluteui/components/button/style.css';import '@absoluteui/components/button-group/style.css';
const element = document.querySelector<HTMLElement>('#clipboard-actions');if (element) { ButtonGroup.enhance(element, { label: 'Clipboard', attached: false }); element.addEventListener('click', (event) => { if (event.target instanceof HTMLButtonElement) element.dataset.last = event.target.textContent; });}More usage
Section titled “More usage”import { ButtonGroup } from '@absoluteui/components/button-group';
const host = document.createElement('div');document.body.append(host);ButtonGroup.create(host, { label: 'Pagination', buttons: [ { icon: 'chevron-left', label: 'Previous page', value: 'prev' }, { icon: 'chevron-right', label: 'Next page', value: 'next' }, ],});Anatomy
Section titled “Anatomy”<div class="aui-button-group aui-button-group--horizontal aui-button-group--attached" role="group" aria-label="Label"> <button class="aui-button aui-button--secondary aui-button--md">First</button> <button class="aui-button ...">Second</button> <button class="aui-button aui-button--icon-only" aria-label="More"><svg class="aui-icon" aria-hidden="true"></button></div>API reference
Section titled “API reference”Factory
Section titled “Factory”ButtonGroup.create(host: HTMLElement, options: ButtonGroupOptions): ButtonGroup; ButtonGroup.enhance(element: HTMLElement, options: ButtonGroupOptions): ButtonGroupGlobal configuration key: buttonGroup (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Accessible name of the group (aria-label). Required. | |
size |
'sm' | 'md' | 'lg' |
Size of the Buttons the group creates or enhances; defaults to the Button configuration. | |
buttons |
readonly { text?: string; icon?: IconRef; label?: string; variant?: ButtonVariant; value?: string; disabled?: boolean }[] |
create() only: Buttons to render. Icon-only buttons need a label. | |
orientation |
'horizontal' | 'vertical' |
"horizontal" |
Layout direction. Globally configurable. |
attached |
boolean |
true |
Shared borders and no inner radii; false spaces the buttons with a gap and lets horizontal groups wrap. Globally configurable. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
element |
HTMLElement |
The group element (role=group). |
buttons |
HTMLButtonElement[] |
Direct child buttons, in DOM order. |
options |
Readonly<ButtonGroupOptions> |
Current instance options. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
ButtonGroup.enhance(element: HTMLElement, options: ButtonGroupOptions): ButtonGroup |
Enhance an existing element containing native buttons; plain buttons become Buttons until destroy. |
update(options: Partial<ButtonGroupOptions>): void |
Merge and apply options; a new buttons array rebuilds the Buttons. |
destroy(): void |
Remove listeners and subscriptions, destroy created or enhanced Buttons, restore the element’s role and aria-label. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-action |
{ value: string } |
Yes | No | create() only: a button was activated. value defaults to the button index. |
Content areas
Section titled “Content areas”| Area | Description |
|---|---|
buttons |
Native buttons (created from buttons, or the existing children with enhance()). |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-button-group |
Group element. |
aui-button-group--horizontal | --vertical |
Orientation modifiers. |
aui-button-group--attached |
Shared borders, removed inner radii; horizontal groups scroll instead of wrapping. |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-focus-ring | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-space-2
TypeScript definitions
Section titled “TypeScript definitions”type ButtonGroupOrientation = 'horizontal' | 'vertical';interface ButtonGroupButton { text?: string; icon?: IconRef; label?: string; variant?: ButtonVariant; value?: string; disabled?: boolean }interface ButtonGroupOptions { label: string; orientation?: ButtonGroupOrientation; attached?: boolean; size?: ButtonSize; buttons?: readonly ButtonGroupButton[] }interface ButtonGroupActionDetail { value: string }Accessibility
Section titled “Accessibility”Pattern: Native buttons inside an element with role=group and an accessible name (WAI-ARIA group role). Not the APG Toolbar pattern.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Tab / Shift+Tab | Any | Moves through every enabled button in DOM order (native). |
| Enter / Space | Button focused | Activates the button (native). |
| Element | Role |
|---|---|
| group element | group |
| buttons | button (native) |
| icons | none (aria-hidden=true) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-label |
group element | The label option |
aria-label |
icon-only button | The button’s label |
Each button shows its own :focus-visible outline. In attached groups the focused button is raised and the ring is inset so the scroll container does not clip it.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”| Criterion | Level | Note |
|---|---|---|
| 1.3.1 Info and Relationships | A | |
| 1.4.11 Non-text Contrast | AA | |
| 2.1.1 Keyboard | A | |
| 2.4.7 Focus Visible | AA | |
| 4.1.2 Name, Role, Value | A |
Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- aria-orientation is not set: the group role does not support it and there is no arrow-key navigation.
- For a toolbar with arrow-key roving focus, a Toolbar component is planned.
- Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.
Behavior
Section titled “Behavior”States
Section titled “States”horizontal, vertical, attached, spaced; per button: default, hover, focus-visible, disabled.
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| any | update({ orientation }) or global config |
new layout |
| any | update({ buttons }) |
Buttons rebuilt |
| any | update({ size }) |
Buttons resized in place |
Accessibility notes
Section titled “Accessibility notes”- Screen readers announce the group label when focus enters the group.
- Disabled buttons use native
disabledand leave the tab order. - In attached groups the hovered or focused button is raised so its full border and focus ring are visible; the ring is inset so the scroll container cannot clip it.
- Manual screen-reader verification is pending (see Known limitations).
Responsive behavior
Section titled “Responsive behavior”Spaced horizontal groups (attached: false) wrap at narrow widths. Attached horizontal groups never wrap, because wrapping would break the shared borders; they are limited to the container width and scroll horizontally. Use attached: false for groups that must stay fully visible on small screens. Vertical groups stack.
Right-to-left
Section titled “Right-to-left”Logical properties throughout: the first button keeps the start radii and the last keeps the end radii in both directions.
Theming
Section titled “Theming”Buttons use their own role tokens. In forced-colors mode focus uses a Highlight outline and button borders use ButtonText.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { buttonGroup: { attached: false } } });Changes apply to existing instances immediately.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic. size falls back to the Button configuration.
None on the group. Each entry in buttons takes an icon.
Security
Section titled “Security”Button text and labels use textContent. CSP-safe: no inline styles or handlers.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry.
Performance
Section titled “Performance”One delegated click listener (create only) and one configuration subscription, plus one Button per button. Budget: under 2 ms for 10 buttons (not yet covered by the performance test).
Supported data scale
Section titled “Supported data scale”Intended for small groups of 2 to 7 buttons. Unit-tested with up to three.
Edge cases
Section titled “Edge cases”- An icon-only entry without
labelthrowsBUTTON_ICON_ONLY_LABEL; Buttons already created for that call are discarded. enhance()only considers direct child buttons; nested buttons are not styled as part of the group.- Consumer-enhanced Buttons inside an enhanced group keep their own size and variant.
- Disabled buttons emit neither
clicknoraui-action.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
BUTTON_GROUP_OPTIONS |
configuration | Missing label, invalid size, or buttons passed to enhance(). |
BUTTON_ICON_ONLY_LABEL |
configuration | An icon-only button has no label. |
CFG_INVALID_VALUE |
configuration | Invalid orientation or attached value. |
A failed construction leaves no rendering, listeners, or subscriptions behind.
Known limitations
Section titled “Known limitations”- No arrow-key navigation; a Toolbar component is planned.
- Attached horizontal groups scroll rather than wrap at narrow widths.
- No pressed/toggle state management.
- Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
Unsupported scenarios
Section titled “Unsupported scenarios”- Split buttons and menu buttons.
- Managing
aria-pressedtoggle state (set it on the buttons yourself).