Props
Usage guidelines
- Communicating an action that will occur.
- Triggering or enabling an action, such as submitting requested information.
- Progressing or regressing a user through a step in a flow.
- Directing users to a new page or different part within the same page. Instead, use Link.
- Limited space available. Consider using an IconButton instead.
Best practices
Place primary Buttons to the right or top of other Button styles.
Place more than one primary Button per container/area.
Show the full text on Buttons. Buttons should be stacked when they cannot be displayed side by side.
Truncate the Button text. In rare instances where Buttons must remain on one line, truncate the text on the secondary Button before truncating on the primary Button.
Keep the Button text as simple and actionable as possible. Refer to the Button writing guidelines for more detail. If text is not sufficient for accessibility, refer to Accessibility guidelines for more detail.
Do not add icons to a Button to reinforce the text.
Use an IconButton + Tooltip next to the disabled Button if you need to explain why it is disabled.
Use a Tooltip on disabled Button, as it is not accessible for keyboard and screen reader users.
Accessibility
ARIA attributes
When Button text does not provide sufficient context about the Button’s behavior, supply a short, descriptive label for screen-readers using accessibilityLabel
.
Texts like “Click here“, “Follow“, or “Shop“ can be confusing when a screen reader reads them out of context. In those cases, we must pass an alternative text with deeper context to replace the Button text, like “Follow Ryan” or “Shop Wedding Invitations”.
If Button is used as a control Button to show/hide a Popover-based component, we recommend passing the following ARIA attributes to assist screen readers:
accessibilityLabel
: if present, read by screen readers read instead of thetext
prop.accessibilityControls
: informs the screen reader that Button controls the display of an anchored Popover-based component. It populates aria-controls.accessibilityHaspopup
: informs the screen reader that there’s a Popover-based component attached to Button. It populates aria-haspopup.accessibilityExpanded
: informs the screen reader whether the button-anchored Popover-based component is currently open or closed. It populates aria-expanded.
import { useRef, useState } from 'react'; import { Box, Button, Dropdown } from 'gestalt'; export default function ActionDropdownExample() { const [open, setOpen] = useState(false); const [selected, setSelected] = useState(null); const anchorRef = useRef(null); const onSelect = ({ item }) => setSelected(item); return ( <Box display="flex" justifyContent="center" padding={8} width="100%"> <Button ref={anchorRef} accessibilityControls="action-variant-dropdown-example" accessibilityExpanded={open} accessibilityHaspopup iconEnd="arrow-down" onClick={() => setOpen((prevVal) => !prevVal)} selected={open} size="lg" text={selected ? selected.label : 'Display'} /> {open && ( <Dropdown anchor={anchorRef.current} id="action-variant-dropdown-example" onDismiss={() => setOpen(false)} > <Dropdown.Item onSelect={onSelect} option={{ value: 'Cozy', label: 'Cozy' }} selected={selected} /> <Dropdown.Item onSelect={onSelect} option={{ value: 'Comfy', label: 'Comfy' }} selected={selected} /> </Dropdown> )} </Box> ); }
Color contrast in disabled state
Disabled Buttons do not need to pass color contrast guidelines.
From w3.org, 1.4.3 Contrast (Minimum): Text or images of text that are part of an inactive user interface component, that are pure decoration, that are not visible to anyone, or that are part of a picture that contains significant other visual content, have no contrast requirement.
Our current disabled Button implementation does fail to pass color contrast on accessibility integration tests. To exclude disabled buttons from the integration tests we recomment conditionally setting a data-test-id={ isDisabled ? "disabled-button-<name>" : undefined }
and excluding them from the integration test.
On
cypress-axe that can be achieved with cy.a11yCheck({ exclude: [['[data-test-id="disabled-button-submit"]']] })
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
Avoid truncating Button text whenever possible. Refer to the Button usage guidelines for more information
Variants
State
- Default
The typical state of a Button that represents it can be interacted with and is not in a selected state. - Disabled
Used to block user interaction such as hover, focus and click. Disabled Buttons are completely unreachable by a keyboard and screenreader, so do not attach Tooltips to disabled Buttons. - Selected
When Button is used to toggle a boolean state or control the visibility of other elements (e.g. Dropdown), use theselected
prop to indicate the current state.
import { Button, ButtonGroup, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <ButtonGroup> <Button accessibilityLabel="Save" color="red" selected={false} size="lg" text="Save" /> <Button accessibilityLabel="Follow" selected={false} size="lg" text="Follow" /> </ButtonGroup> </Flex> ); }
import { Button, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Button accessibilityLabel="Submit" disabled size="lg" text="Submit" />{' '} </Flex> ); }
import { Button, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Button accessibilityLabel="Following" selected size="lg" text="Following" /> </Flex> ); }
Size
Button is available in 3 fixed sizes. The Button text has always a fixed size of 16px:
lg
(48px)
Large is the only size that should be used on Pinner surfaces.md
(40px)
Medium is used on more dense UI such as business surfaces or internal tools.sm
(32px)
Small should be used sparingly and only in places where the UI is very dense.
Color
On white backgrounds
- Red (Primary)
High emphasis, used for primary actions. - Gray (Secondary)
Medium emphasis, used for secondary actions. - Transparent (Tertiary)
Low emphasis when placed on dark/image backgrounds, used for tertiary actions in that context. Note, this treatment should be used with caution as it has potential color contrast issues.
On color/image backgrounds
- White (Primary)
High emphasis when placed on color/image backgrounds, used for primary actions in that context. - Semi-transparent white (Secondary)
Medium emphasis when placed on color/image backgrounds, used for secondary actions in that context.
import { Box, Button, Flex, Text } from 'gestalt'; export default function Example() { const bgColors = { gray: 'default', red: 'default', blue: 'default', transparent: 'default', transparentWhiteText: 'tertiary', }; return ( <Box padding={4}> <Flex gap={6} height="100%" width="100%" wrap> {['gray', 'red', 'blue', 'transparent', 'transparentWhiteText'].map( (color) => ( <Flex key={color} direction="column" gap={2}> <Box alignItems="center" borderStyle="sm" color={bgColors[color]} display="flex" height={200} justifyContent="center" rounding={4} width={200} > <Button backgroundContext={ color === 'transparentWhiteText' ? 'dark' : 'light' } color={color} size="lg" text="Save" /> </Box> <Text size="200" weight="bold"> color="{color}" </Text> </Flex> ) )} </Flex> </Box> ); }
import { Box, Button, Flex, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" gap={6} height="100%" justifyContent="center" width="100%" wrap > {['semiTransparentWhite', 'transparentWhiteText', 'white'].map( (color) => ( <Flex key={color} direction="column" gap={2}> <Box alignItems="center" borderStyle="sm" dangerouslySetInlineStyle={{ __style: { backgroundImage: 'url("https://i.ibb.co/d0pQsJz/stock3.jpg")', }, }} display="flex" height={200} justifyContent="center" rounding={4} width={200} > <Button color={color} size="lg" text="Save" /> </Box> <Text size="200" weight="bold"> color="{color}" </Text> </Flex> ) )} </Flex> ); }
Width
- Inline (default)
Inline is our default Button width. The width of an inline Button is based on the length of its text. Use in most cases where you need a Button. - Full-width (
fullWidth
)
Full-width Buttons can be used in narrower content areas when the text in the Button is close to full width in the content area. This is especially common to see in components such as BannerCallout and BannerUpsell at their smaller breakpoints.
import { Box, Button } from 'gestalt'; export default function Example() { return ( <Box direction="column" display="flex" justifyContent="center" margin={4} width="100%" > <Box margin={2} width="100%"> <Button color="red" fullWidth={false} iconEnd="arrow-down" onClick={({ event }) => event.preventDefault()} size="lg" text="Inline Button" /> </Box> <Box margin={2} width="100%"> <Button color="red" fullWidth iconEnd="arrow-down" onClick={({ event }) => event.preventDefault()} size="lg" text="Full-width Button" /> </Box> </Box> ); }
Icons
iconEnd
adds an icon after the Button text, and iconStart
adds an icon before. Icons should only be used to visually reinforce a specific function or interaction of the Button. Menus and external links are a common use case. Use visit
when linking to an external URL or arrow-down
when displaying a Popover on click. Note that icons on Button are not accessible to screen readers.
import { Button, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Button accessibilityLabel="Notifications" iconEnd="arrow-down" iconStart="bell" size="lg" text="Notifications" /> </Flex> ); }
Focus style
import { Box, Button, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Box height={150} width={150}> {' '} <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Button accessibilityLabel="Save" backgroundContext="light" color="red" selected={false} size="lg" text="Save" /> </Flex> </Box> <Box color="inverse" height={150} width={150}> <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Button accessibilityLabel="Follow" backgroundContext="dark" selected={false} size="lg" text="Follow" /> </Flex> </Box> </Flex> ); }
Writing
- If your object is already described on the screen, Buttons only need a verb (Example: Save).
- If your object isn’t described on the screen, Buttons need a verb + the object (Example: Create Pin).
- Use fewer than 3 words.
- Use sentence case.
- Do not use punctuation.
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Ready | Component is available in Figma for web and mobile web. |
Responsive Web | Ready | Component responds to changing viewport sizes in web and mobile web. |
Internal documentation
Related
ButtonLink
Use ButtonLink when a link is needed instead of an action.
ButtonGroup
When displaying multiple Buttons in a layout, use ButtonGroup to ensure consistent spacing and wrapping behavior.
IconButton
Use IconButton when only an icon is needed instead of text.
TapArea
Use TapArea to make non-button elements interactive, like an Image. This ensures the element interaction is accessible and uses Gestalt styles.
Tabs
Tabs are intended for page-level navigation between multiple URLs.