ButtonGroup
Groups multiple buttons together with connected borders and consistent sizing for cohesive action layouts.
Installation
npx vayu-ui init #one time onlynpx vayu-ui add buttongroupnpx vayu-ui add -t buttongroup #with test case needsUsage
Control
Surface
Overlay
Full
<ButtonGroup aria-label="Text alignment">
<Button variant="outline">
<Button.Text>Left</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Center</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Right</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup aria-label="Save options">
<Button variant="primary">
<Button.Text>Save</Button.Text>
</Button>
<Button variant="primary">
<Button.Text>Save & Close</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup orientation="vertical" aria-label="Vertical actions">
<Button variant="outline">
<Button.Text>Top</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Middle</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Bottom</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup fullWidth aria-label="Confirmation actions">
<Button variant="secondary">
<Button.Text>Cancel</Button.Text>
</Button>
<Button variant="primary">
<Button.Text>Confirm</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup aria-label="Mixed action buttons">
<Button variant="outline">
<Button.Text>Back</Button.Text>
</Button>
<Button variant="secondary">
<Button.Text>Save Draft</Button.Text>
</Button>
<Button variant="primary">
<Button.Text>Submit</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup radius="surface" aria-label="Formatted radius">
<Button variant="outline">
<Button.Text>A</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>B</Button.Text>
</Button>
</ButtonGroup>
<ButtonGroup aria-label="Formatting options">
<Button variant="outline">
<Button.Icon>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
</svg>
</Button.Icon>
</Button>
<Button variant="outline">
<Button.Icon>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="19" x2="10" y1="4" y2="4"/>
<line x1="14" x2="15" y1="20" y2="4"/>
<line x1="5" x2="19" y1="20" y2="20"/>
</svg>
</Button.Icon>
</Button>
<Button variant="outline">
<Button.Icon>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M10 4h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4h-8"/>
<path d="M14 12H6"/>
<path d="M10 8l-4 4 4 4"/>
</svg>
</Button.Icon>
</Button>
</ButtonGroup>Anatomy
import { ButtonGroup, Button } from 'vayu-ui';
<ButtonGroup aria-label="Group label">
<Button variant="outline">
<Button.Text>Button 1</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Button 2</Button.Text>
</Button>
<Button variant="outline">
<Button.Text>Button 3</Button.Text>
</Button>
</ButtonGroup>;- ButtonGroup — Root container with
role="group". Manages orientation, sizing, radius, and focus stacking for child buttons via CSS. - Button — Individual action buttons rendered as children of the group. Supports all variants and subcomponents (
Button.Text,Button.Icon).
Accessibility
- Keyboard Support:
Tab— Move focus to the next focusable button.Shift + Tab— Move focus to the previous focusable button.Enter/Space— Activate the focused button.
- ARIA Attributes:
role="group"onButtonGroupindicates a related set of buttons.aria-labeloraria-labelledbyprovides the accessible group name. At least one is required.
- Focus Behavior:
- Focused and hovered buttons receive
z-10so the focus ring is fully visible over adjacent siblings (WCAG 2.4.11). - Focus follows natural DOM tab order through child buttons.
- Focused and hovered buttons receive
Screen reader behavior
When a screen reader encounters a ButtonGroup, it announces the group label (via aria-label or aria-labelledby) followed by the individual button labels in sequence. Each button is announced as a standard push button with its text content. Users navigate between buttons using Tab and Shift + Tab, and the screen reader announces each button as it receives focus. No additional state information is conveyed since button groups do not manage selection state.
Component Folder Structure
ButtonGroup/
├── ButtonGroup.tsx # Component with CSS-based sizing, radius, and focus management
├── types.ts # TypeScript type definitions (ButtonGroupProps, ButtonGroupRadius)
├── index.ts # Re-exports ButtonGroup and types
└── README.md # Component usage referenceProps
ButtonGroup
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Stack direction of buttons. |
size | "small" | "medium" | "large" | "medium" | Size applied to all child buttons via CSS. |
radius | "control" | "surface" | "overlay" | "full" | "control" | Border radius variant using semantic design tokens. |
fullWidth | boolean | false | Stretch to fill container width. |
aria-label | string | — | Accessible label for the button group. |
aria-labelledby | string | — | ID of element that labels this group. |
className | string | — | Additional CSS classes. |
children | ReactNode | — | Button elements to group. |