Popover is a floating view that contains a task related to the content on screen. It can be triggered when the user clicks or focuses on an element, typically Button or IconButton. It can also be triggered automatically, as in the case of user education. Popover is non-modal and can be dismissed by interacting with another part of the screen or an item within Popover.
Popover is most appropriate for desktop screens and can contain a variety of elements, such as Button and Images. Popover is also the container used to construct more complex elements like Dropdown and the board picker, pictured below, which allow people to choose the board to save a Pin to.
also known as Flyout
Props
Usage guidelines
- Providing additional information for related context without cluttering the surface of a workflow.
- Bringing attention to specific user interface elements for educational purposes. In this case, likely used with a Pulsar.
- Accommodating a variety of features, such as Buttons, Images or SearchFields, that are not available in Dropdown.
Best practices
Use Popover to display a lightweight task related to the content on screen.
Use Popover to communicate critical information, such as an error or interaction feedback. Instead, use the error supplied directly to the form element. See related to learn more.
Use Popover to educate users on a new or existing feature. When Popover is triggered automatically, like in the case of user education, be sure to use a blue background and include a caret pointing to the feature. See the color and caret variant to learn more.
Include a caret if Popover was triggered by user interaction, such as clicking or focusing on Button or IconButton.
Accessibility
Keyboard interaction
- When Popover opens, focus moves to the first focusable element in the Popover container.
- Popovers are also a focus trap, so users should only be able to interact with the content inside the Popover container.
- Popover should always be dismissible using the ESC key. It could also be dismissed by interacting with another part of the screen, or by interacting with an element inside Popover.
- When Popover is closed, focus returns to the anchor element that triggered Popover.
ARIA attributes
We recommend passing the following ARIA attribute to Popover for a better screen reader experience:
accessibilityLabel
: describes the main purpose of a Popover for the screen reader. Should be unique and concise. For example, "Save to board" instead of "Popover". It populates aria-label.accessibilityDismissButtonLabel
: describes the purpose of the dismiss button on Popover for the screen reader. Should be clear and concise. For example, "Close board selection popover" instead of "Close".
To further assist screen readers, we recommend passing the following ARIA attributes to the anchor element:
accessibilityHaspopup
: informs the screen reader that there’s a Popover attached to the anchor element. It populates aria-haspopup.accessibilityExpanded
: informs the screen reader whether Popover is currently open or closed. It populates aria-expanded.accessibilityControls
: match with theid
of the associated Popover whose contents or visibility are controlled by the interactive component so that screen reader users can identify the relationship between elements. It populates aria-controls.
For the role
prop, use:
- 'dialog' if the Popover is a dialog that prompts the user to enter information or requires a response.
- 'menu' if the Popover presents a list of choices to the user.
- 'listbox' if the Popover is a widget that allows the user to select one or more items (whose role is option) from a list. May also include a search option.
import { useRef, useState } from 'react'; import { Popover, Box, Button, Flex, Layer, Text, SearchField, TapArea, Mask, Image, } from 'gestalt'; function List({ title, setSelectedBoard, setOpen }) { return ( <Flex direction="column" gap={{ column: 4, row: 0 }}> <Text color="default" size="100"> {title} </Text> <Flex direction="column" gap={{ column: 4, row: 0 }}> {[ [ 'https://i.ibb.co/s3PRJ8v/photo-1496747611176-843222e1e57c.webp', 'Fashion', 'Thumbnail image: a white dress with red flowers', ], [ 'https://i.ibb.co/swC1qpp/IMG-0494.jpg', 'Food', 'Thumbnail image: a paella with shrimp, green peas, red peppers and yellow rice', ], [ 'https://i.ibb.co/PFVF3JH/photo-1583847268964-b28dc8f51f92.webp', 'Home', 'Thumbnail image: a living room with a white couch, two paints in the wall and wooden furniture', ], ].map((data) => ( <TapArea key={data[1]} onTap={() => { setSelectedBoard(data[1]); setOpen(false); }} > <Flex gap={{ row: 2, column: 0 }} alignItems="center"> <Box height={50} width={50} overflow="hidden" rounding={2}> <Mask rounding={2}> <Image alt={data[2]} color="rgb(231, 186, 176)" naturalHeight={50} naturalWidth={50} src={data[0]} /> </Mask> </Box> <Text align="center" color="default" weight="bold"> {data[1]} </Text> </Flex> </TapArea> ))} </Flex> </Flex> ); } export default function DismissButton() { const [open, setOpen] = useState(false); const [selectedBoard, setSelectedBoard] = useState('Fashion'); const anchorRef = useRef(); return ( <Flex alignItems="start" justifyContent="center" height="100%" width="100%"> <Box padding={3}> <Flex alignItems="center" gap={{ row: 2, column: 0 }}> <Button accessibilityHaspopup accessibilityExpanded={open} accessibilityControls="main-example" color="white" iconEnd="arrow-down" onClick={() => setOpen(!open)} ref={anchorRef} size="lg" selected={open} text={selectedBoard} /> <Button color="red" onClick={() => {}} size="lg" text="Save" /> </Flex> </Box> {open && ( <Layer> <Popover accessibilityLabel="Save to board" anchor={anchorRef.current} id="main-example" idealDirection="down" onDismiss={() => setOpen(false)} positionRelativeToAnchor={false} showDismissButton size="xl" > <Box width={360}> <Box flex="grow" marginEnd={4} marginStart={4} marginBottom={8}> <Flex direction="column" gap={{ column: 6, row: 0 }}> <Text align="center" color="default" weight="bold"> Save to board </Text> <SearchField accessibilityLabel="Search boards field" id="searchField" onChange={() => {}} placeholder="Search boards" size="lg" /> </Flex> </Box> <Box height={300} overflow="scrollY"> <Box marginEnd={4} marginStart={4}> <Flex direction="column" gap={{ column: 8, row: 0 }}> <List title="Top choices" setSelectedBoard={setSelectedBoard} setOpen={setOpen} /> <List title="All boards" setSelectedBoard={setSelectedBoard} setOpen={setOpen} /> </Flex> </Box> </Box> </Box> </Popover> </Layer> )} </Flex> ); }
Localization
Be sure to localize any text elements within Popover, along with accessibilityLabel
and accessibilityDismissButtonLabel
. Note that localization can lengthen text by 20 to 30 percent.
Variants
Size
The maximum width of Popover. Popover has different size configurations:
"xs"
: 180px"sm"
: 230px"md"
: 284px"lg"
: 320px"xl"
: 360pxnumber
: Use this prop to create custom-size Popovers in pixels.flexible
: Use this configuration for larger Popovers. Without a defined maximum width, Popover grows to fit the content inchildren
.
We recommend using "xs
" for education Popovers and "xl
" for more complex Popovers. Avoid using other configurations whenever possible as they are legacy sizes.
Color and caret
When building in-product education, be sure to pass in color="blue"
and showCaret="true"
, as seen in the first example, and use Experience HQ for the configuration. For Popovers that aren’t education, use the default color="white"
and showCaret="false"
, as seen in the second example. Avoid using any other configurations as they are legacy colors.
Anchor
Popover requires a reference element, typically Button or IconButton, to set its position. The anchor
ref can be directly set on the reference component itself. If the components don’t support ref
, the anchor ref can be set to a parent Box.
Popover calculates its position based on the bounding box of the anchor
. Therefore, the anchor
ref should only include the trigger element itself, usually Button or IconButton, or the specific feature component that requires an educational Popover.
We highly recommend including a dismiss button on all Popovers with showDismissButton
. This improves accessibility and gives users an immediate action for closing Popover. A label for the button can be provided with the accessibilityDismissButtonLabel
prop. Don't forget to localize this label as well.
import { useRef, useState } from 'react'; import { Popover, Box, Button, Flex, Layer, Text, SearchField, TapArea, Mask, Image, } from 'gestalt'; function List({ title, setSelectedBoard, setOpen }) { return ( <Flex direction="column" gap={{ column: 4, row: 0 }}> <Text color="default" size="100"> {title} </Text> <Flex direction="column" gap={{ column: 4, row: 0 }}> {[ [ 'https://i.ibb.co/s3PRJ8v/photo-1496747611176-843222e1e57c.webp', 'Fashion', 'Thumbnail image: a white dress with red flowers', ], [ 'https://i.ibb.co/swC1qpp/IMG-0494.jpg', 'Food', 'Thumbnail image: a paella with shrimp, green peas, red peppers and yellow rice', ], [ 'https://i.ibb.co/PFVF3JH/photo-1583847268964-b28dc8f51f92.webp', 'Home', 'Thumbnail image: a living room with a white couch, two paints in the wall and wooden furniture', ], ].map((data) => ( <TapArea key={data[1]} onTap={() => { setSelectedBoard(data[1]); setOpen(false); }} > <Flex gap={{ row: 2, column: 0 }} alignItems="center"> <Box height={50} width={50} overflow="hidden" rounding={2}> <Mask rounding={2}> <Image alt={data[2]} color="rgb(231, 186, 176)" naturalHeight={50} naturalWidth={50} src={data[0]} /> </Mask> </Box> <Text align="center" color="default" weight="bold"> {data[1]} </Text> </Flex> </TapArea> ))} </Flex> </Flex> ); } export default function DismissButton() { const [open, setOpen] = useState(false); const [selectedBoard, setSelectedBoard] = useState('Fashion'); const anchorRef = useRef(); return ( <Flex alignItems="start" justifyContent="center" height="100%" width="100%"> <Box padding={3}> <Flex alignItems="center" gap={{ row: 2, column: 0 }}> <Button accessibilityHaspopup accessibilityExpanded={open} accessibilityControls="main-example" color="white" iconEnd="arrow-down" onClick={() => setOpen(!open)} ref={anchorRef} size="lg" selected={open} text={selectedBoard} /> <Button color="red" onClick={() => {}} size="lg" text="Save" /> </Flex> </Box> {open && ( <Layer> <Popover accessibilityLabel="Save to board" anchor={anchorRef.current} id="main-example" idealDirection="down" onDismiss={() => setOpen(false)} positionRelativeToAnchor={false} showDismissButton size="xl" > <Box width={360}> <Box flex="grow" marginEnd={4} marginStart={4} marginBottom={8}> <Flex direction="column" gap={{ column: 6, row: 0 }}> <Text align="center" color="default" weight="bold"> Save to board </Text> <SearchField accessibilityLabel="Search boards field" id="searchField" onChange={() => {}} placeholder="Search boards" size="lg" /> </Flex> </Box> <Box height={300} overflow="scrollY"> <Box marginEnd={4} marginStart={4}> <Flex direction="column" gap={{ column: 8, row: 0 }}> <List title="Top choices" setSelectedBoard={setSelectedBoard} setOpen={setOpen} /> <List title="All boards" setSelectedBoard={setSelectedBoard} setOpen={setOpen} /> </Flex> </Box> </Box> </Box> </Popover> </Layer> )} </Flex> ); }
With Layer
Popover is typically used within Layer. Layer renders Popover outside the DOM hierarchy of the parent allowing it to overlay surrounding content. Popover calculates its position based on the bounding box of the anchor
. Within Layer, Popover no longer shares a relative root with the anchor
and requires positionRelativeToAnchor=false
to properly calculate its position relative to the anchor element.
Using Layer
with Popover eliminates the need to use z-index
to solve stacking context conflicts. Popovers within Modals and Sheets with z-indexes don't require zIndex
in Layer
thanks to the built-in ScrollBoundaryContainer.
Ideal direction
Pass in idealDirection
to specify the preferred position of Popover relative to the anchor, such as Button or IconButton, that triggered it.
Adjust the idealDirection
as necessary to ensure the visibility of Popover and its contextual information. The default direction is "up", although Popover should be center-aligned directly below the element in most cases. The actual position may change given the available space around the anchor element.
Within scrolling containers
ScrollBoundaryContainer is needed for proper positioning when Popover is anchored to an element that is located within a scrolling container. The use of ScrollBoundaryContainer ensures Popover remains attached to its anchor when scrolling.
Writing
- Be clear and predictable so that people anticipate what will happen when they interact with an item.
- Focus on the action by beginning with a verb.
- Use succinct and scannable language.
- Use sentence case while always capitalizing the word “Pin.”
- Describe the interface element, like “button,” “icon” or “menu” in education messaging, unless it’s absolutely necessary for clarity.
- Use words like “click” or “tap” in education messaging, if possible, or assume universal accessibility.
- Use Popover to communicate critical information, such as an error or interaction feedback.
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Partially ready | Component is live in Figma, however may not be available for all platforms. |
Responsive Web | Ready | Component is available in code for web and mobile web. |
iOS | Component is not currently available in code for iOS. | |
Android | Component is not currently available in code for Android. |
Related
Dropdown
Dropdown is an element constructed using Popover as its container. Use Dropdown to display a list of actions or options in a Popover.
Toast
Toast provides feedback on an interaction. One example of Toast is the confirmation that appears when a Pin has been saved. Toasts appear at the bottom of a desktop screen or top of a mobile screen instead of being attached to any particular element on the interface.
Tooltip
Tooltip describes the function of an interactive element, typically IconButton, on hover. While Popovers offer broader content options, such as Button and Images, Tooltips are purely text-based.
Layer
Layer renders Popover outside the DOM hierarchy of the parent and prevents surrounding components overlaying Popover. See the with Layer variant to learn more.
ScrollBoundaryContainer
ScrollBoundaryContainer is needed for proper positioning when Popover is anchored to an element that is located within a scrolling container. The use of ScrollBoundaryContainer ensures that Popover remains attached to its anchor when scrolling. See the within scrolling containers variant to learn more.