Spacing tokens define distance, not intent. They are reused for gaps, padding, and margins depending on context. For the full list of spacing token values, see the Tokens page.
The system is built in layers: unit tokens (raw values) → space tokens (semantic aliases) → utility classes (applied in HTML). Always use space tokens or utility classes, never hardcode pixel values.
Space Scale
The space scale provides a visual reference for the spacing tokens used throughout the system.
| Token | Value | px Equivalent |
|---|---|---|
| 0 | 0px | |
| 0.125rem | 2px | |
| 0.25rem | 4px | |
| 0.5rem | 8px | |
| 0.75rem | 12px | |
| 1rem | 16px | |
| 1.5rem | 24px | |
| 2rem | 32px | |
| 2.5rem | 40px | |
| 3rem | 48px | |
| 3.5rem | 56px | |
| 4rem | 64px | |
| 4.5rem | 72px | |
| 5rem | 80px | |
| 5.5rem | 88px | |
| 6rem | 96px | |
| 6.5rem | 104px | |
| 7rem | 112px | |
| 7.5rem | 120px | |
| 10rem | 160px |
2xs · 2px
xs · 4px
s · 8px
m · 12px
l · 16px
xl · 24px
2xl · 32px
3xl · 40px
4xl · 48px
6xl · 64px
8xl · 80px
10xl · 96px
padding: var(--space-m);
gap: var(--space-xl);
margin-bottom: var(--space-l);
Gap
Gap modifiers control the space between child elements inside a . The default gap is .
No gap
Extra Small
Small
Default gap
Large
Extra Large
<div class="block gap-l">
<div>Item one</div>
<div>Item two</div>
<div>Item three</div>
</div>
| Class | Value | px Equivalent |
|---|---|---|
| 0 | 0px | |
| 4px | ||
| 8px | ||
| 12px | ||
| 16px | ||
| 24px | ||
| 32px | ||
| 40px |
Padding
Padding utilities apply internal spacing to an element on all sides.
.padding-s
.padding-m
.padding-l
.padding-xl
<div class="padding-l">
<!-- Content with large padding on all sides -->
</div>
| Class | Value | px Equivalent |
|---|---|---|
| 8px | ||
| 12px | ||
| 16px | ||
| 24px | ||
| 32px | ||
| 40px |
Section Spacing
Section spacing controls the vertical rhythm between major page sections. Apply .top-* and .bottom-* classes to <section> elements. These scale responsively between desktop and mobile.
Top spacing
Bottom spacing
<section class="top-medium bottom-medium">
<!-- Section content -->
</section>
| Class | Token | Desktop | Mobile |
|---|---|---|---|
| / | 32px | 24px | |
| / | 64px | 32px | |
| / | 96px | 56px | |
| / | 160px | 80px |
Interaction Sizing
A control can be smaller than a fingertip; its hit area cannot.--target-min names the floor: 44px — WCAG 2.5.5 Target Size (Enhanced),
Level AAA, and the Apple HIG target. The AA floor is 2.5.8's 24px (a
WCAG 2.2 criterion), which every control in the system clears. Apply the
token as a min-width / min-height on small standalone controls: the
visual size stays, the hit area pads out. It is an accessibility floor, not
a per-theme override surface — brands never re-point it.
Dense chrome is the deliberate exception. Bordered controls inside a Bar
hold --bar-control-height (36px) and answer 2.5.8 instead — a toolbar and
a form on a phone are not the same problem. The native checkbox and radio
sit at the 24px floor exactly, extended in practice by their labels.
| Token | Value | Use |
|---|---|---|
| 44px | Minimum hit area for standalone pointer targets |
The password toggle is the system's own precedent — a small icon control
whose hit area pads out to the floor while the icon stays its size:
.password-toggle {
min-width: var(--target-min);
min-height: var(--target-min);
}
Safe-Area Insets
On an installed or standalone app surface, the device claims parts of the
screen — the notch, the home indicator. The safe-area tokens wrapenv(safe-area-inset-*) with a 0px fallback, so composing them is safe
everywhere: in a normal browser tab they resolve to zero and the layout is
unchanged.
| Token | Value | Use |
|---|---|---|
env(safe-area-inset-top, 0px) |
Notch and status-bar clearance | |
env(safe-area-inset-right, 0px) |
Landscape cutout clearance | |
env(safe-area-inset-bottom, 0px) |
Home-indicator clearance | |
env(safe-area-inset-left, 0px) |
Landscape cutout clearance |
Compose them with spacing tokens rather than replacing them — the inset is
the device's share, the space token is yours:
.app-footer {
padding-bottom: calc(var(--space-m) + var(--safe-area-bottom));
}
.edge-drawer {
padding-inline-start: calc(var(--space-l) + var(--safe-area-left));
}
This is interaction sizing as much as visual clearance: a control anchored
to a screen edge that ignores the inset parks its tap target inside the
system gesture zones, where the OS contests the press — the effective
target shrinks below the floor --target-min names.
The insets are only non-zero when the page's viewport meta opts in withviewport-fit=cover. Without it the browser letterboxes the page clear of
the cutouts and the tokens stay at zero.