Button group
Related buttons joined into one control.
Left out of Content first Content first keeps one clear action per section, and alternatives are text links so they do not compete. Joined buttons work against that. Use instead: Button, Pagination
See it live
npm run storybookWhen to use it
- Use it when: A few actions that belong together, such as previous and next, or a split action.
- Not when: A choice that stays selected (toggle group), or unrelated actions.
Instead:
- Toggle group: The choice stays pressed.
- Dropdown menu: More actions than fit in a row.
Words
- Write to one person: "you", not "users".
- Short sentences with room to breathe.
- Explain why before asking for anything.
- Use: Read, Save for later, You, Next issue. Avoid: Click here, Submit, Users, Content.
The labels components use, from the voice tokens:
content.action.commit: Savecontent.action.cancel: Not nowcontent.action.destroy: Removecontent.action.retry: Try againcontent.action.empty-action: Browse guides
See Voice and tone for the whole voice.
Accessibility and keyboard
Test before you ship:
- The buttons are a group with a name. undefined
Use it in code
npx open-ds add button-group
The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.
Tokens it reads
Button group reads 9 tokens of its own, each an alias of a semantic token. Each row is one part: its own token, the semantic it aliases, and the value it ends at. Change a semantic and every component that reads it follows; override one of Button group's tokens to change Button group alone.
Content first decides what changes when you switch theme. Here a switch changes colors only, so the marked rows differ between light and dark; everything else holds steady.
| Part | Its token and the semantic it aliases | Value |
|---|---|---|
| Button group | ||
| Gap | component.button-group.gapspace.gap.defaultvar(--button-group-gap) | space.16:1rem |
| Text | ||
| Gap | component.button-group.text.gapspace.gap.defaultvar(--button-group-text-gap) | space.16:1rem |
| Corner | component.button-group.text.radiusradius.controlvar(--button-group-text-radius) | radius.8:0.5rem |
| Fill Changes with theme | component.button-group.text.backgroundcolor.surface.sunkenvar(--button-group-text-background) | Lightstone.light.100:#F6F2EADarkstone.dark.100:#2E2A24 |
| Padding, left and right | component.button-group.text.padding-xcomponent-spacing-map.x4var(--button-group-text-padding-x) | space.24:1.5rem |
| Text size | component.button-group.text.font-sizetype.body.font-sizevar(--button-group-text-font-size) | font.size.18:1.125rem |
| Weight | component.button-group.text.font-weighttype.label.font-weightvar(--button-group-text-font-weight) | font.weight.600:600 |
| Icon size | component.button-group.text.icon-sizesize.iconvar(--button-group-text-icon-size) | size.16:1rem |
| Separator | ||
| Fill Changes with theme | component.button-group.separator.backgroundcolor.border.strongvar(--button-group-separator-background) | Lightstone.light.500:#7A7062Darkstone.dark.400:#A39A8B |
| From the system | ||
| Edge Changes with theme | color.border.default straight from the system, through --border | Lightstone.light.200 · #E4DDD0Darkstone.dark.100 · #2E2A24 |