Build the shape tokens on one 4 px base: spacing, radii, layout, motion, focus and elevation
Context and Problem Statement
Colour (ADR-0013 to 0016) and type (ADR-0020) are tokens; everything else a site needs to draw
itself was still picked by hand. This site alone used nineteen distinct gaps and paddings
(0.15 to 5 rem, several off any grid), four breakpoints (36, 40, 48 and 56 rem) mixing
min-width and max-width queries, two radii, a focus outline and no motion rule. The 0.2
draft had proposed a shape section (“precise, flat, quiet”: a 4 px base, radii 2/4/6/8,
hairlines and bands, one shadow for overlays, a 2 px focus ring, 120/200/320 ms) that was
never ratified or generated. Which values do the twelve sites share for space, corners,
frames, motion, focus and depth, and how is a site kept from inventing its own?
Decision Drivers
Criteria 2 (one coherent, expandable family), 3 (register: a precise instruments maker), 4 (accessibility, WCAG 2.2 AA) and 9 (generated and tested) of ADR-0009.
Considered Options
- One 4 px base with a named ladder, tight radii, declared frames and breakpoints, three durations, one focus ring and one shadow, generated and linted
- A t-shirt scale (xs to xl) chosen by eye per component
- Leave shape to each site and standardise colour and type only
Decision Outcome
Proposed: the first option. The values are data in brand/shape.yaml; brand/src/shape.ts
writes /tokens/shape.css and the space, radius, layout, motion, focus and
elevation groups of /tokens.json; brand/test/shape.test.ts fails the build when a value
leaves its ladder, when the files disagree, or when this site styles with a spacing, radius,
shadow, duration or breakpoint of its own.
- Spacing. A spacing is a whole multiple of 0.25 rem (4 px); the ladder names the
multiples a site may use:
--space-1to--space-24(1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24). Jobs: 1 to 2 inside a control and between a label and its value; 3 to 4 inside a component; 6 to 8 between components; 12 to 20 between sections. Nothing in between: the site’s 0.15, 0.3, 0.35, 0.45, 0.6 and 0.9 rem values snapped to the nearest step. - Radii. 0 on surfaces, cards, tables and images, because tight corners read as precise; 2 px tags and chips; 4 px controls; 8 px dialogs, menus and toasts; pill (999 px) for status; round (50 %) for avatars and dots. The draft’s 6 px card radius is dropped: cards are square, drawn by a hairline or a band.
- Layout. Frames: container 80 rem (1280 px, a site’s page), content 64 rem (1024 px,
text and figure pages, this site), reading 46 rem (736 px, a column of running text; the
68ch measure of ADR-0020 sits inside it). Gutter
--space-4,--space-8from the md breakpoint, switched insideshape.cssso a site writespadding-inline: var(--gutter)once. Twelve columns with a--space-6gap. Breakpoints sm 36, md 48, lg 64, xl 80 rem (576, 768, 1024, 1280 px), published as--bp-*for scripts and used in CSS as literalmin-widthqueries only, mobile-first; the test rejects any other value and anymax-widthquery. Interactive targets at least 1.5 rem (24 px, WCAG 2.2 2.5.8); controls 2.75 rem (44 px) tall. - Motion. Three durations, each the time a change takes to explain itself: 120 ms for a
state (hover, focus), 200 ms to reveal (menus, tooltips, accordions), 320 ms to move
(dialogs, panels). Easing standard
cubic-bezier(0.2, 0, 0, 1)for entering and changing, exitcubic-bezier(0.4, 0, 1, 1)for leaving. Underprefers-reduced-motion: reducethe three tokens become 0 ms inshape.cssitself, so every transition written against them stops without a second rule (WCAG 2.2 2.3.3). - Focus.
--focus-ring: 2 px solid in the accent, 2 px outside the element, on every interactive element (WCAG 2.2 2.4.13 asks for a 2 px perimeter; 1.4.11 for 3:1, which the accent on the page has by ADR-0015). On an accent-filled control the ring is--focus-ring-on-accent, white, inside the edge. - Elevation. Structure is drawn with 1 px hairlines in
--color-lineand with bands (--color-band); no card has a shadow. The one shadow,--shadow-overlay, is for menus, dialogs and toasts: two layers (0 8 24 at 16 %, 0 1 2 at 8 %), deeper in dark mode (60 %, 40 %). Layers as z-index: raised 1, sticky 10, overlay 100, toast 1000. - Brand wears the brand. This site’s every gap, padding, margin, border, radius, outline
and shadow is a token; its media queries are the declared breakpoints,
min-widthonly; the page frame is--contentwith--gutter. The lint in the test suite keeps it so.
Consequences
- Good, because a component written once against the ladder, the radii and the durations renders the same on every site, and a value that drifts fails a build instead of a review.
- Good, because the accessibility floors (target size, focus ring, reduced motion) are tokens and a block of generated CSS, not a checklist.
- The site’s figure annotations keep a few sizes of their own (swatch labels at 9 and 10 px, a glyph sheet at 36 px): they are chart chrome, outside the lint, as ADR-0020 noted for type.
- Open: whether a second shadowed elevation is ever needed; dark-mode shadows beyond a change of opacity; shared components (topic 3.3) will show whether the ladder’s middle steps (5, 10) earn their place.