Box is a component primitive that can be used to build the foundation of pretty much any other component. It keeps details like spacing, borders and colors consistent with the rest of Gestalt, while allowing the developer to focus on the content.
also known as <div>, View
Props
Best practices
Use Box as a building block when creating other components or layouts that do not rely on flexbox. The included properties should cover any variations needed to create a diverse range of options.
If you find yourself using Box for flexbox layouts, consider Flex instead.
Don’t use the onClick
, className
and style
properties.
Box is a pass-through component, meaning that any other properties you provide to it will be directly applied to the underlying <div>
. The above properties are exceptions, however. We don’t allow onClick
for accessibility reasons, so consider a
Button or
TapArea instead. We remove className
and style
to ensure style encapsulation. If necessary, dangerouslySetInlineStyle
can be used to supply a style not supported by Box props.
If you need to use these features for animation purposes, use a <div>
instead.
When addressing the spacing of the Box, use padding before you use margins, as padding will compose better and won't collapse. Padding is applied in 4px increments and is always symmetric. Learn more about margin collapsing.
Avoid using arbitrary <div>
elements. Instead, when building a component, prioritize using Box. If you need to set a custom style, you can do so using the dangerouslySetInlineStyle
prop. However, this should be avoided whenever possible by utilizing the other props provided in Box. We provide a
lint rule to prevent this from happening.
Accessibility
The visuallyHidden
option of the display
property can be used to prevent content from being visible while ensuring that screen readers still have access to the content. This can be useful when adding context for screen reader users, such as adding a pause to the labels of
Checkboxes.
Setting display="visuallyHidden"
on Box allows for an element to be visually hidden but still be read by screen readers.
The ‘visually-hidden’ CSS technique applies absolute positioning to the element.
<code>
height: 1px;
overflow: hidden;
position: absolute;
width: 1px;
...
</code>
For a correct implementation, make sure the ‘visually-hidden’ element is correctly contained within a relative-positioned Box.
import { Box, Flex, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Text>Enable your screen reader to hear the following text:</Text> <Box position="relative"> <Box display="visuallyHidden">Hi there.</Box> </Box> </Flex> ); }
Using 'as' property
By default, the Box component renders a div
element, which is a non-semantic element that doesn't provide much meaning to the user or assistive technology. Use the as
prop to inform which semantic HTML element should be rendered by the Box component instead of a div
to ensure a more meaningful experience for both the user and the browser.
When using a Box component as a custom element, it is your responsibility to address all the accessibility implications. Both the role
and as
properties semantically classify the Box; however, the as
prop defines a more concise way to describe the HTML element by modifying the underlying DOM element directly, which helps support both accessibility and SEO. Use the as
prop whenever possible, making sure that the prop type is semantically associated with the Box content.
Review the
available options for the as prop. For some of the options, like nav
, you will also need to specify a title
to ensure unique landmarks on the page. Learn more about
semantics in HTML.
import { Box, Flex, Heading, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" direction="column" flex="grow" height="100%" width="100%" > <Flex height="100%" width="100%"> <Box as="nav" borderStyle="sm" color="successBase" column={6} padding={2} title="as prop example nav" width="100%" > <Text color="light" weight="bold"> Top Nav Menu: <code>as="nav"</code> </Text> </Box> <Box borderStyle="sm" column={6} display="inlineBlock"> <Box padding={2} width="100%"> <Text> HTML output: <br /> <code>{'<nav>Menu</nav>'}</code> </Text> </Box> </Box> </Flex> <Flex height="100%" width="100%"> <Box as="article" borderStyle="sm" color="successBase" column={6} padding={2} width="100%" > <Heading color="light" size="500"> Article 1 </Heading> <Text color="light" weight="bold"> Article: <code>as="article"</code> </Text> </Box> <Box borderStyle="sm" column={6} display="inlineBlock"> <Box padding={2} width="100%"> <Text> HTML output: <br /> <code> {'<article>'} <br /> {' <h2>Article 1</h2>'} <br /> {'</article>'} </code> </Text> </Box> </Box> </Flex> </Flex> ); }
Using 'role' property
Setting the role
property on Box classifies the Box as the semantically appropriate HTML element through the use of an ARIA role while leaving the underlying element as a div
. For example, setting role="banner"
will designate that Box to be the equivalent of a <header>
within the page hierarchy, allowing assistive technology to classify the Box appropriately.
Using the role
property creates more specific element classification and gives the user better context on the layout of the page, especially when the ability to specify the
'as' property is not available. Learn more about
ARIA roles.
import { Box, Flex, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" width="100%"> <Box column={12}> <Box color="infoBase" padding={2} role="feed" width="100%"> <Text color="light" weight="bold"> Container: role="feed" </Text> <Box column={8} display="inlineBlock"> <Box color="successBase" height={50} padding={2} role="article" title="Article 1" width="100%" > <Text color="light" weight="bold"> Content: role="article" </Text> </Box> </Box> <Box column={4} display="inlineBlock"> <Box color="warningBase" height={50} padding={2} role="form" width="100%" > <Text color="light" weight="bold"> Contact Form: role="form" </Text> </Box> </Box> </Box> <Box color="successBase" height={50} padding={2} role="navigation" title="Site Map" width="100%" > <Text color="light" weight="bold"> Site Map: role="navigation" </Text> </Box> <Text>{'Everything above will render as a <div>'}</Text> </Box> </Flex> ); }
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
Page direction
Some languages (ex. Arabic, Hebrew) read from right to left (RTL) instead of from left to right. For this reason, we use marginStart
and marginEnd
(as opposed to left and right options) to support RTL. If specific left and right options are needed, use dangerouslySetInlineStyle
.
Utilizing the marginStart
and marginEnd
properties will account for right-to-left languages and maintain proper spacing.
marginStart
is a left margin that flips to a right margin in a RTL layout.
marginEnd
is a right margin that flips to a left margin in a RTL layout.
You can toggle the page direction using the button below to see this behavior.
import { Box, Button, Flex, Text } from 'gestalt'; function BoxWithMargins({ marginEnd = 0, marginStart = 0 }) { return ( <Box dangerouslySetInlineStyle={{ __style: { backgroundColor: 'rgb(110 15 60 / 0.2)' }, }} > <Box dangerouslySetInlineStyle={{ __style: { backgroundColor: 'rgb(19 58 94 / 0.2)' }, }} marginEnd={marginEnd} marginStart={marginStart} padding={2} width={200} > <Text> {JSON.stringify( `marginEnd: ${marginEnd}, marginStart: ${marginStart}` )} </Text> </Box> </Box> ); } export default function Example() { return ( <Flex alignItems="center" direction="column" gap={2} height="100%" justifyContent="center" > <Button onClick={() => { if (document.documentElement) { const isRTL = document.documentElement.dir === 'rtl'; document.documentElement.dir = isRTL ? 'ltr' : 'rtl'; } }} size="sm" text="Toggle Page Direction" /> <BoxWithMargins marginStart={8} /> <BoxWithMargins marginEnd={8} /> <BoxWithMargins marginStart={-8} /> <BoxWithMargins marginEnd={-8} /> </Flex> ); }
Variants
Borders
Borders are controlled by the borderStyle
property. Specifying a size (sm
or lg
) enables a solid, light gray color in that width. Specifying shadow
adds an even box-shadow around the entire container, while raisedTopShadow
and raisedBottomShadow
add shadows to indicate an elevated header or footer. See the
elevation foundations page for more details.
Colors
The following values can be used to change the background color of Box. Be sure to use the value that semantically matches your use case. For full details on how to use our colors, visit our Color usage page.
Colors should be used semantically whenever possible (i.e. using "errorBase" for error scenarios). If a color is needed for a branded moment in product, Box color can be set using our color palette design tokens, but it is considered a hack and should be avoided.
Elevation
Colors and shadows can elevate elements within the UI. In light mode, elevationAccent
can be used when shadows or borders are not an option. elevationFloating
and elevationRaised
are only applicable in dark mode, while shadow
is only applicable in light mode. For full details, visit our
Elevation foundations page.
Rounding
The rounding
property sets a border radius for the Box. Options are circle
or pill
for fully rounded corners or 0-8 representing the radius in 4px increments.
Opacity
While we offer the full range of opacity options, below are usage guidelines for different values. See the opacity design tokens.
- 3% (0.03): Use for Pin wash. Permanent overlay used on Pin images to ensure a visual separation between the white background and any Pin images that have pure white peripheries. For the time being, iOS uses 4%, but this will be reevaluated in the near future.
- Note: at the moment, this can only be accomplished using the
$opacity-100
token as an inline style on Box
- Note: at the moment, this can only be accomplished using the
- 20% (0.2): Overlay wash to be used sparingly. Only use it in situations where a high-level of opacity is needed and if the 40% doesn't fit the design goal.
- 40% (0.4): Overlay wash to supply a mid-range wash over an item (e.g. #FFFFFF media controls | #000000 wash behind modals, wash on images with text overlays).
- 80% (0.8): Overlay wash used on most surface's scrims. Used to supply a low-level of opacity over an element (e.g. #FFFFFF image overlay | #00000 Board cover overlay) .
- 90% (0.9): Component wash applied on IconButton and other elements as needed (e.g. image overlays). In dark mode we recommend an inverse wash. For example: Use $color-background-wash-light instead of $color-background-wash-dark.
Column layout
The column
property allows for automatic widths based on a 12-column grid. To create responsive layouts, specify different values for smColumn
, mdColumn
, and lgColumn
.
Sizing
Box can also be sized using a mixture of width
, height
, max/min width
, max/min height
, and fit
.
When setting the size of a Box, the overflow
property may need to be set in order to hide or scroll content that is outside the bounds of the Box.
import { Box, Flex, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%"> <Box column={12}> <Box borderStyle="lg" color="warningBase" maxHeight={100} minHeight={25} overflow="hidden" padding={2} width="25%" > <Text color="light"> Add or remove text in the editor to see the min and max heights take affect. </Text> </Box> <Box borderStyle="lg" color="successBase" height={100} padding={2} width="50%" > <Text color="light"> Width and Height can be specified with numbers for "px" values or percentages </Text> </Box> <Box borderStyle="lg" color="infoBase" maxWidth={500} minWidth={100} padding={2} width="75%" > <Text color="light"> Change the screen width to see the min and max widths take affect{' '} </Text> </Box> <Box borderStyle="lg" color="errorBase" fit padding={2}> <Text color="light">"fit" sets width to 100% </Text> </Box> </Box> </Flex> ); }
Overflow
When content overflows the bounds of Box, there are multiple options to control the overflow behavior. The default is overflow="visible"
, but the most common use case is supplying overflow="auto"
to ensure overflow content can be accessed. Learn more about
CSS overflow.
import { Box, Flex, Text } from 'gestalt'; export default function Example() { const text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae nisl nec turpis vehicula ultrices. Duis pretium ut ipsum nec interdum. Vestibulum arcu dolor, consectetur ac eros a, varius commodo justo. Maecenas tincidunt neque elit, eu pretium arcu dictum ac. Donec vehicula mauris ut erat dictum, eget tempus elit luctus. In volutpat felis justo, et venenatis arcu viverra in. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin enim lorem, vulputate eget imperdiet nec, dapibus sed diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse rhoncus ut leo non gravida. Nulla tincidunt tellus sit amet ornare venenatis. Sed quis lorem cursus, porttitor tellus sed, commodo ex. Praesent blandit pretium faucibus. Aenean orci tellus, vulputate id sapien sit amet, porta fermentum quam. Praesent sem risus, tristique sit amet pulvinar in, scelerisque sit amet massa.'; return ( <Flex alignItems="center" gap={4} height="100%" justifyContent="center" width="100%" wrap > <Flex direction="column" gap={{ column: 8, row: 0 }} wrap> <Box> <Text>Overflow Hidden</Text> <Box borderStyle="lg" maxHeight={100} overflow="hidden" padding={2} width={300} > <Text>{text}</Text> </Box> </Box> <Box> <Text>Overflow Scroll</Text> <Box borderStyle="lg" maxHeight={100} overflow="scroll" padding={2} tabIndex={0} width={300} > <Text>{text}</Text> </Box> </Box> <Box marginBottom={4}> <Text>Overflow Visible</Text> <Box borderStyle="lg" maxHeight={100} overflow="visible" padding={2} width={300} > <Text>{text.substring(0, 180)}</Text> </Box> </Box> <Box> <Text>Overflow Auto</Text> <Box color="selected" maxHeight={100} overflow="auto" padding={2} width={300} > <Box color="default" padding={2} tabIndex={0} width={350}> <Text>{text}</Text> </Box> </Box> </Box> <Box> <Text>Overflow scrollX</Text> <Box color="selected" maxHeight={100} overflow="scrollX" padding={2} width={300} > <Box color="default" padding={2} tabIndex={0} width={350}> <Text>{text}</Text> </Box> </Box> </Box> <Box> <Text>Overflow scrollY</Text> <Box color="selected" maxHeight={100} overflow="scrollY" padding={2} width={300} > <Box color="default" padding={2} tabIndex={0} width={350}> <Text>{text}</Text> </Box> </Box> </Box> </Flex> </Flex> ); }
Responsive padding
Control the padding on different screen sizes by setting the smPadding
, mdPadding
or lgPadding
properties. In the example, we increase the padding for every breakpoint in either all directions, the x-axis only or the y-axis only.
import { Box, Flex } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" gap={{ column: 0, row: 3 }} height="100%" justifyContent="center" width="100%" > <Box color="darkWash" lgPadding={8} mdPadding={4} padding={0} smPadding={1} > <Box color="successBase" height={40} width={40} /> </Box> <Box color="darkWash" lgPaddingX={8} mdPaddingX={4} paddingX={0} smPaddingX={1} > <Box color="infoBase" height={40} width={40} /> </Box> <Box color="darkWash" lgPaddingY={8} mdPaddingY={4} paddingY={0} smPaddingY={1} > <Box color="warningBase" height={40} width={40} /> </Box> </Flex> ); }
Auto margins
Auto margin is a useful tool when positioning items without using flexbox layouts. By setting any of the margin properties to "auto", the margin will extend to fill the extra space.
This can be seen below, where the 5-column width Box is centered using margin="auto"
and the 3-column width Box uses marginStart="auto"
to automatically adjust the Box to the far edge.
import { Box, Flex } from 'gestalt'; export default function Example() { return ( <Flex height="100%" justifyContent="center" width="100%"> <Box color="infoBase" column={12} marginEnd={12} marginStart={12}> <Box borderStyle="sm" color="successBase" column={5} height={100} margin="auto" /> <Box borderStyle="sm" color="warningBase" column={3} height={100} marginStart="auto" /> </Box> </Flex> ); }
Absolute positioning
Position is static by default but can be made absolute. Box has helpers to help align to absolute edges (top, bottom, left, right). These can be used in combination with padding to achieve desired offsets from edges.
import { Box, Text } from 'gestalt'; export default function Example() { return ( <Box height="100%" width="100%"> <Box color="infoBase" left padding={2} position="absolute" top> <Text color="light">Top Left</Text> </Box> <Box color="infoBase" padding={2} position="absolute" right top> <Text color="light">Top Right</Text> </Box> <Box bottom color="infoBase" left padding={2} position="absolute"> <Text color="light">Bottom Left</Text> </Box> <Box bottom color="infoBase" padding={2} position="absolute" right> <Text color="light">Bottom Right</Text> </Box> </Box> ); }
Using as a ref
The ref
property can be used to anchor a
Popover to a Box.
import { Fragment, useRef, useState } from 'react'; import { Box, Button, Flex, Popover, Text } from 'gestalt'; export default function Example() { const [open, setOpen] = useState(false); const anchorRef = useRef(null); return ( <Fragment> <Flex alignItems="center" direction="column" gap={6} height="100%" justifyContent="center" width="100%" > <Button color="red" onClick={() => setOpen((prevVal) => !prevVal)} text={open ? 'Close Popover' : 'Anchor a Popover to Box'} /> <Box ref={anchorRef} borderStyle="sm" padding={3} rounding={1}> <Text>I'm a Box</Text> </Box> </Flex> {open && ( <Popover anchor={anchorRef.current} idealDirection="down" onDismiss={() => {}} shouldFocus={false} > <Box padding={3}> <Text weight="bold">I'm a Popover anchored to a Box</Text> </Box> </Popover> )} </Fragment> ); }
Z-Index
It's possible to use Box with external elements using the CSS z-index
property by capturing those values in controlled objects. The example below shows using a FixedZIndex
for a value that comes from somewhere else, and a CompositeZIndex
to layer the Box on top of it. Visit our
Z-Index documentation for more details on how to use these utility classes.
import { Box, CompositeZIndex, FixedZIndex, Sticky, Text } from 'gestalt'; export default function Example() { const HEADER_ZINDEX = new FixedZIndex(100); const zIndex = new CompositeZIndex([HEADER_ZINDEX]); return ( <Box column={12} dangerouslySetInlineStyle={{ __style: { isolation: 'isolate' } }} height={150} overflow="scroll" tabIndex={0} > <Sticky top={0} zIndex={HEADER_ZINDEX}> <Box color="errorBase" height={60} padding={2} width="80%"> <Text color="light"> This is sticky and won't move when scrolling </Text> </Box> </Sticky> <Box color="infoBase" height={100} padding={2} position="relative" width="50%" zIndex={zIndex} > <Text color="light"> This will float above the sticky Box when scrolling </Text> </Box> <Box color="successBase" height={120} padding={2} width="30%"> <Text color="light"> This will go behind the sticky Box when scrolling </Text> </Box> </Box> ); }
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. |
Internal documentation
Related
Flex
Use Flex for flexbox layouts, especially when even spacing between elements is desired, by using the gap
property.
Container
Use Container to responsively layout content with a max-width on large screens.
ScrollBoundaryContainer
For proper positioning when using anchored components (Popover, Tooltip, etc.) within a container that could scroll, use ScrollBoundaryContainer.
TapArea
If a tap target is needed in order to click on a portion of the page, use TapArea, since onClick
is not supported on Box.
Sticky
Use Sticky if a portion of the page should stick to either the top or bottom when scrolling.