<ButtonGroup>
<Button primary>Save</Button>
<Button>Discard</Button>
</ButtonGroup>
Combination Best Practices
The following is a general recommendation on pairing buttons of various hierarchy. This refers to Buttons that are next to each other, not across a screen. The goals can be described as:
- Preserving visual prominence of important actions, while reducing visual noise for less important actions.
- Bringing general consistency of style across the app.
These are combinations found commonly throughout the app.
const options = [
{
example: (
<ButtonGroup>
<Button primary>Add Technician</Button>
<Button primary outline>
Edit Technician
</Button>
</ButtonGroup>
),
description:
'A single primary action with one or more secondary actions',
},
{
example: (
<ButtonGroup>
<Button primary>Submit</Button>
<Button>Cancel</Button>
</ButtonGroup>
),
description:
'A single primary action with one or more tertiary actions',
},
{
example: (
<ButtonGroup>
<Button outline primary>
View Job
</Button>
<Button fill="subtle" primary>
Undo Estimate
</Button>
</ButtonGroup>
),
description: 'Secondary actions with subtle blue actions',
},
{
example: (
<ButtonGroup>
<Button outline>Clone</Button>
<Button iconName="more_vert" fill="subtle" />
</ButtonGroup>
),
description: 'Alternative tertiary actions with subtle gray actions',
},
{
example: (
<ButtonGroup>
<Button>Edit</Button>
<Button iconName="more_vert" fill="subtle" />
</ButtonGroup>
),
description: 'Tertiary actions with subtle gray actions',
},
{
example: (
<ButtonGroup>
<Button>Reschedule</Button>
<Button>Assign</Button>
</ButtonGroup>
),
description: 'Any non-primary action style can be paired with itself',
},
];
const Example01 = () =>
<Stack direction="column">
{options.map((option, index) =>
<Stack.Item key={index}>
{index > 0 && <Divider spacing={3} />}
<Stack alignItems="center" spacing={4} wrap="wrap">
<div style={{ minWidth: '400px' }} className="d-f">
{option.example}
</div>
{option.description && (
<BodyText
subdued
style={{ minWidth: '340px', maxWidth: '340px' }}
className="m-y-1"
>
{option.description}
</BodyText>
)}
</Stack>
</Stack.Item>
)}
</Stack>
render (Example01);
While some pairings visually work, we recommend against these in order to bring more
consistency across the app.
const options = [
{
example: (
<ButtonGroup>
<Button primary>Add Technician</Button>
<Button primary>Edit Technician</Button>
</ButtonGroup>
),
description: 'Avoid multiple primary actions',
},
{
example: (
<ButtonGroup>
<Button primary>Submit</Button>
<Button fill="subtle" primary>
Cancel
</Button>
</ButtonGroup>
),
description:
'Avoid placing subtle and primary actions next to each other',
},
{
example: (
<ButtonGroup>
<Button outline primary>
View Job
</Button>
<Button>Undo Estimate</Button>
</ButtonGroup>
),
description:
'Secondary actions and gray colored actions do not pair well visually',
},
{
example: (
<ButtonGroup>
<Button outline primary>
Clone
</Button>
<Button fill="subtle">Edit</Button>
</ButtonGroup>
),
description:
'Secondary actions and gray colored actions do not pair well visually',
},
{
example: (
<ButtonGroup>
<Button outline>Clone</Button>
<Button fill="subtle" primary>
Edit
</Button>
</ButtonGroup>
),
description:
'Tertiary actions and subtle blue actions do not pair well visually',
},
];
const Example02 = () =>
<Stack direction="column">
{options.map((option, index) => {
return (
<Stack.Item key={index}>
{index > 0 && <Divider spacing={3} />}
<Stack alignItems="center" spacing={4} wrap="wrap">
<div style={{ minWidth: '400px' }} className="d-f">
{option.example}
</div>
{option.description && (
<BodyText
subdued
style={{ minWidth: '340px', maxWidth: '340px' }}
className="m-y-1"
>
{option.description}
</BodyText>
)}
</Stack>
</Stack.Item>
);
})}
</Stack>
render (Example02);
Caution with combinations
const options = [
{
example: (
<ButtonGroup>
<Button primary>Book Job</Button>
<Button outline primary>
Build Invoice
</Button>
<Button fill="subtle" primary>
Build Estimate
</Button>
</ButtonGroup>
),
description:
"Within a group, it's preferred to have 1–2 button styles over 3",
},
{
example: (
<ButtonGroup>
<Button primary outline>
Add Material
</Button>
<Button negative primary outline>
Delete Material
</Button>
</ButtonGroup>
),
description:
"Destructive actions can be in a group, but it's better to isolate them",
},
{
example: (
<ButtonGroup>
<Button primary fill="subtle">
Duplicate
</Button>
<Button primary fill="subtle">
Find
</Button>
</ButtonGroup>
),
description: 'In a group, tertiary actions may be a better option',
},
];
const Example03 = () =>
<Stack direction="column">
{options.map((option, index) => {
return (
<Stack.Item key={index}>
{index > 0 && <Divider spacing={3} />}
<Stack alignItems="center" spacing={4} wrap="wrap">
<div style={{ minWidth: '400px' }} className="d-f">
{option.example}
</div>
{option.description && (
<BodyText
subdued
style={{ minWidth: '340px', maxWidth: '340px' }}
className="m-y-1"
>
{option.description}
</BodyText>
)}
</Stack>
</Stack.Item>
);
})}
</Stack>
render (Example03);
Attached Groups
Attached Button Groups are used for horizontal bars of buttons, like toolbars, and group related actions.
<ButtonGroup attached>
<Button>Filter</Button>
<Button>Sort</Button>
<Button>Refresh</Button>
</ButtonGroup>
Button Groups can be used to attach dropdown menus to Primary Action buttons.
<ButtonGroup attached>
<Button primary>Add Customer</Button>
<Button primary iconName="expand_more" />
</ButtonGroup>
In space-constrained layouts, attached Button Groups will allow actions to live within a smaller footprint.
<ButtonGroup attached>
<Button primary>Add Customer</Button>
<Button outline primary>
Add Technician
</Button>
<Button outline primary>
Add Contractor
</Button>
</ButtonGroup>
Using text buttons in a Button Group evenly spaces the buttons in content-heavy sections like tables.
<div>
<Headline className="m-t-1 m-b-2">Employees</Headline>
{['Lisbeth Anglin', 'Etta Twiford', 'Renea Finlayson'].map(
(name, index) => (
<div key={index}>
<Divider spacing={1} />
<Stack alignItems="center" justifyContent="space-between">
<BodyText>{name}</BodyText>
<ButtonGroup attached>
<Button primary fill="subtle">
View
</Button>
<Button primary fill="subtle">
Edit
</Button>
</ButtonGroup>
</Stack>
</div>
)
)}
</div>
Full Width Groups
Full width Button Groups are used to fill the space within a small content area like a Card or Modal.
<Card>
<ButtonGroup attached fullWidth>
<Button fill="subtle" primary>
Small
</Button>
<Button fill="subtle" primary>
Medium Size
</Button>
<Button fill="subtle" primary>
Long Button Name
</Button>
</ButtonGroup>
</Card>
With solid and outline buttons, it's recommended to set the buttons to equal width.
<Card>
<ButtonGroup attached fullWidth equalWidth>
<Button>Small</Button>
<Button primary>Long Button Name</Button>
</ButtonGroup>
</Card>
Best Practices
A Button Group should:
- Group together actions that are related
- Not have too many buttons to cause user indecision
- Not have too many buttons to fit on small devices
- For an individual button, use a Button.
Importing
import { ButtonGroup } from '@servicetitan/design-system';