On this page

Layout

Box

A layout element with responsive Luke UI layout properties.

Box accepts Luke UI layout properties and renders a div by default. It places content in a layout, adds token spacing, constrains an element's size, or controls a flex child. Use a normal div when no supported layout property applies.

FirstSecond
import { Box } from '@luke-ui/react/box';

export default () => {
	return (
		<Box display="flex" gap="space.12">
			<span>First</span>
			<span>Second</span>
		</Box>
	);
};

Choose a layout

Use Box for a custom flex layout or to control an item within a flex container. Use Grid to define grid tracks or areas, and its GridItem to place a child in a grid. Render a Box through GridItem's renderRoot to style a grid item. Read Layout to choose between layout components.

Size and space content

Use sizing and spacing props to constrain content and set gaps, padding, or margins. Read Layout for the supported values. For a size outside those values, read Styling.

Adapt to available space

Pass responsive values when a layout needs to change with its container. Read Layout for breakpoints and container behaviour.

Choose the rendered element

Set elementType when the layout needs a supported structural element. The prop does not change Box's accepted DOM props. Box owns the element and its DOM attributes.

The supported elements are article, aside, dd, div, dl, dt, figcaption, figure, footer, header, li, main, nav, ol, section, span, and ul.

Box: Semantic element

Account settings

Manage your profile and sign-in details.
import { Box } from '@luke-ui/react/box';
import { Heading } from '@luke-ui/react/heading';
import { Text } from '@luke-ui/react/text';

export default () => {
	return (
		<Box aria-labelledby="account-settings-title" elementType="section" padding="space.16">
			<Heading id="account-settings-title">Account settings</Heading>
			<Text>Manage your profile and sign-in details.</Text>
		</Box>
	);
};

Remove list markers

Set listStyle="none" on a ul or ol Box to remove its markers. The element keeps its list semantics. Browsers do not reset a list's margin and padding, so set them to "0" when the list should not be indented. Safari with VoiceOver (WebKit) can drop list semantics when CSS removes the markers, and adding role="list" restores them. Box and Stack never add the role for you. listStyle accepts only none and has no responsive values. Stack accepts it too.

Box: List markers
  • First list item
  • Second list item
  • Third list item
import { Box } from '@luke-ui/react/box';
import { Text } from '@luke-ui/react/text';

export default () => (
	<Box elementType="ul" listStyle="none" margin="0" padding="0" role="list">
		<Text elementType="li">First list item</Text>
		<Text elementType="li">Second list item</Text>
		<Text elementType="li">Third list item</Text>
	</Box>
);

Own the rendered element

Use renderRoot when the callback must own the element and its DOM attributes. The callback receives domProps containing resolved children, className, style, and a callback ref as its first argument. Its second argument is an empty state object. Put other DOM attributes on the element the callback returns. You may omit or replace supplied props. You then own the consequences.

Do not combine renderRoot with elementType. Use Link or Button when you need their behaviour.

Box: Own the rendered element
More details

This content is revealed when the details element is expanded.

import { Box } from '@luke-ui/react/box';

export default () => {
	return (
		<Box padding="space.16" renderRoot={(domProps) => <details {...domProps} open />}>
			<summary>More details</summary>
			<p>This content is revealed when the details element is expanded.</p>
		</Box>
	);
};

Read Composition for the general elementType and renderRoot rule.

Contain scrolling

Set overscrollBehavior="contain" on a Box that scrolls, such as one with overflowY="auto", to stop the scroll from continuing to the page behind it once the Box reaches its end. The other value is auto, the browser default. The prop accepts responsive values and works on the shared layout components.

Box: Scroll container

Scrolling to the end of this Box does not scroll the page behind it.

Each paragraph adds height, so the Box needs its own scrollbar.

The Box is a focusable region, so the arrow keys scroll it too.

The fourth paragraph keeps adding height to the scrollable area.

The fifth paragraph keeps adding height to the scrollable area.

The sixth paragraph keeps adding height to the scrollable area.

The seventh paragraph keeps adding height to the scrollable area.

The eighth paragraph keeps adding height to the scrollable area.

The ninth paragraph keeps adding height to the scrollable area.

The tenth paragraph keeps adding height to the scrollable area.

The eleventh paragraph keeps adding height to the scrollable area.

The last paragraph reaches the end of the scrollable area.

import { Box } from '@luke-ui/react/box';
import { Text } from '@luke-ui/react/text';

