Spacing & Layout
Every measure in the system is a multiple of 4 px. That cadence gives us clean 8s, 16s, 24s, and 32s without ever needing a half-pixel, and it stays sharp at any zoom or device-pixel ratio. A 12-column editorial grid carries the page; named slots – inline, stack, inset, section – carry the rhythm inside it.
The base unit
Seventeen stops, from 0 to 192 px. Pick from the scale for any padding, margin, or gap. If a measure feels wrong, try a different stop – never a fractional one.
Spacing slots
Components consume semantic spacing, never primitives directly. Four families, each with t-shirt sizes. That’s what makes the system tunable: change --es-spacing-stack-md once and every form on the site reflows to match.
The 12-column grid
Twelve divides cleanly into 2, 3, 4, and 6 – every classic editorial split. Gutter and outer margin scale with the viewport: tight on phones, generous on desktop. Mobile-first, so design from sm upward.
Breakpoints
Mobile-first. Multi-column layouts kick in at md; full sidebars and dashboards arrive at lg. The xl step is the design-canvas baseline.
| Token | Min-width | Target |
|---|---|---|
--es-bp-xs | 0px | Default – phones, narrow viewports |
--es-bp-sm | 640px | Large phones, small tablets in portrait |
--es-bp-md | 768px | Tablets – first multi-column layouts |
--es-bp-lg | 1024px | Small laptops – nav, sidebars, dashboards |
--es-bp-xl | 1280px | Standard desktop – design-canvas baseline |
--es-bp-2xl | 1536px | Large desktops, wide-bleed layouts |
Gutter and margin
Both scale with the viewport. The grid stays at 12 columns; the breathing room around it grows.
| Breakpoint | Gutter | Page margin |
|---|---|---|
| Mobile (default) | --es-space-4 · 16px | --es-space-6 · 24px |
md (768px+) | --es-space-6 · 24px | --es-space-8 · 32px |
lg (1024px+) | --es-space-8 · 32px | --es-space-12 · 48px |
Container widths
Five widths cover every layout. Long-form caps at prose for readable line lengths; product UI sits inside page; hero spreads stretch to wide.
Radii
The brand is editorial. Surfaces stay close to square (sm is 4 px), with pill reserved for the actions where the wireframes already use it – CTAs, tags, chips. Avatars and dots use full.
Semantic mapping
Components reach for the semantic alias, not the primitive. Buttons stay pill, cards stay sm – swap the alias once and every consumer follows.
| Token | Resolves to | Use |
|---|---|---|
--es-radius-control | sm · 4px | Inputs, selects, checkboxes |
--es-radius-card | sm · 4px | Cards, panels – the magazine feel |
--es-radius-surface | md · 8px | Modals, sheets |
--es-radius-action | pill · 999px | Buttons, tags, chips |
--es-radius-media | sm · 4px | Images, video – sharp, editorial |
--es-radius-circular | full · ∞ | Avatars, status dots |
Borders
The system favours hairlines. Elevation is the exception, the rule is line – that’s the magazine in it. heavy is the editorial accent: a 4 px rule above a section opener, in teal or orange.
Semantic mapping
| Token | Resolves to | Use |
|---|---|---|
--es-border-default | hairline · 1px | Cards, dividers, table rows |
--es-border-focus | thick · 2px | Focus rings, emphasised inputs |
--es-border-accent | heavy · 4px | Editorial rules above section opens |
Elevation
Two-layer shadows for natural depth. The ramp is short and discreet – the page should feel like a printed spread, not a stack of cards. Reach for elevation only when an element physically leaves the surface: overlays, dialogs, dropdowns.
Semantic mapping
| Token | Resolves to | Use |
|---|---|---|
--es-elevation-flat | none | Default – no shadow |
--es-elevation-raised | sm | Cards at rest |
--es-elevation-floating | md | Hover states, dropdown menus |
--es-elevation-overlay | lg | Popovers, tooltips |
--es-elevation-dialog | xl | Modals, sheets, command palette |
--es-elevation-focus | focus ring | 3 px teal ring – keyboard accessibility |
Usage rules
Common patterns and the mistakes to avoid. Spacing discipline is what separates a system from a stylesheet.
Pick from the scale. --es-space-4 (16px) and --es-space-6 (24px) compose without surprises.
Off-grid values cascade. One 13 px decision becomes ten across the codebase, and the rhythm is gone.
.card { padding: var(—es-spacing-inset-lg); }Components reference semantic tokens. Tune the system once; every consumer updates with it.
.card { padding: var(—es-space-6); }Primitives are the alphabet. Components should speak in words – semantic slots – not letters.
Use stack for vertical gaps inside a group and inset for the padding around it. Both stay in sync.
Per-element margin overrides break the rhythm and fight the next person who edits the file.
For three years she ran the calendar of the man who ran the building. The lines fall where the eye expects them, because the column is capped at sixty-five characters.
Reading copy stays inside --es-container-prose (65ch). Lines longer than that lose the reader between sentences.
For three years she ran the calendar of the man who ran the building. When body copy stretches to the page edge, the eye loses its place returning from one line to the next, and reading slows.
Full-bleed prose belongs in feature spreads, not paragraphs. Hold the line length.
Buttons are pill, cards are sm. The two never trade places.
Three or four radii in the same view reads as a prototype, not a publication.
Default to a 1 px border. Editorial brands feel printed when surfaces sit flat to the page.
Reserve elevation for elements that physically lift – modals, hover states – not the resting state.
Tokens to ship
Six namespaces cover every layout decision. Components reach for the semantic alias; primitives exist to be tuned, not consumed directly.
| Namespace | Example tokens | Purpose |
|---|---|---|
| Spacing primitives | --es-space-0 … --es-space-48 | The 4-px base scale. 17 stops, 0 to 192 px. |
| Spacing slots | --es-spacing-inline-*, --es-spacing-stack-*, --es-spacing-inset-*, --es-spacing-section-* | Semantic gaps and padding for components and layouts. |
| Grid | --es-grid-columns, --es-grid-gutter, --es-grid-margin, --es-bp-*, --es-container-* | The 12-column grid, breakpoints, and container widths. |
| Radii | --es-radius-none … --es-radius-full, --es-radius-control, --es-radius-card, --es-radius-action, --es-radius-circular | Corner shapes – primitives plus six semantic roles. |
| Borders | --es-border-hairline, --es-border-thick, --es-border-heavy, --es-border-default, --es-border-focus, --es-border-accent | Line widths – primitive and semantic. |
| Elevation | --es-shadow-xs … --es-shadow-xl, --es-elevation-raised, --es-elevation-floating, --es-elevation-overlay, --es-elevation-dialog, --es-elevation-focus | Two-layer shadows for the five elevation roles. |