Skip to main content
Styles

Layout grids

Draft

We use layout grids to ensure that our content aligns properly on the page.

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

Span

Use CSS grid-column: span N to specify how many grid columns a cell should span.

span 2
span 2
span 2
span 2
span 2
span 2
span 4
span 4
span 4
span 6
span 6

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.

skip 1
skip 1

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.

✓ Do

Align content to the columns. Place elements within column boundaries so the vertical rhythm of the grid is visible.

✕ Don't

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.

Tag
Tag
Tag
✓ Do

Keep intrinsic width components at their default width even if they do not span the columns.

Tag
Tag
Tag
✕ Don't

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.

↔ fluid

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.

flexible🔒 fixedflexible

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.

BreakpointRangeColumnsGutterMarginBehaviour
xs (Mobile)0 to 599px416px16pxFluid, columns stretch
sm (Tablet portrait)600 to 904px816px24pxFluid, columns stretch
md (Tablet landscape)905 to 1239px1224px32pxFluid, columns stretch
lg (Desktop)1240px +1224pxautoFixed max-width, centred
Mobile
4 cols
Tablet
8 cols
Desktop
12 cols

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);
✓ Do

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;
✕ Don't

Hardcoded pixel values for gutters, margins, or section gaps bypass the token system and will drift over time.

Mobile (4) → Tablet (8) → Desktop (12)
✓ Do

Use a mobile-first approach: start with 4 columns and layer on complexity at wider breakpoints.

Desktop (12) → Mobile (?)
✕ Don't

Don't design desktop-first and try to cram 12 columns of content into a 4-column mobile layout as an afterthought.