Button group
PortedJoins a few related buttons into one control, most often a split button: an action next to a menu of related actions. Inner corners are square and neighbors share one edge. It is a group: give it an aria-label, and an icon-only menu trigger its own. For a row of separate controls with arrow-key focus, use Toolbar.
import { CaretDownIcon } from "@phosphor-icons/react";
import { Button } from "@etched/monet/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@etched/monet/dropdown";
import { ButtonGroup } from "@etched/monet/button-group";
export function Preview() {
return (
<ButtonGroup aria-label="Run tests">
<Button variant="primary">Run tests</Button>
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="primary" shape="square" aria-label="More run options" icon={<CaretDownIcon />} />} />
<DropdownMenuContent align="end">
<DropdownMenuItem>Run on bench A-03</DropdownMenuItem>
<DropdownMenuItem>Run failed tests only</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>
);
}Installation
import { ButtonGroup } from "@etched/monet/button-group";Examples
Secondary
Any look works; give every button in the group the same one.
import { CaretDownIcon } from "@phosphor-icons/react";
import { Button } from "@etched/monet/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@etched/monet/dropdown";
import { ButtonGroup } from "@etched/monet/button-group";
export function Secondary() {
return (
<ButtonGroup aria-label="Save">
<Button>Save</Button>
<DropdownMenu>
<DropdownMenuTrigger render={<Button shape="square" aria-label="More save options" icon={<CaretDownIcon />} />} />
<DropdownMenuContent align="end">
<DropdownMenuItem>Save as draft</DropdownMenuItem>
<DropdownMenuItem>Save and close</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>
);
}Sizes
Give every button in the group the same size.
import { CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
import { Button } from "@etched/monet/button";
import { ButtonGroup } from "@etched/monet/button-group";
export function Sizes() {
return (
<div className="row">
{(["sm", "base"] as const).map((size) => (
<ButtonGroup key={size} aria-label="Candidates">
<Button size={size} shape="square" aria-label="Previous candidate" icon={<CaretLeftIcon />} />
<Button size={size} shape="square" aria-label="Next candidate" icon={<CaretRightIcon />} />
</ButtonGroup>
))}
</div>
);
}With a field
A field and the button that acts on it.
import { Button } from "@etched/monet/button";
import { Input } from "@etched/monet/input";
import { ButtonGroup } from "@etched/monet/button-group";
export function WithAField() {
return (
<ButtonGroup aria-label="Invite">
<Input type="email" aria-label="Email" placeholder="name@etched.com" />
<Button>Invite</Button>
</ButtonGroup>
);
}Accessibility
- A
group. Each button in it is its ownTabstop; arrow keys do not move between them (useToolbarfor that). - A split button's caret is a menu trigger with
aria-expanded. - Name the group with
aria-label, such as "Run tests". Give an icon-only caret its ownaria-label, such as "More run options".