Layout grids
DraftWe use layout grids to ensure that our content aligns properly on the page.
- Usage
- Tokens
- Code
- Status & changelog
Common alternative names
Layout grids, responsive grids, viewport grids, display grids
Anatomy
The layout grid consists of 3 elements:
Columns
This is where the content aligns to. Column sizes change based on the size of their container.
Gutter
The fixed space between columns. This space remains the same even if the container size changes.
Margin
Space between the outer columns and its container. This space remains the same even if the container size changes.
Usage
Content
Align content to the columns on the grid. Any cells that do not fit on a single row will wrap to a new row.
Span
Use CSS grid-column: span N to specify how many grid columns a cell should span.
If there aren't enough columns to accommodate the span, it will wrap to the next row.
Hide
When content should be hidden at certain breakpoints, remove it from the flow entirely. This is useful for responsive layouts where some sections may be hidden on a specific breakpoint.
Skip
Another common layout scenario is "offsetting" or "skipping" columns. Use grid-column-start to move content to a specific column position.
Fixed-width content
You can specify content to have a fixed arbitrary width. The space of this element and the content next to it will be as wide as the gutter of its parent. The rest of the content will remain aligned to the outer grid. This is useful for things like a side-nav component.
Align content to the columns. Place elements within column boundaries so the vertical rhythm of the grid is visible.
Align content to the gutters. Gutters are reserved to provide space between content blocks.
Exceptions
Intrinsic width items like tags and pill buttons don't need to span all the columns in a layout. Keep them at the default width.
Keep intrinsic width components at their default width even if they do not span the columns.
Stretching intrinsic width components to fill a grid wastes space.
Behavior
You can set the layout grid to behave in different ways.
Fluid
By default, the layout grid will take the container's full width. Columns stretch proportionally to fill the available space while gutters and margins remain fixed.
Fixed
You can give the layout grid a fixed size, which will place it either centred or left/right aligned to the container. Margins become auto on large screens.
Hybrid
You can combine multiple behaviours on a single screen. For example, a fixed sidebar alongside a fluid content area.
Breakpoints
A container's horizontal size defines which layout grid it should be using. When a container reaches a certain different size a new layout grid will be applied. We're calling the sizes where this happens a breakpoint.
Resizing a container past a breakpoint will update its layout grid.
| Breakpoint | Range | Columns | Gutter | Margin | Behaviour |
|---|---|---|---|---|---|
| xs (Mobile) | 0 to 599px | 4 | 16px | 16px | Fluid, columns stretch |
| sm (Tablet portrait) | 600 to 904px | 8 | 16px | 24px | Fluid, columns stretch |
| md (Tablet landscape) | 905 to 1239px | 12 | 24px | 32px | Fluid, columns stretch |
| lg (Desktop) | 1240px + | 12 | 24px | auto | Fixed max-width, centred |
Content
As the layout grid changes at breakpoints, the content adjusts to the new grid. Elements that span a fraction of the desktop grid should reorganise to span proportionally more columns on smaller grids, or stack vertically when appropriate.
| Desktop (12 cols) | Tablet (8 cols) | Mobile (4 cols) |
|---|---|---|
| 3 of 12 (25%) | 4 of 8 (50%) | 4 of 4 (100%) |
| 4 of 12 (33%) | 4 of 8 (50%) | 4 of 4 (100%) |
| 6 of 12 (50%) | 8 of 8 (100%) | 4 of 4 (100%) |
| 8 of 12 (67%) | 8 of 8 (100%) | 4 of 4 (100%) |
| 12 of 12 (100%) | 8 of 8 (100%) | 4 of 4 (100%) |
Responsive content
All the properties you have available to position your content are responsive. They will accept multiple values that will be executed on the different breakpoints.
Span: Multiple span values will change the number of columns a cell spans at every breakpoint.
Hide: Setting the span to 0 will hide the cell. You can use this to show and hide a navigational element at certain screen sizes.
Skip: You can do the same for offset. A cell can have a span of 3 columns but a different skip for each breakpoint.
Sub-grids
In order to sub-divide areas of the grid, you can use sub-grids: areas on your base grid that use a separate grid. A column region can itself contain a sub-grid. When nesting, the inner grid uses the parent column's width as its total width and applies the same gutter value.
For this, for every grid breakpoint, we provide a version with the margins removed, making it easier to align to the rest of the content. Like the base layout grid, the width of these areas dictates which grid to use.
Maximum content width
On large screens (1240px and above), the grid stops expanding. Content locks to a maximum width of 1200px and centres itself horizontally. Margins become auto, preventing ultra-wide layouts from stretching content to the point of poor readability.
Do / Don't
padding: 0 var(--spacing-layout-margin);Use semantic layout tokens for all grid spacing. This keeps your layout in sync with the rest of the system when token values are updated.
padding: 0 16px;Hardcoded pixel values for gutters, margins, or section gaps bypass the token system and will drift over time.
Use a mobile-first approach: start with 4 columns and layer on complexity at wider breakpoints.
Don't design desktop-first and try to cram 12 columns of content into a 4-column mobile layout as an afterthought.
Layout spacing tokens
These are the layout-related spacing tokens available in the system. All are defined in packages/tokens/src/semantic/spacing.json.
Semantic tokens
| Token | CSS variable | Value | Purpose |
|---|---|---|---|
| spacing-layout-page-gutter | --spacing-layout-page-gutter | 16px | Page-edge margins on mobile; small-screen gutters |
| spacing-layout-content-gap | --spacing-layout-content-gap | 24px | Gap between content blocks; desktop gutters |
| spacing-layout-section-gap | --spacing-layout-section-gap | 48px | Vertical space between major page sections |
Primitive tokens used by the grid
| Token | CSS variable | Value | Grid role |
|---|---|---|---|
| spacing-16 | --spacing-16 | 16px | Mobile gutter and margin |
| spacing-24 | --spacing-24 | 24px | Desktop gutter, tablet margin |
| spacing-32 | --spacing-32 | 32px | Tablet landscape margin |
| spacing-48 | --spacing-48 | 48px | Section gap between page regions |
Breakpoint reference
| Name | Min width | Max width | Columns | Gutter | Margin |
|---|---|---|---|---|---|
| xs | 0px | 599px | 4 | 16px | 16px |
| sm | 600px | 904px | 8 | 16px | 24px |
| md | 905px | 1239px | 12 | 24px | 32px |
| lg | 1240px | 12 | 24px | auto |
Implementation
Arch UI layout grids are implemented using CSS Grid. The token-backed approach means you never hardcode pixel values for gutters or margins.
/* Mobile-first fluid grid */
.page-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--spacing-layout-page-gutter); /* 16px */
padding: 0 var(--spacing-layout-page-gutter); /* 16px margins */
}
/* Tablet — 8 columns, wider margins */
@media (min-width: 600px) {
.page-grid {
grid-template-columns: repeat(8, 1fr);
padding: 0 var(--spacing-layout-content-gap); /* 24px margins */
}
}
/* Desktop — 12 columns */
@media (min-width: 905px) {
.page-grid {
grid-template-columns: repeat(12, 1fr);
gap: var(--spacing-layout-content-gap); /* 24px */
padding: 0 var(--spacing-32); /* 32px margins */
}
}
/* Large desktop — fixed max-width, centred */
@media (min-width: 1240px) {
.page-grid {
max-width: 1200px;
margin: 0 auto;
padding: 0;
}
}
Status & changelog coming soon.