Z-Index Classes
FixedZIndex
FixedZIndex is used for setting fixed z-index values. Use this class when you want to create an initial z-index to stack others on top of. FixedZIndex must be instantiated with a number.
import { FixedZIndex } from 'gestalt';
const fixedZindex = new FixedZIndex(1);
CompositeZIndex
CompositeZIndex is used for dynamically composing z-index values. Use this class to layer components on top of an existing z-index in the stacking context.
CompositeZIndex must be instantiated with an array of FixedZIndex or CompositeZIndex instances. CompositeZIndex returns the highest z-index value in the array +1.
import { CompositeZIndex, FixedZIndex } from 'gestalt';
const fixedZIndex = new FixedZIndex(1); //z-index value: 1
const compositeZIndex = new CompositeZIndex([fixedZIndex]); //z-index value: 2
const highestCompositeZIndex = new CompositeZIndex([fixedZIndex, compositeZIndex]); //z-index value: 3
Best practices
Export FixedZIndex and CompositeZIndex rather than the z-index values itself.
// exporting file
import { FixedZIndex } from 'gestalt';
export const BaseZIndex = = new FixedZIndex(1);
// importing file
import { BaseZIndex } from './path/to/your/zindex/file';
const BoxWithZIndex = <Box zIndex={BaseZIndex}/>
Export constant z-index values to create FixedZIndex and CompositeZIndex.
// exporting file
export const BaseZIndex = 1;
// importing file
import { BaseZIndex } from './path/to/your/zindex/file';
import { FixedZIndex } from 'gestalt';
const BoxZIndex = new FixedZIndex(BaseZIndex);
const BoxWithZIndex = <Box zIndex={BoxZIndex}/>
Use CompositeZIndex to compose z-indices.
// exporting file
import { FixedZIndex } from 'gestalt';
export const BaseZIndex = = new FixedZIndex(1);
// importing file
import { CompositeZIndex } from 'gestalt';
import { BaseZIndex } from './path/to/your/zindex/file';
const composedZIndex = new CompositeZIndex([BaseZIndex]);
const BoxWithZIndex = <Box zIndex={composedZIndex}/>
Use FixedZIndex to manually compose z-indices.
// exporting file
export const BaseZIndex = 1;
// importing file
import { FixedZIndex } from 'gestalt';
import { BaseZIndex } from './path/to/your/zindex/file';
const composedZIndex = new FixedZIndex(BaseZIndex + 1);
const BoxWithZIndex = <Box zIndex={composedZIndex}/>
Use the lowest possible z-index values and compose them.
import { CompositeZIndex, FixedZIndex } from 'gestalt';
const PageHeaderZindex = new FixedZIndex(1);
const ArticleZindex =new FixedZIndex(1);
const ArticleHeaderZindex = new CompositeZIndex([ArticleZindex]);
const SheetZindex = new CompositeZIndex([PageHeaderZindex, ArticleHeaderZindex]);
const modalZindex = new CompositeZIndex([SheetZindex]);
const ModalWithZIndex = <Layer zIndex={modalZindex}><Modal/></Layer>
Use unnecessarily high fixed z-index values.
import { FixedZIndex } from 'gestalt';
const HeaderZindex = new FixedZIndex(1);
const MenuZindex =new FixedZIndex(1000);
const SheetZindex = new FixedZIndex(9999);
const modalZindex = new FixedZIndex(100000);
const ModalWithZIndex = <Layer zIndex={modalZindex}><Modal/></Layer>
Wrap non-Gestaltifiable HTML tags in Box and pass z-index classes.
import { FixedZIndex } from 'gestalt';
const fixedZindex = new FixedZIndex(1);
const canvas = (
<Box zIndex={fixedZindex}>
<canvas
id="myCanvas"
width="200"
height="100"
style="border:1px solid #000000;"></canvas>
</Box>
);
Extract z-index values from either z-index classes to use in non-Gestaltifiable HTML Tags. See z-index in non-Gestalt components to learn more.
import { FixedZIndex } from 'gestalt';
const fixedZindex = new FixedZIndex(1);
const canvas = (
<canvas
z-index={fixedZindex.index()}
id="myCanvas"
width="200"
height="100"
style="border:1px solid #000000;"></canvas>
);
Use Layer instead of z-index when a component needs to visually break out of its parent container, for example, in the case of Modals, OverlayPanels and Tooltips. See Z-Index in foundational components and Z-Index in Layer to learn more.
const modal = (
<Layer>
<Modal>
<Text>Modal</Text>
</Modal>
</Layer>
);
Use Box with high fixed z-index values to position your components at the top of the stacking context or use them redundantly with Layer. See z-Index in foundational components and ZIndex in Layer to learn more.
import { FixedZIndex } from 'gestalt';
const MODAL_ZINDEX = new FixedZIndex(1000000);
const modalA = (
<Box zIndex={MODAL_ZINDEX}>
<Modal>
<Text>Modal A</Text>
</Modal>
<Box>
);
const modalB = (
<Layer>
<Box zIndex={MODAL_ZINDEX}>
<Modal>
<Text>Modal B</Text>
</Modal>
<Box zIndex={MODAL_ZINDEX}>
</Layer>
);
Accessibility
Variants
z-index in foundational components
Box,
Sticky, and
Layer are foundational components that have zIndex
props. If any other components need to be positioned within their stacking context, wrap with one of these foundational components to set the z-index.
Layer creates a new stacking context. Unless there's a conflict with another z-index, don't pass unnecessary zIndex
to Layer.
The following example sets a z-index in the Layer wrapping
OverlayPanel to position OverlayPanel over the page header in the Docs. Set PAGE_HEADER_ZINDEX
below 10 to see the importance of z-index in this example.
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } import { Fragment, useEffect, useRef, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Flex, Image, Layer, Mask, OverlayPanel, Popover, SearchField, TapArea, Text, TextArea, } from 'gestalt'; /* ======= Z-INDEX ======= */ const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const SHEET_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); function SearchBoardField() { const ref = useRef(null); useEffect(() => { _optionalChain([ ref, 'access', (_) => _.current, 'optionalAccess', (_2) => _2.focus, 'call', (_3) => _3(), ]); }, []); return ( <SearchField ref={ref} accessibilityLabel="Search boards field" id="searchField" onChange={() => {}} placeholder="Search boards" size="lg" /> ); } function List({ title, onSelect }) { 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={() => onSelect(data[1])} rounding={2}> <Flex alignItems="center" gap={{ row: 2, column: 0 }}> <Box height={50} overflow="hidden" rounding={2} width={50}> <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> ); } function SelectBoard() { const [openPopover, setOpenPopover] = useState(false); const [selectedBoard, setSelectedBoard] = useState('Fashion'); const anchorRef = useRef(null); const handleSelect = (data) => { setSelectedBoard(data); setOpenPopover(false); }; return ( <Fragment> <Flex direction="column" gap={{ column: 2, row: 0 }}> <Text size="100">Board</Text> <Button ref={anchorRef} accessibilityLabel="Select Board" iconEnd="arrow-down" onClick={() => setOpenPopover(!openPopover)} text={selectedBoard} /> </Flex> {openPopover && ( <Layer> <Popover anchor={anchorRef.current} idealDirection="down" onDismiss={() => setOpenPopover(false)} positionRelativeToAnchor={false} size="xl" > <Box width={360}> <Box flex="grow" marginBottom={8} marginEnd={4} marginStart={4} marginTop={6} > <Flex direction="column" gap={{ column: 6, row: 0 }}> <Text align="center" color="default" weight="bold"> Save to board </Text> <SearchBoardField /> </Flex> </Box> <Box height={300} overflow="scrollY"> <Box marginEnd={4} marginStart={4}> <Flex direction="column" gap={{ column: 8, row: 0 }}> <List onSelect={handleSelect} title="Top choices" /> <List onSelect={handleSelect} title="All boards" /> </Flex> </Box> </Box> </Box> </Popover> </Layer> )} </Fragment> ); } export default function ScrollBoundaryContainerExample() { const [showSheet, setShowSheet] = useState(false); return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Button onClick={() => setShowSheet(true)} size="lg" text="Edit Pin" /> {showSheet && ( <Layer zIndex={SHEET_ZINDEX}> <OverlayPanel accessibilityDismissButtonLabel="Close edit Pin overlay panel" accessibilityLabel="Edit your Pin details" footer={ <Flex> <Flex.Item flex="grow"> <Button color="white" onClick={() => setShowSheet(false)} size="lg" text="Delete" /> </Flex.Item> <Flex gap={{ column: 0, row: 2 }}> <Button onClick={() => setShowSheet(false)} size="lg" text="Cancel" /> <Button color="red" onClick={() => setShowSheet(false)} size="lg" text="Done" type="submit" /> </Flex> </Flex> } heading="Edit Pin" onDismiss={() => setShowSheet(false)} size="lg" > <Box display="flex" height={400} paddingX={8}> <Flex gap={{ row: 8, column: 0 }} width="100%"> <Box paddingX={2} rounding={4} width={200}> <Mask rounding={4}> <Image alt="Tropic greens: The taste of Petrol and Porcelain | Interior design, Vintage Sets and Unique Pieces agave" color="rgb(231, 186, 176)" naturalHeight={751} naturalWidth={564} src="https://i.ibb.co/7bQQYkX/stock2.jpg" /> </Mask> </Box> <Flex.Item flex="grow"> <Flex direction="column" gap={{ column: 8, row: 0 }}> <SelectBoard /> <TextArea id="note" label="Note" onChange={() => {}} placeholder="Add note" value="" /> </Flex> </Flex.Item> </Flex> </Box> </OverlayPanel> </Layer> )} </Box> ); }
z-index in Layer
Modal and OverlayPanel always require a parent Layer to position themselves outside the DOM hierarchy.
Components built on top of
Popover, such as
Tooltip,
Dropdown and
ComboBox, have a built-in Layer to be positioned outside the DOM hierarchy. To set the internal z-index value of Layer, these Popover-based components have zIndex
props as well. This is used when placing the Popover-based components within another component wrapped in Layer that has a z-index set.
However, Modal and OverlayPanel have a built-in ScrollBoundaryContainer wrapping their children, so you shouldn’t need to pass z-index values when using Popover-based children.
The following example sets a z-index in the Layer wrapping Modal to position Modal over the page header in the Docs. Thanks to ScrollBoundaryContainer, child Tooltips don't require z-index.
import { Fragment, useState } from 'react'; import { Box, Button, CompositeZIndex, FixedZIndex, Flex, Heading, IconButton, Image, Layer, Modal, Text, Tooltip, } from 'gestalt'; /* ======= Z-INDEX ======= */ const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const MODAL_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); export default function ScrollBoundaryContainerExample() { const [showModal, setShowModal] = useState(false); const [alignText, setAlignText] = useState('forceLeft'); return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Fragment> <Box display="flex" justifyContent="center"> <Button accessibilityLabel="Edit this Pin" color="gray" onClick={() => setShowModal(true)} size="lg" text="Open edit modal" /> </Box> {showModal && ( <Layer zIndex={MODAL_ZINDEX}> <Modal accessibilityModalLabel="Edit Pin" footer={ <Box flex="grow" paddingX={3} paddingY={3}> <Box display="flex" justifyContent="end" marginBottom={-1} marginEnd={-1} marginStart={-1} marginTop={-1} wrap > <Box paddingX={1} paddingY={1}> <Button onClick={() => setShowModal(false)} size="lg" text="Cancel" /> </Box> <Box paddingX={1} paddingY={1}> <Button color="red" onClick={() => setShowModal(false)} size="lg" text="Save" type="submit" /> </Box> </Box> </Box> } heading="Edit" onDismiss={() => setShowModal(false)} size="lg" > <Box column={12} display="flex" justifyContent="center"> <Box column={6} paddingX={4}> <Image alt="Tropic greens: The taste of Petrol and Porcelain | Interior design, Vintage Sets and Unique Pieces agave" color="rgb(231, 186, 176)" naturalHeight={751} naturalWidth={564} src="https://i.ibb.co/7bQQYkX/stock2.jpg" > <Box padding={3}> <Heading align={alignText} color="light" size="500"> Tropic greens: The taste of Petrol and Porcelain </Heading> </Box> </Image> </Box> <Flex direction="column" gap={{ column: 4, row: 0 }}> <Heading size="400">Text Overlay</Heading> <Text size="300">Add text directly onto your Pin</Text> <Text size="300" weight="bold"> Alignment </Text> <Flex> <Tooltip text="Align left"> <IconButton accessibilityLabel="Align left" bgColor="white" icon="text-align-left" iconColor="darkGray" onClick={() => setAlignText('left')} selected={alignText === 'left'} size="lg" /> </Tooltip> <Tooltip text="Align center"> <IconButton accessibilityLabel="Align center" bgColor="white" icon="text-align-center" iconColor="darkGray" onClick={() => setAlignText('center')} selected={alignText === 'center'} size="lg" /> </Tooltip> <Tooltip text="Align right"> <IconButton accessibilityLabel="Align right" bgColor="white" icon="text-align-right" iconColor="darkGray" onClick={() => setAlignText('right')} selected={alignText === 'right'} size="lg" /> </Tooltip> </Flex> </Flex> </Box> </Modal> </Layer> )} </Fragment> </Box> ); }
z-index in non-Gestalt components
FixedZIndex and CompositeZIndex work with Gestalt components. To stay consistent across the codebase using Gestalt’s zIndex classes, you can extract z-index values from both FixedZIndex and CompositeZIndex in cases where the component doesn’t accept Gestalt’s z-index classes.
import { FixedZIndex, CompositeZIndex } from 'gestalt';
const fixedZindex = new FixedZIndex(1);
const fixedZindexValue = fixedZindex.index(); // 1
const compositeZIndex = new CompositeZIndex([fixedZindex]);
const compositeZIndexValue = compositeZIndex.index(); // 2
However, this is an escape hatch that should only be used when the source code is not accessible, such as working with a third-party library. Otherwise, a better approach is to wrap the component or HTML element that needs a z-index in a Box. See Best Practices for more info.
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Component is not currently available in Figma. | |
Responsive Web | Ready | Component responds to changing viewport sizes in web and mobile web. |
Related
Layer
Layer allows you to render children outside the DOM hierarchy of the parent. This is useful for places you might otherwise need to use z-index to overlay the screen, simplifying the stacking context complexity in the app. See
z-Index in foundational components and
ZIndex in Layer to learn more.