Thrive Design System

Components

ButtonGroup

A container component for grouping related buttons together with attached or detached styles and horizontal or vertical orientations

Installation

npm install @thrivecart/ui
yarn add @thrivecart/ui
pnpm add @thrivecart/ui
bun add @thrivecart/ui

Usage

Import the ButtonGroup component from the package:

import { ButtonGroup, Button, IconButton } from '@thrivecart/ui';

Examples

Basic Attached Group

Attached Buttons

Buttons visually connected with shared borders

<ButtonGroup attached>
<Button variant="secondary">Left</Button>
<Button variant="secondary">Center</Button>
<Button variant="secondary">Right</Button>
</ButtonGroup>

Detached Group

Detached Buttons

Buttons separated with consistent spacing

<ButtonGroup attached={false}>
<Button variant="primary">Save</Button>
<Button variant="secondary">Cancel</Button>
</ButtonGroup>

Text Formatting Toolbar

Formatting Controls

Common text formatting button group

<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdFormatBold />} aria-label="Bold" />
<IconButton variant="secondary" icon={<MdFormatItalic />} aria-label="Italic" />
<IconButton variant="secondary" icon={<MdFormatUnderlined />} aria-label="Underline" />
</ButtonGroup>

Alignment Controls

Text Alignment

Alignment button group

<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdFormatAlignLeft />} aria-label="Align left" />
<IconButton variant="secondary" icon={<MdFormatAlignCenter />} aria-label="Align center" />
<IconButton variant="secondary" icon={<MdFormatAlignRight />} aria-label="Align right" />
</ButtonGroup>

View Switcher

View Options

Switch between different view modes

<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdViewList />} aria-label="List view" />
<IconButton variant="secondary" icon={<MdViewModule />} aria-label="Grid view" />
<IconButton variant="secondary" icon={<MdViewStream />} aria-label="Stream view" />
</ButtonGroup>

Vertical Orientation

Vertical Layout

Stack buttons vertically

<div className="flex gap-8">
<ButtonGroup orientation="vertical" attached>
  <Button variant="secondary">Top</Button>
  <Button variant="secondary">Middle</Button>
  <Button variant="secondary">Bottom</Button>
</ButtonGroup>

<ButtonGroup orientation="vertical" attached={false}>
  <Button variant="primary">Primary Action</Button>
  <Button variant="secondary">Secondary Action</Button>
  <Button variant="ghost">Tertiary Action</Button>
</ButtonGroup>
</div>

Size Variants

All Sizes

Button groups in different sizes

<div className="flex flex-col gap-4 items-start">
<ButtonGroup size="xs">
  <Button variant="secondary">XS</Button>
  <Button variant="secondary">Size</Button>
  <Button variant="secondary">Group</Button>
</ButtonGroup>

<ButtonGroup size="sm">
  <Button variant="secondary">Small</Button>
  <Button variant="secondary">Size</Button>
  <Button variant="secondary">Group</Button>
</ButtonGroup>

<ButtonGroup size="md">
  <Button variant="secondary">Default</Button>
  <Button variant="secondary">Size</Button>
  <Button variant="secondary">Group</Button>
</ButtonGroup>

<ButtonGroup size="lg">
  <Button variant="secondary">Large</Button>
  <Button variant="secondary">Size</Button>
  <Button variant="secondary">Group</Button>
</ButtonGroup>
</div>

Toggle State

Interactive Toggle

Single and multi-select toggle groups

Single Select (View)

Multi Select (Formatting)

function ToggleExample() {
const [activeAlign, setActiveAlign] = React.useState('left');
const [formatting, setFormatting] = React.useState({
  bold: false,
  italic: false,
  underline: false,
});

return (
  <div className="space-y-6">
    {/* Single Select */}
    <div>
      <h4 className="text-sm font-medium mb-3">Text Alignment (Single Select)</h4>
      <ButtonGroup attached>
        <IconButton
          variant={'secondary'}
          data-active={activeAlign === 'left'}
          icon={<MdFormatAlignLeft />}
          onClick={() => setActiveAlign('left')}
          aria-label="Align left"
        />
        <IconButton
          variant={'secondary'}
          data-active={activeAlign === 'center'}
          icon={<MdFormatAlignCenter />}
          onClick={() => setActiveAlign('center')}
          aria-label="Align center"
        />
        <IconButton
          variant={'secondary'}
          data-active={activeAlign === 'right'}
          icon={<MdFormatAlignRight />}
          onClick={() => setActiveAlign('right')}
          aria-label="Align right"
        />
      </ButtonGroup>
    </div>

    {/* Multi Select */}
    <div>
      <h4 className="text-sm font-medium mb-3">Text Formatting (Multi Select)</h4>
      <ButtonGroup attached>
        <IconButton
          variant={'secondary'}
          data-active={formatting.bold}
          icon={<MdFormatBold />}
          onClick={() => setFormatting(prev => ({ ...prev, bold: !prev.bold }))}
          aria-label="Bold"
        />
        <IconButton
          variant={'secondary'}
          data-active={formatting.italic}
          icon={<MdFormatItalic />}
          onClick={() => setFormatting(prev => ({ ...prev, italic: !prev.italic }))}
          aria-label="Italic"
        />
        <IconButton
          variant={'secondary'}
          data-active={formatting.underline}
          icon={<MdFormatUnderlined />}
          onClick={() => setFormatting(prev => ({ ...prev, underline: !prev.underline }))}
          aria-label="Underline"
        />
      </ButtonGroup>
    </div>
  </div>
);
}

Best Practices

Use attached for related actions

Use attached mode when buttons represent related options in a single control (e.g., text alignment).

Use detached for distinct actions

Use detached mode for action pairs like Save/Cancel where actions are distinct but related.

Provide clear active states

When using as toggle buttons, clearly indicate the active state using the primary variant.

Add aria-labels to icon buttons

Always provide accessible labels for icon-only buttons in the group.

Don't mix attached and detached

Use a consistent style within your application for similar use cases.

Don't group unrelated actions

Only group buttons that are contextually related or form a cohesive control.

Props

ButtonGroup

PropTypeDefaultDescription
attachedbooleantrueWhether buttons are visually connected
orientation'horizontal' | 'vertical''horizontal'Layout direction
size'xs' | 'sm' | 'md' | 'lg''md'Size applied to all buttons
childrenReactNode-Button or IconButton components
classNamestring-Additional className

Accessibility

ARIA Roles

ButtonGroup uses role="group" to indicate that the buttons are related. For toggle groups, consider using role="toolbar" or implementing a proper ARIA pattern.

  • Uses semantic grouping with role="group"
  • Supports keyboard navigation between buttons
  • Individual buttons maintain their accessibility features
  • Always provide aria-label for icon-only buttons
  • For exclusive selection, consider using aria-pressed to indicate state