On this page

Foundations

Layout

Build responsive structure with layout utilities and breakpoints.

Choose a layout component

ComponentUse when
BoxYou need a custom flex layout, or visual utilities
StackDirect children should flow on the logical block axis
ClusterDirect children should flow on the logical inline axis and wrap
GridDirect children need column tracks, rows, or named areas
LockupSupporting content sits beside one flexible body
ContainerContent needs a maximum inline size and local responsive queries
BleedContent should extend beyond the space provided by its parent
AspectRatioMedia should fill a locked inline-to-block ratio
ScrollFadeScrollable content should fade at its edges

Use Grid for a two-dimensional layout with explicit grid structure. Use Stack or Cluster for a single-axis layout. Place a child in a Grid with GridItem.

Box

Box is the general layout element. Use it to:

  • Provide spacing to child elements.
  • Impose sizing constraints on content.
  • Control layout behaviour within flex containers.
  • Hide content responsively.
import { Box } from '@luke-ui/react/box';

<Box maxInlineSize="container.672" padding="space.24">
	{children}
</Box>;

Use semantic HTML when no layout properties are needed. Box is a layout tool, not a replacement for every element.

Responsive values

Properties passed to Box and the other layout components accept either a direct value or an object keyed by breakpoint.

Responsive values resolve against the nearest ancestor size container. Luke UI's stylesheet makes the document root a size container, so the root is the fallback, with or without a theme. Unlayered CSS can override it. Use Container when descendants should respond to a nearer content area. Component-responsive styles always query an ancestor. They do not query the component itself.

Portalled content leaves containers around its trigger and resolves against the nearest size container at its portal location, falling back to the root when there is none.

The root container measures its own content box, which can differ from the browser viewport width by the width of a visible scrollbar. A breakpoint set exactly at the viewport width can miss by a few pixels when a scrollbar is present.

BreakpointMinimum container inline size
initial0px (base)
bp640640px
bp768768px
bp10241024px
bp12801280px
bp15361536px

Each breakpoint is a fixed constant, not a theme token. A custom theme cannot change these sizes. Every breakpoint is a minimum inline size, so there is no maximum size or range condition. A value set at bp640 also applies at bp768 and above, unless a later breakpoint overrides it. Values cascade up from initial, so specify only the points where the layout changes. null and undefined at a breakpoint mean the same as leaving it out. They do not reset the property. To change the value at a breakpoint, pass the new value, such as '0' for spacing.

Import breakpoints from @luke-ui/react/theme when you author @container queries against the same thresholds Luke UI responsive props use.

Layout: Shared breakpoints
First
Second
import { breakpoints, vars } from '@luke-ui/react/theme';
import { ExampleItem } from '#docs';

export default () => {
	return (
		<>
			<style
				dangerouslySetInnerHTML={{
					__html: `
				.layout-breakpoints {
					display: flex;
					flex-direction: column;
					gap: ${vars.space['12']};
				}

				@container (inline-size >= ${breakpoints.bp640}px) {
					.layout-breakpoints {
						flex-direction: row;
					}
				}
			`,
				}}
			/>
			<div className="layout-breakpoints">
				<ExampleItem style={{ flex: 1 }}>First</ExampleItem>
				<ExampleItem style={{ flex: 1 }}>Second</ExampleItem>
			</div>
		</>
	);
};

Layout: Responsive values
Item
Item
import { Box } from '@luke-ui/react/box';
import { Text } from '@luke-ui/react/text';

export default () => {
	return (
		<Box display="flex" flexDirection={{ initial: 'column', bp640: 'row' }} gap="space.12">
			<Item />
			<Item />
		</Box>
	);
};

function Item() {
	return (
		<Box
			alignItems="center"
			backgroundColor="neutral.solid"
			display="flex"
			flexGrow="1"
			justifyContent="center"
			padding="space.16"
			style={{ minBlockSize: '4rem' }}
		>
			<Text elementType="span" fontWeight="label">
				Item
			</Text>
		</Box>
	);
}

Set a responsive value when the layout needs a deliberate change. Prefer intrinsic wrapping when the content can adapt without a breakpoint.

Spacing and sizing

Padding, gap, and margin accept 0 plus value-based spacing keys such as space.16 and space.24. The number in each key reflects its value at the default 16px root and is emitted as rem. Margin also accepts auto. Insets accept the same spacing keys, 0, and auto.

Sizing properties accept 0, auto, 100%, min-content, max-content, and fit-content. Maximum sizes accept none instead of auto, and maxInlineSize also accepts the container.* presets. Use flexGrow, flexShrink, and flexBasis for flex items. There is no flex shorthand. Write zero as the string "0" everywhere except order, which takes a number.

There is no length scale. Put a one-off length on style. When a size changes at a breakpoint, write it in CSS with a @container query built from breakpoints and pass it as className. Read Styling.

Use logical properties such as paddingInline, marginBlockStart, and maxInlineSize. They work in both writing directions without a second layout rule.

Visual styles

The layout properties include theme-backed background, border colour, radius, and shadow values, plus fixed border width and style values. Typography and interaction states stay with a component API. When a custom element that is not a Box needs a visual token, use vars from @luke-ui/react/theme. Semantic values follow the active identity and colour mode, while Luke UI defines its spacing scale and typography styles in source.

Continue learning