Maturity
CTA Button
Action Button
Related content
Overview
The previous version of our Buttons was plagued with inconsistencies due to a lack of definition and maintenance, which cascaded badly in the code.
Benefits
- 1:1 parity design/development.
- Simplified properties and usage
- On the design side, we simplified the rules and styling, all available via semantic tokens.
- On the code side, we removed properties that should belong to the parent container, hardcoded values, and magic numbers.
- Better accessibility, with holistic interaction states, colors, and more.
How to migrate
containerStyle
The prop containerStyle has been removed and no longer needed.
ctype
The ctype prop has been removed. It has been replaced by two separate buttons, CTAButton and ActionButton, as well as the emphasis prop.
In most cases there is a direct translation between the previous ctype and the new buttons.
Before |
After |
---|---|
bordered |
<ActionButton> |
borderedMuted |
<ActionButton> |
danger |
<CTAButton destructive> |
flatBlue |
<ActionButton> |
flatGrey |
<ActionButton> |
floating |
<CTAButton> |
golden |
<CTAButton> |
but there are some situations to take into account.
If a Button is a companion to another Button that should be a CTAButton, use emphasis="tertiary".
// Before
<Flex gap="16px">
<Button size="m" ctype="bordered" onClick={handleCancel}>
Cancel
</Button>
<Button size="m" onClick={handleDelete}>
Delete
</Button>
</Flex>
// After
<Flex gap="16px">
<CTAButton destructive size="MD" onClick={handleDelete}>
Delete
</CTAButton>
<CTAButton size="MD" emphasis="tertiary" onClick={handleCancel}>
Cancel
</CTAButton>
</Flex>
Note that this example also changes the arrangement of the buttons.
If a Button is supposed to be a ActionButton but there is a need for less emphasis, like a Menu Button, use emphasis="tertiary".
When migrating a Button to an ActionButton, make sure to add a related icon as well.
For more information see our Button Guidelines documentation.
disabledTip
The disabledTip prop has been removed as Tooltips should be used to clarify the functionality of a button and not to describe why something is disabled or has an error. Instead use a
loading
The loading prop has renamed to waiting as it better describes what is occurring.
In some cases the button might be waiting for an action to finish like deleting or moving something. And loading implies data transfer.
There is also no need to disable a button when it’s in it’s waiting state, as this is handle automatically.
In addition a button in waiting state will add a spinner and update the accessible label to be Waiting for {action}, so the label can be left alone as well.
// Before
<Button
disabled={isLoading}
loading={isLoading}
onClick={onConfirm}
ctype="danger"
>
{isLoading ? 'Deleting...' : 'Delete'}
</Button>
// After
<CTAButton
waiting={isLoading}
onClick={onConfirm}
destructive
>
Delete
</CTAButton>
iconPosition
The prop iconPosition has been removed as icons are always consistently at the start of ActionButton.
margin, my, mx, mt, mr, mb, ml, etc.
Margin related props have been removed. Either migrate to wrapping with a Flex or Box with the related margin props or move to using a Flex with gap.
onMouseEnter, onMouseLeave, etc
Instead of using onMouse* even handlers, use the equivalent onPointer* event handler instead. As pointer events handle all different input types (touch, pen, mouse, etc).
// Before
<Button onMouseEnter={handleMouseEnter}>New project</Button>
// Before
<ActionButton onPointerEnter={handleMouseEnter}>New project</ActionButton>
padding, py, px, pt, pr, pb, pl, etc.
Padding related props have been removed. In most cases you shouldn’t need to change the padding of the new buttons. If you do happen to run into a case where you would need to, let us know.
size
The size prop has had it’s value updated to have a consistent naming pattern of t-shirt sizes.
Before |
After |
---|---|
sm |
SM |
m |
MD |
lg |
MD |
// Before
<Button
size="m"
onClick={createMazeHandler}
>
New maze
</Button>
// After
<CTAButton
size="MD"
onClick={createMazeHandler}
>
New maze
</CTAButton>
Arrangement
For complementary buttons, make sure to follow the F pattern.
// Before
<Flex gap="16px">
<Button size="m" ctype="bordered" onClick={handleCancel}>
Cancel
</Button>
<Button size="m" onClick={handleDelete}>
Delete
</Button>
</Flex>
// After
<Flex gap="16px">
<CTAButton destructive size="MD" onClick={handleDelete}>
Delete
</CTAButton>
<CTAButton size="MD" emphasis="tertiary" onClick={handleCancel}>
Cancel
</CTAButton>
</Flex>
For more information, see our documentation on Button arrangement.
Destructive Actions
If the Button is triggering a destructive action, be sure to add the destructive prop to it.
// Before
<Button onClick={handleDelete}>
Delete
</Button>
// After
<CTAButton destructive onClick={handleDelete}>
Delete
</CTAButton>
Icon Buttons
Previously we were building ad-hoc solutions for Icon Only buttons, but now this is built directly into ActionButton.
Make sure to add the iconOnly prop as well as give the ActionButton an accessible label.
// Before
const IconButton = styled.button`
cursor: pointer;
border: none;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background-color: transparent;
&:hover {
background-color: ${colors.neutral100};
color: ${colors.red500};
}
& ${Icon} {
&:hover {
color: ${colors.red500};
}
}
`;
<IconButton onClick={onButtonClick}>
<Icon name="trash-can" fontSize="24px" color={colors.neutral500} />
</IconButton>
// After
<ActionButton icon='trash-can' iconOnly onClick={onButtonClick}>
Delete
</ActionButton>
Note that in this example Delete will act as an accessible label and not visually show because off the iconOnly prop.
Real Word Examples
TextSection
// Before
<Flex mt="18px" alignItems="center" justifyContent="flex-end">
<Box mr="16px">
<Button size="m" ctype="bordered" onClick={() => setShowDeleteModal(false)}>
Cancel
</Button>
</Box>
<Button size="m" onClick={handleDelete} data-e2e="delete-text-section-button">
Delete
</Button>
</Flex>
// After
<Flex mt="18px" gap="16px" alignItems="center" justifyContent="flex-end">
<CTAButton emphasis="tertiary" size="MD" onClick={() => setShowDeleteModal(false)}>
Cancel
</CTAButton>
<CTAButton destructive size="MD" onClick={handleDelete} data-e2e="delete-text-section-button">
Delete
</CTAButton>
</Flex>
DeleteButton
// Before
const IconButton = styled.button`
cursor: pointer;
border: none;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background-color: transparent;
&:hover {
background-color: ${colors.neutral100};
color: ${colors.red500};
}
& ${Icon} {
&:hover {
color: ${colors.red500};
}
}
`;
<IconButton onClick={onButtonClick}>
<Icon name="trash-can" fontSize="24px" color={colors.neutral500} />
</IconButton>
// After
<ActionButton icon='trash-can' iconOnly onClick={onButtonClick}>
Delete
</ActionButton>
Let Us Know
For cases where you are not easily able to migrate Button to CTAButton/ActionButton or Text is missing a property you need, let us know in #design-system.