Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

SplitButton

A primary action with a menu of related ones beside it: two halves of one pill, each its own button.

import { SplitButton } from "@butteryui/react";

Demo

Nothing exported yet

When to use it

Use it for

  • One main action with a few close alternatives: Export, with Export as JPG and Copy to clipboard
  • The footer of an inspector or a dialog where the main action has variants

Not for

  • Actions that are equally likely: show them as separate buttons
  • More than six alternatives, or settings: use a menu or a dialog

Examples

An inspector's footer: Copy beside a split Export whose menu holds the other formats.

Export

Quality90%
Last export
Not exported yet

Export options

Export as the main action, with other formats in the menu.

Nothing exported yet

Publish later

Publish now, or pick a later time from the menu: both halves stay disabled until the deck has a title, and one entry is unavailable.

Draft

Show new option

The menu's open state kept in the example, so a tip can open it to point out a new entry.

New: copy to the clipboard.

Nothing exported yet

Variants and states

Main action and alternatives

Each half is its own button: Tab moves between them, and the chevron opens the menu.

Nothing yet

Disabled

Props

SplitButton

PropTypeDefaultDescription
children

required

ReactNodeThe main half's label.
onClickMouseEventHandler<HTMLButtonElement>The main action.
items

required

readonly SplitButtonItem[]The related actions in the menu.
menuLabel

required

stringNames the menu half for screen readers, such as "More export options".
disabledbooleanDisables both halves.
openbooleanWhether the menu is open. Pair with onOpenChange to control it.
defaultOpenbooleanfalseWhether the menu starts open when uncontrolled. Defaults to false.
onOpenChange((open: boolean) => void)Called when the menu opens or closes.

It also takes Omit<ComponentPropsWithRef<"div">, "onClick">.

SplitButtonItem

One entry in a `SplitButton`'s menu.

FieldTypeDefaultDescription
label

required

stringWhat the entry says.
onSelect

required

() => voidRuns when the entry is chosen. The menu closes afterwards.
iconComponentType<IconComponentProps>An icon before the label.
disabledbooleanKeeps the entry from being chosen.

Accessibility

A group of two buttons, each a tab stop: the main half is named by its label, the chevron half by menuLabel. The chevron opens a menu: arrow keys move, Enter chooses, Escape closes and returns focus.

KeysWhat they do
TabMoves between the main half and the chevron half, each its own stop
Enter or SpaceRuns the main action on the main half, or the focused entry in the menu, which then closes
Enter, Space or Arrow DownOpens the menu from the chevron half, with focus on its first entry
Arrow Down or Arrow UpMoves between the menu's entries, skipping disabled ones
Home or EndMoves to the first or last entry
LettersJumps to the entry that starts with them
EscapeCloses the menu and puts focus back on the chevron half

Motion

It scales to 97% while it's pressed, over `--bt-dur-press`, and springs back when it's let go.

With reduced motion: Each half presses in at once, and the menu fades in and out over 100ms without growing.

Layout

One pill --bt-h-button (36px) tall: the main half as wide as its label and padding, then a 1px divider half the pill's height, then the chevron half, a 36px square. It's never wider than its container, where a long label truncates with an ellipsis and shows in full as its title. Each half is a hit area the pill's full height, and the menu is at least --bt-w-menu-min (200px) wide.