VayuUI

ButtonGroup

Groups multiple buttons together with connected borders and consistent sizing for cohesive action layouts.

Installation

npx vayu-ui init #one time only
npx vayu-ui add buttongroup
npx vayu-ui add -t buttongroup #with test case needs

Usage

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 &amp; 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" on ButtonGroup indicates a related set of buttons.
    • aria-label or aria-labelledby provides the accessible group name. At least one is required.
  • Focus Behavior:
    • Focused and hovered buttons receive z-10 so the focus ring is fully visible over adjacent siblings (WCAG 2.4.11).
    • Focus follows natural DOM tab order through child buttons.

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 reference

Props

ButtonGroup

PropTypeDefaultDescription
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.
fullWidthbooleanfalseStretch to fill container width.
aria-labelstringAccessible label for the button group.
aria-labelledbystringID of element that labels this group.
classNamestringAdditional CSS classes.
childrenReactNodeButton elements to group.

On this page