Theme
On this page (10)

§Spacing on the same base

Gaps, padding and margins use n × --size-base × --layout-density. Density is 1 by default. gap-4 therefore matches 1rem at the default base; w-4 remains geometric when density changes. The px suffix always means one CSS pixel and is not density-scaled. Below, the three everyday jobs of spacing: gap-2 between related actions, gap-6 between independent cards, p-6 inside a card.

§The box model, labeled

Every spacing utility addresses one zone of the box model: m-* works outside the border (against siblings), p-* inside it (against content). The physical sides (t/r/b/l) never move; the logical sides (s/e) follow the writing direction. The diagram uses ordinary CSS colors to make the zones visible - the classes label what each zone is.

§Gap axes

gap-x-* sets column-gap, gap-y-* sets row-gap - the two axes are independent. A calendar makes that tangible: days of a week sit gap-x-3 apart while the weeks stack tightly at gap-y-1.

The same split works for masonry-like walls and tag clouds: keep rows tight (gap-y-2) so they read as one group, and let columns spread (gap-x-6) so entries don't collide.

§Padding families

Padding shares the numeric scale with gap. p-* pads all four sides; px-* and py-* pad the inline and block axes independently - the recipe for chips, badges and inputs; a single-side helper like pt-* adds space in one direction, useful for optical alignment against a divider or media edge. The dashed outline marks the box edge: the band between it and the filled content is the padding.

§Centering and pushing with margins

Automatic margins absorb spare space. mx-auto on a fixed-width box centers it in its container - the classic content column. ms-auto on one flex item pushes it, and everything after it, to the inline end - the standard right-aligned toolbar action. Negative margins and margin-based space-x/y shortcuts are intentionally omitted.

§Physical vs logical sides

The axis families already follow the writing mode - px-* is padding-inline, py-* is padding-block. For single sides there are two families: physical pt/pr/pb/pl-* always hit the same physical edge, while logical ps/pe-* follow inline-start and inline-end. The same classes render below in dir="ltr" and in dir="rtl": the logical box mirrors with the direction, the physical box does not move. The filled band is the content box; the empty bands are the padding.

§Spacing families

Every family in this table is demonstrated in the sections before it.

ClassesCSS property
gap-*, gap-x-*, gap-y-*gap, column-gap, row-gap
p-*, m-*padding, margin
px-*, mx-*padding-inline, margin-inline
py-*, my-*padding-block, margin-block
pt/pr/pb/pl-*, mt/mr/mb/ml-*Physical top/right/bottom/left.
ps/pe-*, ms/me-*Logical inline-start/inline-end.
m/mx/my/mt/mr/mb/ml/ms/me-autoAutomatic margins.

Every family shares the numeric scale - rendered bar by bar on Width & Height - plus px. Negative margins and margin-based space-x/y shortcuts are intentionally omitted. Gap does not create outer margins and works with wrapping layouts.

§Real world: a receipt

A whole component on one rhythm: p-6 around the card, stack gap-3 between line items, ms-auto to push amounts right, and mt-4 pt-4 with a top border to separate the total. No custom CSS anywhere.

§Custom gaps and cascade

<div class="stack" style="--layout-gap:clamp(1rem, 2vw, 2rem);">
  <p>First block</p>
  <p>Second block</p>
</div>

A --layout-gap override configures a layout primitive. An explicit gap-* helper overrides that primitive default. To override a helper, use normal unlayered application CSS. Avoid conflicting helpers; class-attribute order does not determine CSS precedence. Existing documentation helpers are unlayered and will win unless removed or isolated.

§Platform references

CSS cascade layers · CSS logical properties

Comments, ideas or improvements? Edit this page's source on GitHub