Props
Usage guidelines
- Interrupting users to get confirmation on a user-triggered action that is potentially disruptive or significantly changes the user’s content and system.
- Interrupting users to alert them of potential issues and errors; this can be user or system-generated.
- Requesting large forms of information. Consider OverlayPanel or new page instead.
- Any action that should not interrupt users from their current work stream, such as saving a Pin. Use Toast instead.
- When alerting users of issues that can be corrected on the page or surface itself without interrupting their flow. Instead use BannerCallout or BannerSlim.
Best practices
Clearly communicate what response is expected and make the action simple and straightforward, such as clicking/tapping a button to confirm.
Limit the content to prevent the need to scroll at most screen sizes.
Provide a way for the user to correct an error or issue via a button or a link.
Explain to the user why they’ve encountered a warning or error when an action button or link is not possible.
Use language that makes it hard to understand what action is being taken, while adding additional actions that may take the user out of their existing context.
Use ModalAlert on top of another modal dialog. This can cause accessibility issues with focus states and make it hard for a user to escape and go back to the previous surface. On mobile surfaces, if a user has to confirm something triggered by a modal dialog, auto-dismiss the first dialog before presenting with the confirmation dialog.
Use ModalAlert for long and complex content or tasks, or for content that should have a dedicated surface, like login flows. If extra functionality is needed in an overlay, use Modal or OverlayPanel.
Leave it up to the user to find where to go to fix an issue.
Omit an explanation as to why a user is encountering an error or issue.
Accessibility
Labels
Make sure ModalAlerts have a clear purpose when being read by a screen reader by specifying an accessibilityModalLabel
that will update the spoken text for the heading prop and give the user more context about the ModalAlert. Also ensure the accessibilityLabel
is supplied when primaryAction
or secondaryAction
is specified. This label should provide a clear description of the action's purpose, like "Cancel board deletion".
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
Variants
Multiple actions for confirmation
This is generally triggered by user action and asks a user to confirm or cancel an action. Confirmation ModalAlerts should always have a primary and secondary button; the primary button is for confirming, and the secondary for dismissing the modal. Confirmations aren’t critical and can be dismissed by clicking outside of the modal and hitting the ESC key, in addition to using the “Cancel” buttons provided in the modal.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Delete current Pin draft confirmation" heading="Delete this draft?" onDismiss={() => {}} primaryAction={{ accessibilityLabel: 'Delete draft', label: 'Delete', onClick: () => {}, role: 'button', }} secondaryAction={{ accessibilityLabel: 'Cancel, keep editing', label: 'Return to editing', onClick: () => {}, role: 'button', }} > <Text> Deleting this draft cannot be undone. Are you sure you want to delete? </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
Single action for acknowledgment
This is system-generated and only requires a user to dismiss the message.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Unable to follow more people" heading="Follower limit reached" onDismiss={() => {}} primaryAction={{ accessibilityLabel: '', label: 'Got it', onClick: () => {}, role: 'button', }} type="warning" > <Text> You've hit a spam block and can't follow any more people right now. Try again later. </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
Warning type
Warnings are used to alert a user that they need to proceed with caution. Due to their critical nature, warnings can only be dismissed by interacting with the dismiss buttons provided by the modal. If there is a way to resolve the warning, two buttons can be included. If not, only one “dismiss” button is needed.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Spam link warning" heading="This site may lead to spam" onDismiss={() => {}} primaryAction={{ accessibilityLabel: 'Continue to Pin site', label: 'Continue to site', href: 'https://www.google.com', role: 'link', }} secondaryAction={{ accessibilityLabel: 'Cancel navigation to site', label: 'Cancel', onClick: () => {}, role: 'button', }} type="warning" > <Text> We aren't sure of the contents of this site and can't verify that you will find what you are looking for. Are you sure you want to continue? </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Unable to follow more people" heading="Follower limit reached" onDismiss={() => {}} primaryAction={{ accessibilityLabel: '', label: 'Got it', onClick: () => {}, role: 'button', }} type="warning" > <Text> You've hit a spam block and can't follow any more people right now. Try again later. </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
Error type
Error messages alert users of an error or a very critical issue that severely limits the user’s ability to continue. Like warnings, errors can only be dismissed by interacting with the dismiss buttons provided by the modal. If there is a way to resolve the error, two buttons can be included. If not, only one “dismiss” button is needed.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="API access revoked error" heading="Your API access has been revoked" onDismiss={() => {}} primaryAction={{ accessibilityLabel: 'Submit appeal to Pinterest', label: 'Submit an appeal', href: 'https://www.pinterest.com', role: 'link', }} secondaryAction={{ accessibilityLabel: 'Cancel', label: 'Cancel', onClick: () => {}, role: 'button', }} type="error" > <Text> You will not be able to make any API calls or create new apps. Pinterest Developers functionality will be limited to read-only data until you submit an appeal. </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Layer, Link, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Site blocked error" heading="Website blocked" onDismiss={() => {}} primaryAction={{ accessibilityLabel: 'Acknowledge site blocked', label: 'Got it', onClick: () => {}, role: 'button', }} type="error" > <Text> We blocked the website you are trying to reach because it contains harmful material. Review our{' '} <Link display="inlineBlock" href="https://policy.pinterest.com/en/community-guidelines" underline="always" > content policy. </Link> </Text> </ModalAlert> </Layer> ) : null} </Fragment> ); }
With checkbox
Checkbox can be added to a modal that isn’t a warning or an error. Checkboxes are normally used for confirmation modals that may appear frequently in a creation or editing flow. An example is creating an Idea Pin. If the action is infrequent or highly destructive (like deleting something), do not offer an option to not show the modal again.
import { Fragment, useState } from 'react'; import { Box, Button, Checkbox, CompositeZIndex, FixedZIndex, Flex, Layer, ModalAlert, Text, } from 'gestalt'; const HEADER_ZINDEX = new FixedZIndex(10); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); export default function Example() { const [showComponent, setShowComponent] = useState(true); const [checked1, setChecked1] = useState(false); return ( <Fragment> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer zIndex={zIndex}> <ModalAlert accessibilityModalLabel="Delete current Pin draft confirmation" heading="Delete this page?" onDismiss={() => {}} primaryAction={{ accessibilityLabel: 'Delete page', label: 'Delete page', onClick: () => {}, role: 'button', }} secondaryAction={{ accessibilityLabel: 'Cancel, keep page', label: 'Cancel', onClick: () => {}, role: 'button', }} > <Flex direction="column" flex="grow" gap={4}> <Text> If you change your mind, you'll have to create this pin again—starting from the very beginning. </Text> <Checkbox checked={checked1} id="checkbox-show-again" label="Got it—don't warn me again" onChange={({ checked }) => setChecked1(checked)} /> </Flex> </ModalAlert> </Layer> ) : null} </Fragment> ); }
Mobile
ModalAlert is responsive but not adaptive to mobile devices; therefore, it does not require DeviceTypeProvider.
import { useState } from 'react'; import { Box, Button, DeviceTypeProvider, Layer, ModalAlert, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); return ( <DeviceTypeProvider deviceType="mobile"> <Box padding={2}> <Button accessibilityLabel="Show Modal" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show Modal" /> </Box> {showComponent ? ( <Layer> <ModalAlert accessibilityModalLabel="Mobile ModalAlert example" heading="Heading" onDismiss={() => setShowComponent(false)} primaryAction={{ accessibilityLabel: 'Confirm delete board', label: 'Yes, delete', onClick: () => {}, role: 'button', }} secondaryAction={{ accessibilityLabel: 'Cancel board deletion', label: 'No, keep', onClick: () => {}, role: 'button', }} > <Box>{Array(100).fill(<Text>Content</Text>)}</Box> </ModalAlert> </Layer> ) : null} </DeviceTypeProvider> ); }
Writing
- Consider internationalization and how other languages may be constrained.
- Use concise language while making it clear what is expected of the user. If the desired action can be confused with “Cancel”, add “Yes,” to the action. For example “Yes, remove”, “No, keep”
- Pose a question in the headline that isn’t clear about the action being proposed, like “Are you sure?”
- Use lengthy, technical jargon or local idioms that will be hard to translate to other languages.
- Avoid exclamation marks unless the tone is celebratory; this is especially true when surfacing errors or warnings.
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. |
Related
Toast
Toast provides feedback shortly after a user interaction, like a confirmation that appears when a Pin has been saved. Unlike BannerUpsells and BannerSlims, toasts overlay Page content. They also automatically disappear after a certain amount of time without being dismissed by the user.
BannerCallout
BannerCallouts are used at the top-most level of a page to communicate highest-priority information that applies to the entire page or surface. BannerCallouts can be dismissed and are also actionable.
BannerSlim
BannerSlim conveys brief information related to a specific section of a page. The message can relay success, warning, error or general information.
Modal
A generic, customizable container for modals that aren’t used as alerts and need more functionality, like form fields.