Button Group
Groups related buttons, shares its styling with every child, and optionally welds them into a segmented control.
import { Button, ButtonGroup } from "astralis-ui";
export function ButtonGroupDemo() {
return (
<ButtonGroup variant="outline" colorScheme="gray">
<Button>Save draft</Button>
<Button>Preview</Button>
{/* An explicit prop on a child always wins over the group default. */}
<Button variant="solid" colorScheme="brand">
Publish
</Button>
</ButtonGroup>
);
}orientation
spacing
variant
colorScheme
size
import { Button, ButtonGroup } from "astralis-ui";
<ButtonGroup>
<Button>Save draft</Button>
<Button>Preview</Button>
<Button>Publish</Button>
</ButtonGroup>
Import#
import { ButtonGroup, Button } from "astralis-ui";Usage#
Set variant, colorScheme, size or disabled once on the group and every
direct child Button inherits it. The group hands
the props down during server render, so the sharing costs no client
JavaScript. An explicit prop on a child always wins, which is how the
"Publish" button above breaks out of its group's outline style.
Attached#
attached collapses the gap, the inner corner radii, and the doubled-up
borders between neighbors, producing a single segmented control. Classic uses:
toolbars and pagination.
"use client";
import { Button, ButtonGroup, Icon, VStack } from "astralis-ui";
import { AlignLeft, AlignCenter, AlignRight, ChevronLeft, ChevronRight } from "lucide-react";
export function ButtonGroupAttached() {
return (
<VStack gap="4" alignItems="start">
<ButtonGroup attached variant="outline" colorScheme="gray">
<Button leftIcon={<Icon as={AlignLeft} size="xs" />} aria-label="Align left" />
<Button leftIcon={<Icon as={AlignCenter} size="xs" />} aria-label="Align center" />
<Button leftIcon={<Icon as={AlignRight} size="xs" />} aria-label="Align right" />
</ButtonGroup>
<ButtonGroup attached variant="subtle">
<Button leftIcon={<Icon as={ChevronLeft} size="xs" />}>Previous</Button>
<Button rightIcon={<Icon as={ChevronRight} size="xs" />}>Next</Button>
</ButtonGroup>
</VStack>
);
}Orientation#
orientation="vertical" stacks the buttons. It composes with attached for
vertical segmented controls.
import { Button, ButtonGroup, HStack } from "astralis-ui";
export function ButtonGroupOrientation() {
return (
<HStack gap="8" alignItems="start">
<ButtonGroup orientation="vertical" variant="surface">
<Button>Profile</Button>
<Button>Settings</Button>
<Button>Sign out</Button>
</ButtonGroup>
<ButtonGroup orientation="vertical" attached variant="outline" colorScheme="gray">
<Button>Day</Button>
<Button>Week</Button>
<Button>Month</Button>
</ButtonGroup>
</HStack>
);
}Spacing#
When not attached, spacing controls the gap between buttons.
import { Button, ButtonGroup, Text, VStack } from "astralis-ui";
const spacings = ["none", "sm", "md", "lg"] as const;
export function ButtonGroupSpacing() {
return (
<VStack gap="5" alignItems="stretch">
{spacings.map((spacing) => (
<VStack key={spacing} gap="2" alignItems="stretch">
<Text as="span" size="xs" weight="medium" color="muted">
{spacing}
</Text>
<ButtonGroup spacing={spacing} variant="subtle" size="sm">
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
</VStack>
))}
</VStack>
);
}Disabled#
Disabling the group disables every button in it, handy while a form section is pending.
import { Button, ButtonGroup } from "astralis-ui";
export function ButtonGroupDisabled() {
return (
<ButtonGroup disabled variant="outline" colorScheme="gray">
<Button>Cut</Button>
<Button>Copy</Button>
<Button>Paste</Button>
</ButtonGroup>
);
}Props#
Button Group renders a div and accepts all its standard attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Lays the buttons out in a row or a column. |
attached | boolean | false | Welds the buttons into one segmented control: inner radii collapse and adjacent borders merge. spacing is ignored. |
spacing | "none" | "sm" | "md" | "lg" | "md" | Gap between buttons when not attached. |
variant | "solid" | "subtle" | "surface" | "outline" | "text" | "link" | None | Shared visual style pushed onto every child Button. |
colorScheme | all 15 schemes | None | Shared hue pushed onto every child Button. |
size | "xs" | "sm" | "md" | "lg" | "xl" | None | Shared size pushed onto every child Button. |
disabled | boolean | false | Disables every child Button at once. |
Accessibility#
- Renders with
role="group"by default; pass arole(e.g.toolbar) and anaria-labelto describe the set when the context isn't obvious. - Attached icon-only buttons still need individual
aria-labels. See the alignment toolbar demo.