SheetMobile is a mobile only component. It is not used in desktop experiences.
SheetMobile is a supplementary container that sits on top of the screen’s primary content and can be dismissed in order to interact with the underlying content. Sheets can contain a wide variety of information and layouts, including menu items, actions, and supplemental content.
also known as Panel, Dialog, Drawer, Tray
Props
Usage guidelines
- Use a partial sheet when the content of sheet compliments the content of the primary screen behind.
- Use a full sheet when the content of the sheet does not need to reference the primary screen behind. Usually serving as a lightweight way to complete actions or move through a flow.
- If possible, similar content should remain on a primary screen. Only use SheetMobile if the content is a unique experience to the primary screen and/or to reference its content.
- To present binary or blocking decisions. Use an ModalAlert or Modal instead.
- For a temporary message. Use Toast instead.
Best practices
- Always include a collapse affordance. SheetMobile should close when users press the dismiss icon button, a cancel/close button, when swiped away or when users tap the area outside the partial sheet.
- Include a grabber for partial sheets. This provides a visual indicator of resizability and allows screen reader users to resize the sheet.
- Include a header title as it adds context to the task. Headers can be either center or start aligned, but should remain consistant throughout a flow.
- Have more than two buttons (primary and secondary) in the footer of the sheet. This prevents unclear hierarchy and crowding on mobile screens. Footers should be simple and provide clear actions for the user.
- Remove the wash behind the partial SheetMobile. The wash separates the sheet content from the primary content and allows for better focus and accessibility.
- Display more than one sheet at a time or overlay sheets. For transitions and navigation within sheets, view the Interactions and transitions section below.
- Truncate header text. Headers should be no more than two lines of text. If they are, consider revising the content.
Accessibility
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DefaultLabelProvider, DeviceTypeProvider, FixedZIndex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DefaultLabelProvider labels={{ SheetMobile: { accessibilityDismissButtonLabel: 'Das untere Blatt ist zu verwerfen', accessibilityGrabberLabel: 'Greifer', accessibilityLabel: 'Unteres Blatt.', }, }} > <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile heading="Beginnen Sie jetzt mit der Erstellung." onDismiss={() => setShowComponent(false)} showDismissButton subHeading="Die Inspiration beginnt hier." > <Text>Inhalt</Text> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Auf Pinterest erstellen." color="red" onClick={() => setShowComponent(true)} size="lg" text="Erstellen." /> </Box> </DeviceTypeProvider> </DefaultLabelProvider> ); }
Subcomponents
DismissingElement
DismissingElement is a render props component that provides access to the callback function onDismissStart
. onDismissStart
triggers the exit-animation from external trigger points in a component. Internal trigger points are pressing ESC
key, built-in dismiss buttons, and clicking outside the component. Use DismissingElement when external elements to the component, such as header, footer or any content element require dismissing the animated component.
DismissingElement Props
Variants
Size
SheetMobile is used only on mobile web experiences.
Use a partial SheetMobile when the content of the sheet compliments the content of the primary screen behind. Partial SheetMobiles have a 40% wash behind the sheet. This allows users to view content but not interact. Tapping the scrim behind the sheet will dismiss the sheet and the wash by default.
There are two size variants for partial SheetMobiles: "default" and "auto". See examples below for more details.
Use a full SheetMobile when the content of the sheet does not need to reference the primary screen behind. Usually serving as a lightweight way to complete actions or move through a flow.
There is one size variant for full Sheets: "full". See examples below for more details.
A partial SheetMobile with a fix height of 50% of the screen height. When size
is set to "default", SheetMobile doesn't require a visible dismiss IconButton.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile footer={ <Flex gap={2} justifyContent="center"> <Button color="gray" text="Secondary" /> <Button color="red" text="Primary" /> </Flex> } heading="Heading" onDismiss={() => setShowComponent(false)} primaryAction={{ accessibilityLabel: 'Next page', label: 'Next', onClick: () => {}, }} > <Box> {Array(100).map((number, index) => { const key = `example${index}`; return <Text key={key}>Content</Text>; })} </Box> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
A partial SheetMobile with a max of 90% and a min of 30% screen height. When size
is set to "auto", SheetMobile doesn't require a visible dismiss IconButton.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile footer={ <Flex gap={2} justifyContent="center"> <Button color="gray" text="Secondary" /> <Button color="red" text="Primary" /> </Flex> } heading="Heading" onDismiss={() => setShowComponent(false)} primaryAction={{ accessibilityLabel: 'Next page', label: 'Next', onClick: () => {}, }} size="auto" > <Box> {Array(100).map((number, index) => { const key = `example${index}`; return <Text key={key}>Content</Text>; })} </Box> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
A full SheetMobile fully fills the page. It completely covers the primary screen. When size
is set to "full", SheetMobile requires a visible dismiss IconButton.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile footer={ <Flex gap={2} justifyContent="center"> <Button color="gray" onClick={() => setShowComponent(false)} text="Secondary" /> <Button color="red" onClick={() => setShowComponent(false)} text="Primary" /> </Flex> } heading="Heading" onDismiss={() => setShowComponent(false)} primaryAction={{ accessibilityLabel: 'Next page', label: 'Next', onClick: () => setShowComponent(false), }} size="full" subHeading="SubHeading" > <Box> {Array(100).map((number, index) => { const key = `example${index}`; return <Text key={key}>Content</Text>; })} </Box> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Header
SheetMobile's header has a flexible configuration.
- It requires a
heading
prop and an optionalsubHeading
. - It can display navigation buttons (back and forward navigation) using the
backIconButton
andforwardIconButton
. - It can display a dismiss button (
showDismissButton
prop) as well as a primary action button (primaryAction
prop). - It can display either a center-aligned or start-aligned header.
See the following cases for reference.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Icon, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile align="center" heading="Start creating now" onDismiss={() => setShowComponent(false)} showDismissButton={false} size="auto" subHeading="Inspiration starts here" > <Flex alignItems="center" gap={12} height="100%" justifyContent="center" > <Flex alignItems="center" direction="column" gap={1}> <Box alignItems="center" color="secondary" display="flex" height={50} justifyContent="center" rounding={4} width={50} > <Icon accessibilityLabel="Pin" color="default" icon="pin" /> </Box> <Text size="100" weight="bold"> Pin </Text> </Flex> <Flex alignItems="center" direction="column" gap={1}> <Box alignItems="center" color="secondary" display="flex" height={50} justifyContent="center" rounding={4} width={50} > <Icon accessibilityLabel="Pin" color="default" icon="board" /> </Box> <Text size="100" weight="bold"> Board </Text> </Flex> </Flex> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, IconButtonLink, Layer, SheetMobile, TextField, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile align="center" footer={ <Flex gap={2} justifyContent="between"> <IconButtonLink accessibilityLabel="This IconButton is an example of IconButton acting as a link" href="https://www.pinterest.com" icon="share" target="blank" tooltip={{ text: 'Link example' }} /> <Flex gap={2}> <Button color="gray" text="Secondary" /> </Flex> <IconButtonLink accessibilityLabel="This IconButton is an example of IconButton acting as a link" href="https://www.pinterest.com" icon="ellipsis" target="blank" tooltip={{ text: 'Link example' }} /> </Flex> } heading="Create a new personal account" onDismiss={() => setShowComponent(false)} primaryAction={{ accessibilityLabel: 'Next page', label: 'Next', onClick: () => {}, }} showDismissButton={false} > <TextField autoComplete="username" id="header-example" label="Username" onChange={() => {}} placeholder="Please enter your username" type="text" /> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Optional in partial SheetMobiles but required in full variants.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile heading="Heading" onDismiss={() => setShowComponent(false)} > <Text>Content</Text> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Back and forward navigation enables SheetMobile to display sequential content in a step by step flow.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const [page, setPage] = useState(1); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile backIconButton={ page > 1 ? { accessibilityLabel: 'Previous page', onClick: () => setPage((value) => value - 1), } : undefined } forwardIconButton={ page < 3 ? { accessibilityLabel: 'Next page', onClick: () => setPage((value) => value + 1), } : undefined } heading="Business guidelines" onDismiss={() => setShowComponent(false)} subHeading="Create Pins in no time with our flexible tools." > <Fragment> {page === 1 ? ( <Text> Upload images or videos. Create and edit Pins right from our app or desktop site. You can make one Pin at a time, or upload assets in bulk. </Text> ) : null} {page === 2 ? ( <Text> Add your product feed. Connect a product feed and we’ll turn every product into its own Pin. </Text> ) : null} {page === 3 ? ( <Text> Publish from your site. Link your site’s RSS feed and we’ll automatically create Pins for new images in the feed. </Text> ) : null} </Fragment> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
SheetMobile's footer has a flexible configuration. footer
prop accepts any kind of node. The footer can have up to two Buttons and another two IconButtons as shown in the example.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, IconButtonLink, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile footer={ <Flex gap={2} justifyContent="between"> <IconButtonLink accessibilityLabel="This IconButton is an example of IconButton acting as a link" href="https://www.pinterest.com" icon="share" target="blank" tooltip={{ text: 'Link example' }} /> <Flex gap={2}> <Button color="gray" text="Secondary" /> <Button color="red" text="Primary" /> </Flex> <IconButtonLink accessibilityLabel="This IconButton is an example of IconButton acting as a link" href="https://www.pinterest.com" icon="ellipsis" target="blank" tooltip={{ text: 'Link example' }} /> </Flex> } heading="Heading" onDismiss={() => setShowComponent(false)} showDismissButton={false} > <Text>Content</Text> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Animation
By default, SheetMobile animates in (up), with the initial render process from the entry-point, and out (down), when the ESC
key is pressed, the header close button is pressed, or the user clicks outside of the SheetMobile. However, to trigger the exit-animation from other elements in other areas such as the children
or footer
, the following render prop can be used:
<SheetMobile.DismissingElement>
({ onDismissStart }) => ( ... )
</SheetMobile.DismissingElement>
For exposed onClick
props where it's not possible to use SheetMobile.DismissingElement, the onClick
passes "onDismissStart" as render props along with the event.
onClick={({ onDismissStart }) => onDismissStart() }
When using these render props, just pass the argument onDismissStart
to your exit-point action elements or execute them on the onClick
. In the example below, we've added the exit animation to the:
- Back arrow icon button (header)
- "Close" gray button (children)
- "Close" red button (footer)
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Layer, SheetMobile, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile footer={ <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Flex gap={2} justifyContent="center"> <Button color="red" onClick={onDismissStart} text="Close" /> </Flex> )} </SheetMobile.DismissingElement> } forwardIconButton={{ accessibilityLabel: 'Next page', onClick: ({ onDismissStart }) => onDismissStart(), }} heading="Heading" onDismiss={() => setShowComponent(false)} size="auto" > <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Flex alignItems="center" height="100%" justifyContent="center"> <Button color="gray" onClick={onDismissStart} text="Close" /> </Flex> )} </SheetMobile.DismissingElement> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Preventing close on outside click
By default, users can click outside a partial SheetMobile (on the overlay) to close it. This can be disabled by setting closeOnOutsideClick
to "false".
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile closeOnOutsideClick={false} heading="Heading" onDismiss={() => setShowComponent(false)} > <Text>Content</Text> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
Transitions
SheetMobile slides up from the bottom as the initial transition. However, transitions between sheets should follow the following patterns.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Layer, SheetMobile, Text, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const [page, setPage] = useState(1); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); return ( <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile backIconButton={ page > 1 ? { accessibilityLabel: 'Previous page', onClick: () => setPage((value) => value - 1), } : undefined } forwardIconButton={ page < 3 ? { accessibilityLabel: 'Next page', onClick: () => setPage((value) => value + 1), } : undefined } heading="Business guidelines" onDismiss={() => setShowComponent(false)} subHeading="Create Pins in no time with our flexible tools." > <Fragment> {page === 1 ? ( <Text> Upload images or videos. Create and edit Pins right from our app or desktop site. You can make one Pin at a time, or upload assets in bulk. </Text> ) : null} {page === 2 ? ( <Text> Add your product feed. Connect a product feed and we’ll turn every product into its own Pin. </Text> ) : null} {page === 3 ? ( <Text> Publish from your site. Link your site’s RSS feed and we’ll automatically create Pins for new images in the feed. </Text> ) : null} </Fragment> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
If there's a transition between SheetMobile of the same size, the content transitions in place.
import { useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, Layer, SheetMobile, TextField, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const [showComponentData, setShowComponentData] = useState(null); const [showNextData, setShowNextData] = useState(null); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); const resetShowNextData = (animationState) => { if (animationState === 'in') { setShowNextData(null); } }; return ( <DeviceTypeProvider deviceType="mobile"> {showComponent && showComponentData === 'A' ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile heading="Heading" onAnimationEnd={({ animationState }) => { resetShowNextData(animationState); if (animationState === 'out' && showNextData === 'B') { setShowComponentData('B'); setShowComponent(true); } if (animationState === 'out' && !showNextData) { setShowComponentData(null); } }} onDismiss={() => { setShowComponent(false); }} > <Flex alignContent="center" height="100%" justifyContent="center"> <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Button color="red" onClick={() => { onDismissStart(); setShowNextData('B'); }} text="Review form" /> )} </SheetMobile.DismissingElement> </Flex> </SheetMobile> </Layer> ) : null} {showComponent && showComponentData === 'B' ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile backIconButton={{ accessibilityLabel: 'Previous page', onClick: ({ onDismissStart }) => { onDismissStart(); setShowNextData('A'); }, }} footer={ <Flex gap={2} justifyContent="center"> <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Button color="red" onClick={() => { onDismissStart(); setShowNextData(null); }} text="Submit" /> )} </SheetMobile.DismissingElement> </Flex> } heading="Heading" onAnimationEnd={({ animationState }) => { resetShowNextData(animationState); if (animationState === 'out' && showNextData === 'A') { setShowComponentData('A'); setShowComponent(true); } }} onDismiss={() => setShowComponent(false)} size="auto" > <Flex alignContent="center" direction="column" gap={4} justifyContent="center" > {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((x) => ( <TextField key={x} id={`Input${x}`} label={`Input${x}`} onChange={() => {}} placeholder={`Input${x}`} type="text" /> ))} </Flex> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => { setShowComponentData('A'); setShowComponent(true); }} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> ); }
If there's a transition between SheetMobile of different sizes or with a size set to "auto", where height adjusts to content, the initial sheet will slide down to close and the new sheet will slide up to open.
External handlers
SheetMobile consumes external handlers from GlobalEventsHandlerProvider.
Handlers:
- onOpen: executed when SheetMobile opens
- onClose: executed when SheetMobile closes
See GlobalEventsHandlerProvider for more information.
import { useCallback, useMemo, useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, GlobalEventsHandlerProvider, Layer, SheetMobile, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); const sheetMobileOnOpen = useCallback( () => console.log('On open handler'), [] ); const sheetMobileOnClose = useCallback( () => console.log('On close handler'), [] ); const sheetMobileHandlers = useMemo( () => ({ onOpen: sheetMobileOnOpen, onClose: sheetMobileOnClose }), [sheetMobileOnOpen, sheetMobileOnClose] ); return ( <GlobalEventsHandlerProvider sheetMobileHandlers={sheetMobileHandlers}> <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile heading="Heading" onDismiss={() => setShowComponent(false)} size="auto" > <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Flex alignItems="center" height="100%" justifyContent="center" > <Button color="gray" onClick={onDismissStart} text="Close" /> </Flex> )} </SheetMobile.DismissingElement> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> </GlobalEventsHandlerProvider> ); }
Implementation guidelines
SheetMobile is a mobile only component; therefore, there shouldn't be instances of SheetMobile in desktop experiences. To enforce proper usage, SheetMobile only renders when
DeviceTypeProvider wraps your app and deviceType
prop is set to "mobile". Otherwise, it only renders "null".
Writing
- Keep headings short and clear
- Use Sentence case for headings per our Pinterest writing standards
- Punctuate headings unless they are posing a question or making an exclamation
- Use Title Case or ALL CAPS
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Component is not currently available in Figma. | |
Responsive Web | Component does not respond to changing viewport sizes in web and mobile web. |
Internal documentation
Related
Modal
Modal displays content that requires user interaction. Modals appear on a layer above the page and therefore block the content underneath, preventing users from interacting with anything else besides the Modal.
ModalAlert
ModalAlert is a simple modal dialog used to alert a user of an issue, or to request confirmation after a user-triggered action.
OverlayPanel
OverlayPanels are surfaces that allow users to view optional information or complete sub-tasks in a workflow while keeping the context of the current page.