export default () => (
	<Box
		aria-label="Scrollable example"
		borderColor="decorative"
		borderStyle="solid"
		borderWidth="thin"
		overflowY="auto"
		overscrollBehavior="contain"
		padding="space.16"
		role="region"
		style={{ maxBlockSize: '8rem' }}
		tabIndex={0}
	>
		<Text elementType="p">
			Scrolling to the end of this Box does not scroll the page behind it.
		</Text>
		<Text elementType="p">Each paragraph adds height, so the Box needs its own scrollbar.</Text>
		<Text elementType="p">The Box is a focusable region, so the arrow keys scroll it too.</Text>
		<Text elementType="p">The fourth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The fifth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The sixth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The seventh paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The eighth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The ninth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The tenth paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The eleventh paragraph keeps adding height to the scrollable area.</Text>
		<Text elementType="p">The last paragraph reaches the end of the scrollable area.</Text>
	</Box>
);

Visual styles

Box accepts theme-backed background, border colour, radius, and shadow values, plus fixed border width and style values. The values follow the token reference. Use a component API for interaction states and typography. boxShadow accepts inset, resting, raised, floating, and overlay, matching the vars.shadow leaves.

backgroundColor takes a standard surface, such as surface.base, or a role's resting fill, such as accent.subtle or accent.solid. A solid value paints the role's resting fill, so accent.solid is vars.color.background.accent.solid.rest. Box values never carry a state suffix, so accent.solid.rest, accent.solid.hover, and accent.solid.pressed are not accepted. Use vars for the hover and pressed fills. The background also sets the text colour that content inside the Box inherits. Without backgroundColor, a Box keeps the colour it inherits. Read Colour for the colours text reads on each background, and for custom surfaces.

borderColor="control" is a guaranteed control boundary that reaches 3:1 against every surface and every role's subtle background. It paints vars.color.border.control.rest. Box values are state-free, so the hover and pressed borders are theme tokens for your own styles. Each role has a strong border, such as borderColor="warning.strong". It reaches 3:1 against the three surfaces and that role's subtle background, not against arbitrary backgrounds. borderColor="decorative" is a separator. Focus is not a border colour, so borderColor has no focus value. Box does not style focus. When a custom element that is not a Box needs a colour, radius, or shadow value, use vars from @luke-ui/react/theme. Read Styling for when to reach for vars, and the token reference for the contract.

Set borderBlockStartWidth, borderBlockEndWidth, borderInlineStartWidth, or borderInlineEndWidth to none, thin, or thick to control a logical edge. These props override borderWidth on that edge, including with responsive values. Set borderStyle as well: a width alone does not make a border visible when the style is none. The edges follow the writing mode and text direction.

Box: Visual styles
Card headingBackground, border, radius, and shadow values applied together.
import { Box } from '@luke-ui/react/box';
import { Text } from '@luke-ui/react/text';

export default () => {
	return (
		<Box
			backgroundColor="success.subtle"
			borderColor="decorative"
			borderRadius="control"
			borderStyle="solid"
			borderWidth="thin"
			boxShadow="resting"
			display="flex"
			flexDirection="column"
			gap="space.8"
			padding="space.16"
		>
			<Text fontWeight="label">Card heading</Text>
			<Text>Background, border, radius, and shadow values applied together.</Text>
		</Box>
	);
};

Box: Logical borders
A section with a separator
import { Box } from '@luke-ui/react/box';
import { Text } from '@luke-ui/react/text';

export default () => (
	<Box
		borderColor="decorative"
		borderStyle="solid"
		borderWidth="none"
		borderBlockEndWidth="thin"
		borderInlineStartWidth="thick"
		padding="space.16"
	>
		<Text>A section with a separator</Text>
	</Box>
);

API

BoxProps also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.

