---
title: "Spacing and layout"
description: "A 210px rail, a 1200px page measure, 48px gutters: --width-sidebar, --width-page and --gutter-page, so no surface writes a width of its own."
canonical: spacing.html
navigation-title: "Spacing and layout"
---

<a id="spacing-and-layout"></a>

# Spacing and layout

- [Space scale](#space-scale)
- [Distances with a name](#distances-with-a-name)
- [Reading rhythm](#reading-rhythm)
- [The scale, enforced](#the-scale-enforced)
- [Layout frame](#layout-frame)
- [Radius, by role](#radius-by-role)

A 210px rail, a 1200px page measure, 48px gutters: `--width-sidebar`,
`--width-page` and `--gutter-page`, so no surface writes a width of its
own. Section boundaries are full-bleed hairlines. The content inside them
keeps the measure.

**1px grid gaps over a** `--border-subtle` **background** produce the
hairline-separated card grid. It is the system's signature move, and the
reason nothing on the page needs a shadow to stand apart from its neighbour.

The header is sticky, translucent canvas with an 8px backdrop blur. Nothing
else in the system is sticky, transparent or blurry. It **never wraps**. A
header on two lines moves the sticky offset everything below measures
against, so it sheds as the window narrows. [Page layout](../frontend/layout.md) has the
order and the width of each step, in one table.

<a id="space-scale"></a>

## Space scale

A 4px base, halved below 16 and sparser above 24. The half steps,
`--space-0-5`, `--space-1-5`, `--space-2-5`, `--space-3-5`, are the
small end of the same grid. A glyph beside a word and a label over its value
sit at distances 4px is too coarse for. A scale with no step there gets a
literal under it. Above 16 nothing has needed one.

Every step stands in `rem`. So do the type scale, the measures, the
control heights and the widths of the page. The whole grid follows the type
size a reader set in the browser, and a gap grows with the word beside it. A
pixel figure in this manual is what a token draws at the browser's default,
16px to the rem. A pixel stays on what the browser draws rather than sets: a
hairline, a radius, a focus ring, a viewport width a layout changes at.

[Space scale · 700x125](../_cards/guidelines/spacing-scale.card.html)

<a id="distances-with-a-name"></a>

## Distances with a name

A step is a number. A role is what the number is for, and it binds to one
step in `spacing.css`. A component reads the role where the role is what it
means. The reading step then moves once, in every flow at once.

| Role | Step | Where it stands |
| --- | --- | --- |
| `--space-inline` | `--space-1` | a glyph beside a word, a label over its value |
| `--space-cluster` | `--space-2` | between the items of a row: pills, badges, a control's icon and its label |
| `--space-flow` | `--space-4` | the step a text block carries in a reading column: a paragraph, a list |
| `--space-components` | `--space-6` | the step a component carries there, and a bare table, figure, quote or code block |
| `--space-section` | `--space-12` | between the sections of a page |

A boxed block takes `--block-pad-y` by `--block-pad-x`, and every box
shares the horizontal value. So a card, a note, a modal and a code block
start their text on the same edge, in any stack.

<a id="reading-rhythm"></a>

## Reading rhythm

Every block carries its step on both sides, and two margins that meet
collapse into the larger one. A text block carries `--space-flow`. A
component carries `--space-components`, and so does a bare table, figure,
quote or code block. A reader's eye stops at an edge, and a paragraph's
step reads as too little there. So a table stands off the paragraphs on
either side of it by its own step, and nothing adds up.

A heading carries its own air above: `--space-10` above a second level,
`--space-8` above a third and `--space-6` above a fourth. The decreasing
air carries the hierarchy where the heading sizes no longer change. Under
itself a heading keeps the small step, because it belongs to what follows.

One flex gap cannot express this. A gap is a minimum between every pair of
children, and it cannot shrink for the quieter step into a paragraph or a
list. So the step belongs to the block, and the larger one wins where two
meet. A flow where a heading gets a paragraph's air has no hierarchy,
whatever its type size says.

The air is for a heading **on the page**. A `hidden` heading draws no box,
and one in the `sds-said-only` register, heard and not seen, stands out of
the flow. Neither takes air with it.

A box that pays its own gap or padding takes both sides back at its edges.
[Documents](../frontend/documents.md) says when the element keeps its step and when a
container takes it back.

<a id="the-scale-enforced"></a>

## The scale, enforced

The scale holds by construction, not by measurement. Every component draws
its sizes and gaps through its own property set, derived from the tokens.
`make verify ARGS=sets` holds that route. A page composes components
without a style of its own. So a value off the scale reaches a surface only
through a token or a set, where review reads it beside its reason.

A fractional computed size can still be right. An `em` correction relative
to its context is an optical decision, not a new step. Diagrams carry their
own type rules for the same reason.

<a id="layout-frame"></a>

## Layout frame

[Layout frame · 700x152](../_cards/guidelines/spacing-layout.card.html)

<a id="radius-by-role"></a>

## Radius, by role

Radius follows what a thing *is*, not how loud it looks.

| Role | Value | Applies to |
| --- | --- | --- |
| Structural | `0` | section rules, table lines, header underline, hairline grids |
| Control | `--radius-control` 4px | buttons, fields, selects, tabs, badges, **code blocks** |
| Container | `--radius-card` 6px | cards, panels, modals |

A container must not share its corner with its contents. That is the whole
reason the card is one step larger than the control inside it. Hard edges
stay where they do structural work.
