---
title: Button Group
description: Weld adjacent controls into a single unit — attached buttons, split buttons, search fields
visibility: guest
draft: false
---

# Button Group

Welds adjacent controls into **one** unit: the inner corners square off and the
touching borders collapse to a single seam, so a row of buttons reads as one
control instead of a row of pills.

It welds **whatever you put in it** — buttons, an input plus a button, a select
plus a button — which is what makes it the primitive behind attached actions,
split buttons, and search fields.

## Basic Usage

:::preview{title="Attached actions"}
<x-wirekit::button-group label="Text alignment">
    <x-wirekit::button intent="neutral" surface="outline">Left</x-wirekit::button>
    <x-wirekit::button intent="neutral" surface="outline">Center</x-wirekit::button>
    <x-wirekit::button intent="neutral" surface="outline">Right</x-wirekit::button>
</x-wirekit::button-group>
:::

## Split button

A primary action welded to a menu of secondary ones — the canonical use.

:::preview{title="A split button"}
<x-wirekit::button-group label="Save options">
    <x-wirekit::button intent="primary">Save</x-wirekit::button>
    <x-wirekit::dropdown>
        <x-slot:trigger>
            <x-wirekit::button intent="primary" aria-label="More save options">
                <x-wirekit::icon name="chevron-down" size="sm" />
            </x-wirekit::button>
        </x-slot:trigger>
        <x-wirekit::dropdown.item>Save and close</x-wirekit::dropdown.item>
        <x-wirekit::dropdown.item>Save as draft</x-wirekit::dropdown.item>
    </x-wirekit::dropdown>
</x-wirekit::button-group>
:::

## Search field

An input welded to its button. The group squares the seam; the input keeps its
own border and focus ring.

:::preview{title="A search field"}
<x-wirekit::button-group label="Search">
    <x-wirekit::input name="q" type="search" label="Search invoices" hide-label placeholder="Search invoices" />
    <x-wirekit::button intent="neutral" surface="outline">Search</x-wirekit::button>
</x-wirekit::button-group>
:::

## Vertical

:::preview{title="A vertical weld"}
<x-wirekit::button-group orientation="vertical" label="Zoom">
    <x-wirekit::button intent="neutral" surface="outline" aria-label="Zoom in">
        <x-wirekit::icon name="plus" size="sm" />
    </x-wirekit::button>
    <x-wirekit::button intent="neutral" surface="outline" aria-label="Zoom out">
        <x-wirekit::icon name="minus" size="sm" />
    </x-wirekit::button>
</x-wirekit::button-group>
:::

## Not a toolbar

Button Group is a **visual** weld: every control inside stays individually
Tab-reachable, which is what people expect from attached buttons.

If you want a **roving** keyboard model — one Tab stop for the whole cluster,
arrow keys to move between controls — that is [Toolbar](/components/toolbar).
Reach for it when the cluster is a dense bar of many controls.

Related: [Segmented Control](/components/segmented-control) is the right choice
when the buttons are **mutually-exclusive options** rather than separate actions.

## Input addons

For a text affix or an icon *inside* a single field — `https://`, `$`, `@`, a
search glyph, a clear or copy button — you do **not** need a group.
[Input](/components/input) already takes `prefix` / `suffix` (and `clearable` /
`copyable`) inside one shared border and focus ring.

Use Button Group when you are welding **separate controls** together.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | string\|null | `null` | Accessible name for the group |
| `orientation` | string | `'horizontal'` | `horizontal` or `vertical` |
| `scope` | string\|null | `null` | Scoped personalization name |

## Accessibility

- The group is `role="group"`; give it a `label` so assistive technology can
  announce what the cluster is for.
- **Every control stays Tab-reachable** — no roving tabindex. That is
  [Toolbar](/components/toolbar)'s job.
- A focused control is lifted above the collapsed seam (`z-index`), so its focus
  ring is never clipped by its neighbor — a weld must not cost focus
  visibility (WCAG 2.4.7).
- Icon-only children still need their own `aria-label`.
- **RTL mirrors for free.** The weld uses CSS logical properties
  (`border-start-start-radius`, `margin-inline-start`), so it flips correctly
  under `dir="rtl"` with no extra rules and no separate stylesheet.

## Keyboard Interaction

| Key | Action |
| --- | --- |
| <kbd>Tab</kbd> | Move to the next control in the group |
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Activate the focused control |

## Pitfalls

- **Do not use it for mutually-exclusive options** — that is
  [Segmented Control](/components/segmented-control).
- **Do not use it for a dense control bar** — that is
  [Toolbar](/components/toolbar), which brings the roving keyboard model.
- **Do not reach for it for a text affix** — [Input](/components/input)'s
  `prefix` / `suffix` already share one border.

## Design Tokens

| Element | Token |
| --- | --- |
| Seam width | `--border-wk-width` (the collapse is a negative margin of exactly this) |
| Corner radii | inherited from the welded children |

## Further Reading

- [WAI-ARIA: `group` role](https://www.w3.org/TR/wai-aria-1.2/#group)
- [MDN: CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