NameTypeDefault
alignContent
<"center" |⁠ "end" |⁠ "flex-end" |⁠ "flex-start" |⁠ "normal" |⁠ "space-around" |⁠ "space-between" |⁠ "space-evenly" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
alignItems
<"baseline" |⁠ "center" |⁠ "end" |⁠ "flex-end" |⁠ "flex-start" |⁠ "normal" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
alignSelf
<"auto" |⁠ "baseline" |⁠ "center" |⁠ "flex-end" |⁠ "flex-start" |⁠ "normal" |⁠ "stretch"> |⁠ undefined—
backgroundColor
<"accent.solid" |⁠ "accent.subtle" |⁠ "danger.solid" |⁠ "danger.subtle" |⁠ "info.solid" |⁠ "info.subtle" |⁠ "neutral.solid" |⁠ "neutral.subtle" |⁠ "success.solid" |⁠ "success.subtle" |⁠ "surface.base" |⁠ "surface.raised" |⁠ "surface.subdued" |⁠ "warning.solid" |⁠ "warning.subtle"> |⁠ undefined—
blockSize
<"0" |⁠ "100%" |⁠ "auto" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content"> |⁠ undefined—
borderBlockEndWidth
<"none" |⁠ "thick" |⁠ "thin"> |⁠ undefined—
borderBlockStartWidth
<"none" |⁠ "thick" |⁠ "thin"> |⁠ undefined—
borderColor
<"accent.strong" |⁠ "control" |⁠ "danger.strong" |⁠ "decorative" |⁠ "info.strong" |⁠ "neutral.strong" |⁠ "success.strong" |⁠ "warning.strong"> |⁠ undefined—
borderInlineEndWidth
<"none" |⁠ "thick" |⁠ "thin"> |⁠ undefined—
borderInlineStartWidth
<"none" |⁠ "thick" |⁠ "thin"> |⁠ undefined—
borderRadius
<"control" |⁠ "detail" |⁠ "full" |⁠ "overlay" |⁠ "surface"> |⁠ undefined—
borderStyle
<"dashed" |⁠ "dotted" |⁠ "none" |⁠ "solid"> |⁠ undefined—
borderWidth
<"none" |⁠ "thick" |⁠ "thin"> |⁠ undefined—
boxShadow
<"floating" |⁠ "inset" |⁠ "overlay" |⁠ "raised" |⁠ "resting"> |⁠ undefined—
columnGap
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
display
<"block" |⁠ "contents" |⁠ "flex" |⁠ "grid" |⁠ "inline-block" |⁠ "inline-flex" |⁠ "inline-grid" |⁠ "inline" |⁠ "none"> |⁠ undefined—
flexBasis
<"0" |⁠ "100%" |⁠ "auto" |⁠ "content" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content"> |⁠ undefined—
flexDirection
<"column-reverse" |⁠ "column" |⁠ "row-reverse" |⁠ "row"> |⁠ undefined—
flexGrow
<"0" |⁠ "1"> |⁠ undefined—
flexShrink
<"0" |⁠ "1"> |⁠ undefined—
flexWrap
<"nowrap" |⁠ "wrap-reverse" |⁠ "wrap"> |⁠ undefined—
gap
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
inlineSize
<"0" |⁠ "100%" |⁠ "auto" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content"> |⁠ undefined—
inset
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetBlock
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetBlockEnd
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetBlockStart
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetInline
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetInlineEnd
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
insetInlineStart
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
justifyContent
<"center" |⁠ "end" |⁠ "flex-end" |⁠ "flex-start" |⁠ "normal" |⁠ "space-around" |⁠ "space-between" |⁠ "space-evenly" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
justifyItems
<"baseline" |⁠ "center" |⁠ "end" |⁠ "normal" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
justifySelf
<"auto" |⁠ "center" |⁠ "end" |⁠ "normal" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
listStyle
"none" |⁠ undefined—
margin
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginBlock
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginBlockEnd
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginBlockStart
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginInline
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginInlineEnd
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
marginInlineStart
<"0" |⁠ "auto" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
maxBlockSize
<"0" |⁠ "100%" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content" |⁠ "none"> |⁠ undefined—
maxInlineSize
<"0" |⁠ "100%" |⁠ "container.448" |⁠ "container.672" |⁠ "container.896" |⁠ "container.1152" |⁠ "container.1280" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content" |⁠ "none"> |⁠ undefined—
minBlockSize
<"0" |⁠ "100%" |⁠ "auto" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content"> |⁠ undefined—
minInlineSize
<"0" |⁠ "100%" |⁠ "auto" |⁠ "fit-content" |⁠ "max-content" |⁠ "min-content"> |⁠ undefined—
order
<number> |⁠ undefined—
overflow
<"auto" |⁠ "clip" |⁠ "hidden" |⁠ "scroll" |⁠ "visible"> |⁠ undefined—
overflowX
<"auto" |⁠ "clip" |⁠ "hidden" |⁠ "scroll" |⁠ "visible"> |⁠ undefined—
overflowY
<"auto" |⁠ "clip" |⁠ "hidden" |⁠ "scroll" |⁠ "visible"> |⁠ undefined—
overscrollBehavior
<"auto" |⁠ "contain"> |⁠ undefined—
padding
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingBlock
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingBlockEnd
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingBlockStart
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingInline
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingInlineEnd
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
paddingInlineStart
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—
placeSelf
<"auto" |⁠ "center" |⁠ "end" |⁠ "normal" |⁠ "start" |⁠ "stretch"> |⁠ undefined—
position
<"absolute" |⁠ "fixed" |⁠ "relative" |⁠ "static" |⁠ "sticky"> |⁠ undefined—
rowGap
<"0" |⁠ "space.4" |⁠ "space.8" |⁠ "space.12" |⁠ "space.16" |⁠ "space.24" |⁠ "space.32" |⁠ "space.40" |⁠ "space.48" |⁠ "space.64" |⁠ "space.96"> |⁠ undefined—