ButtonGroup
Joins adjacent Buttons into a single control, collapsing the inner corners and overlapping borders.
Last updated
Show codeHide code
<ButtonGroup label="View">
<Button variant="outline" color="neutral">Day</Button>
<Button variant="outline" color="neutral">Week</Button>
<Button variant="outline" color="neutral">Month</Button>
</ButtonGroup>Add
npx @ava-ui/apogee add button-group
No Apogee dependencies of its own — it’s a layout wrapper. Designed to compose
with Button (add that separately: npx @ava-ui/apogee add button).
Usage
---
import ButtonGroup from '@/components/apogee/ButtonGroup/ButtonGroup.astro'
import Button from '@/components/apogee/Button/Button.astro'
---
<ButtonGroup label="Alignment">
<Button variant="outline" color="neutral">Left</Button>
<Button variant="outline" color="neutral">Center</Button>
<Button variant="outline" color="neutral">Right</Button>
</ButtonGroup>
API
@composable — exposed to Content Editors in AVA. Any other native <div>
attribute passes through.
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string |
— | Accessible label for the group (sets aria-label on the role="group" root). |
disabled |
boolean |
false |
Disables every button inside. The root is a <fieldset>, so this is native and needs no JS. |
class |
string |
— | Extra classes, merged last. |
ButtonGroup applies no visual style to itself beyond the join — it targets its
direct children ([&>*:not(:first-child)] / [&>*:not(:last-child)]) to
overlap borders and drop the inner corner radius. It reads no color tokens of
its own; those come from whatever’s composed inside (see Button’s Tokens).
States
Hover, focus and active belong to the composed Buttons. A focused item lifts
above its overlapping neighbour so its whole outline shows. disabled on the
group disables every button at once.
Show codeHide code
<ButtonGroup label="Alignment">
<Button variant="outline" color="neutral">Left</Button>
<Button variant="outline" color="neutral">Center</Button>
<Button variant="outline" color="neutral">Right</Button>
</ButtonGroup>
<ButtonGroup label="Alignment" disabled>
<Button variant="outline" color="neutral">Left</Button>
<Button variant="outline" color="neutral">Center</Button>
<Button variant="outline" color="neutral">Right</Button>
</ButtonGroup>Examples
Mixed content — a group works with any adjacent controls, not only Button:
Show codeHide code
<ButtonGroup label="Zoom">
<Button variant="outline" color="neutral" iconOnly aria-label="Zoom out">−</Button>
<Button variant="outline" color="neutral" iconOnly aria-label="Zoom in">+</Button>
</ButtonGroup>Accessibility
- Renders
role="group"witharia-label(from thelabelprop) so screen readers announce it as a set, not stray buttons. - Always pass
label— without it the group has no accessible name. - Keyboard — unchanged from the composed children:
Tabmoves through each button in order (no roving tabindex; this isn’t aradiogrouportablist).
Tokens
None of its own. Borders, backgrounds, and focus rings come from the composed components (typically Button — see its Tokens table).
Do / Don’t
- Do use it for a small set of related, mutually exclusive or peer actions (view toggles, pagination arrows).
- Do always pass
label. - Don’t mix more than one
variantinside a group — it reads as one control, so its buttons should look uniform. - Don’t use it for unrelated actions that happen to sit next to each other — use plain spacing instead.
Content
- Labels follow Button’s content rules (short, verb-led where the action is a command; nouns for a view/filter toggle).