Overview gives you two questions to pick a step with. This page turns them into specific answers, shows the tokens on the elements themselves, and lists what tends to go wrong.
Inline and block
Inline and block describe direction relative to the flow of the content, rather than fixed sides of the screen.
| Axis | What it follows | In horizontal text |
|---|---|---|
| Inline | the direction a line of text is written | left and right |
| Block | the direction lines and elements are stacked | top and bottom |
For example, padding-inline adds space before and after content on the inline axis, while
padding-block adds space before and after it on the block axis. These logical properties adapt to
right-to-left and vertical writing modes, unlike padding-left, padding-right, padding-top and
padding-bottom.
Decision table
The gap rows are the two questions already answered for common cases. Padding depends on the component, so this table is where to look it up. If your case is not here, the nearest row is usually right.
| You are spacing | Use | Why |
|---|---|---|
| a button's interior, block axis | spacing.xs | text needs less room above it than beside it |
| a button's interior, inline axis | spacing.md | and more beside it, or the label looks off-centre |
| an icon from its label, inside a control | spacing.xs | one control, two parts |
| a chip or badge interior | spacing.3xs | dense by definition. 2xs if it carries an icon |
| an input's interior | spacing.xs block, spacing.sm inline | shorter inline than a button: the value needs to look aligned with the label above it |
| one field from the next | spacing.sm | related, separate |
| one fieldset from the next | spacing.lg | a group boundary |
| a card's interior | spacing.md | the default container padding |
| a card's children from each other | spacing.sm | related items in a group |
| a list row's interior | spacing.sm block, spacing.md inline | rows read as a stack, so the block axis stays tighter |
| a table cell's interior | spacing.2xs block, spacing.sm inline | and then use Compact density rather than shrinking these |
| one section of a card or dialog from the next | spacing.xl | a boundary inside one container |
| one section of a page from the next | spacing.7xl | a page-level boundary |
| one section from the next, on a long page | spacing.8xl | a boundary that has to read as a break in the page |
| a page gutter | spacing.2xl or spacing.3xl | layout, outside any component |
| one major region of a page from another | spacing.5xl | the gap between a sidebar and its content, or a header and the body |
| full-page composition | spacing.9xl | the widest step, for the space around a whole page of content |
Worked examples
Each diagram is rendered from the tokens it names, so the labels cannot describe something the element is not doing. The values are read back from the browser after layout, which is why they follow the density around them.
Padding a control
.button {
padding-block: var(--eds-spacing-xs);
padding-inline: var(--eds-spacing-md);
gap: var(--eds-spacing-xs);
border-radius: var(--eds-corner-radius-rounded);
}
Two steps apart on the two axes is deliberate rather than sloppy. Text needs more room beside it than above it to look optically centred, because the letterforms already carry their own vertical space in the line box. Equal padding on both axes reads as too tight horizontally.
A focus ring
Worth including because it is the one place corner-radius.rounded-outer shows up, and because the
code and the Figma file get there differently.
In code, a focus ring is an outline with an outline-offset. The browser follows the element's
own border-radius and widens it by the offset, so the ring's curve comes out right without naming
a second radius:
.button {
border-radius: var(--eds-corner-radius-rounded); /* 4px at Comfortable */
}
.button:focus-visible {
outline: 2px solid var(--eds-border-interactive-focus);
outline-offset: 1px; /* the ring's inner edge comes out at 5px on its own */
}
Figma has no outline-offset. A ring there is a separate rectangle behind the element, and it needs
its radius set explicitly, one step larger than the element it surrounds. corner-radius.rounded-outer
is that value, which is why it is rounded plus 1px: the same 1px the CSS offset adds.
So the token is a workaround for one tool, rather than a radius with a design meaning. Do not use it in code, and do not use it for nested containers. If a container around a rounded child needs a particular corner, set that corner.
A compact table
Switching density is one attribute on a wrapper. Nothing in the markup below it changes, and no spacing token in it is re-picked:
Well | Status | Depth |
|---|---|---|
31/2-A-14 | Producing | 2 940 m |
31/2-B-07 | Shut in | 3 120 m |
<section data-density="compact">
<table>
…
</table>
</section>
td,
th {
padding: var(--eds-spacing-2xs) var(--eds-spacing-sm);
}
Density can be set on any ancestor and it cascades, so a compact table inside a comfortable page is a normal thing to want. Two cautions:
- Do not nest a looser density inside a tighter one to undo it. Set the density once, where the
content changes.
relaxedinsidecompactinsiderelaxedis legal CSS and unreadable design. - Compact reduces hit area. Less padding means a smaller target, so do not use Compact for a primary touch interface. That is what Relaxed is for.
Do's and Don'ts
- Use
spacing.nonewhen you mean zero. It states the intent and survives a density change - Pick by relationship, so the same pairing gets the same step on every page
- Let density resize things, with one attribute on a wrapper
- Use different steps on the block and inline axes when the optics need it
- Let a focus ring's
outline-offsetproduce its curve, rather than naming a second radius
- Write a hard
0. It is indistinguishable from a value nobody set - Hand-pick a smaller step through a dense area. That reimplements Compact badly and drifts from every other dense view in the product
- Use a spacing token for a component's height. Height falls out of type size plus padding
- Use a spacing token for icon size. They resolve to the same values today, so it works, and it breaks silently the day icon sizing changes
- Use
corner-radius.rounded-outer. It exists for Figma's focus rings, not for code - Space everything equally. If every gap is
md, nothing is grouped
Where to go next
- Token reference: every token and every value, in all three densities.
- The scale: why the steps are these sizes, and how density maps onto them.