1. Radius
--radius is the only knob (precision/instrument 6px, editorial 2px, swiss/terminal 0, warm 10px).
| Utility | Factor | precision value | Use |
|---|---|---|---|
rounded-xs |
×0.25 | 1.5px | checkbox inner, tiny tags |
rounded-sm |
×0.5 | 3px | badges, kbd |
rounded-md |
×0.75 | 4.5px | small buttons, inputs sm, nav hover pills |
rounded-lg |
×1 | 6px | buttons, inputs |
rounded-xl |
×1.5 | 9px | cards, popovers |
rounded-2xl |
×2 | 12px | dialogs, large panels, product frames |
rounded-3xl |
×3 | 18px | device mockups, hero media only |
rounded-full |
pill | — | avatars, dots, toggles; CTAs only if the brand is pill-shaped everywhere |
Measured most frequent non-pill radius: Stripe 4, Vercel 6, Linear 8, Attio 8, Raycast 11. Pill CTAs on Linear, Vercel, Mercury, Granola. Pick pill or radius-lg for buttons and use it on every button.
Concentric corners: outer = inner + padding.
// Card with an inset image: card rounded-xl (9px), padding 6px → image 3px
<div className="radius-nested surface-card"> {/* --nest-r = radius-xl, --nest-p = 6px */}
<img className="w-full" … /> {/* radius computed automatically */}
</div>
// Tune: className="radius-nested [--nest-p:--spacing(3)]"2. Elevation
| Token | Light (precision) | Dark (precision) | Use |
|---|---|---|---|
shadow-1 |
1px contact shadow | inset 1px top highlight only | resting card, secondary button |
shadow-2 |
ring + 2 soft layers | inset highlight + 1 dark drop | popover, menu, sticky toolbar |
shadow-3 |
ring + 2 layers incl. 32/64 ambient | ring + highlight + deep drop | dialog, hero product frame, terminal focal element |
shadow-(--elevation-highlight) |
inset top light + bottom shade | stronger | primary (brand) buttons |
Editorial has no resting shadows (shadow-1 is empty); swiss has none; terminal uses hard offsets. Don't fight the direction.
Measured recipes worth knowing (already encoded in the direction tokens; use them to judge overrides):
- Linear secondary button:
inset 0 0 0 1px rgb(255 255 255/.03), inset 0 1px 0 rgb(255 255 255/.04), 0 0 0 1px rgb(0 0 0/.6), 0 4px 4px rgb(0 0 0/.1). - Vercel menu:
0 0 0 1px #00000014, 0 1px 1px #00000005, 0 4px 8px -4px #0000000a, 0 16px 24px -8px #0000000f. - Stripe card:
0 30px 45px -30px rgba(50,50,93,.25), 0 18px 36px -18px rgba(0,0,0,.1)(negative spread, navy tint). - Principles: ≥ 2 layers (tight contact + wide ambient), y offset only, alpha ≤ 0.08 per layer in light, tinted with the neutral hue, a ring layer for edge definition.
One separation method per element. Card on bg-bg: border border-line-subtle (dark) or shadow-1 + border (the surface-card utility, which counts as one designed recipe). Don't add hover shadows and border changes and background changes.
3. Product frame (hero screenshots, demos)
<figure className="relative mt-16 lg:mt-24">
<div className="overflow-clip rounded-2xl border border-line bg-surface shadow-3">
<Image src={shot} alt="Acme deploy view showing three branch previews"
fetchPriority="high" loading="eager" sizes="(min-width: 1280px) 1216px, 100vw" className="h-auto w-full" />
</div>
<figcaption className="label-mono mt-4 text-fg-subtle">Real product, staging data</figcaption>
</figure>- Real UI at real size (don't shrink a dashboard to 400px of unreadable pixels; crop instead).
- No browser chrome, no traffic lights, no glow halo, no 3D tilt.
- Dark UI on a light page: frame with
border-line-strong; light UI on a dark page:border-line+shadow-3is enough. - Only the LCP image gets
fetchPriority="high" loading="eager";priorityis deprecated in Next 16 (seespeed).
4. Hairlines and rules
- Divider:
orborder-t border-line-subtle. - 0.5px ring on retina (Clerk 342 uses, Attio 209):
shadow-[0_0_0_0.5px_var(--line-strong)]for small chips only. - Section-head rule (editorial/swiss):
border-t border-line-strong pt-6. - Hairline cell grid:
gap-px bg-lineparent,bg-bgcells (seelayout.md). - Row lists:
divide-y divide-line-subtle(hellohello capability rows: 1px #2e2e2e between service rows).
5. Focus
- Base:
:focus-visible { outline: 2px solid var(--line-focus); outline-offset: 2px }. Outline followsborder-radiusin all modern engines. - Inputs: offset 0 (base does). On a brand-filled button the 2px offset shows the page between button and ring, which reads as a double ring (Vercel/Linear
0 0 0 2px bg, 0 0 0 4px focus). - Composite controls (search field + button):
focus-within:outline-2 focus-within:outline-line-focuson the wrapper. - Never
outline-none/outline-hiddenwithout a replacement. Every hover effect needs afocus-visible:twin.
6. Texture
grainutility: SVG noise at--grain-opacity(direction-set: 0–0.06), behind content (z-index:-1, isolated). Use on the hero section or a large flat band. One grain region per viewport.- On near-black canvases keep grain ≤ 0.02–0.035: measured on the owner's portfolio, 4.5% grain lifted the gutter mean from 5 to 13.9/255 and read as haze.
- Masked grid/dots (only if the concept is technical; an unmasked full-bleed grid is a template tell):
<div aria-hidden="true" className="pointer-events-none absolute inset-0 -z-10
bg-[linear-gradient(to_right,var(--line-subtle)_1px,transparent_1px),linear-gradient(to_bottom,var(--line-subtle)_1px,transparent_1px)]
[background-size:48px_48px] [mask-image:radial-gradient(70%_60%_at_30%_0%,black,transparent)]" />- One light source:
bg-[radial-gradient(60%_70%_at_20%_0%,color-mix(in_oklch,var(--brand)_12%,transparent),transparent_70%)]on an absolutely positioned layer at the top edge. Never two, never multi-hue. - Scanlines (terminal hero only):
bg-[repeating-linear-gradient(to_bottom,transparent_0_2px,color-mix(in_oklch,var(--bg)_60%,transparent)_2px_3px)] opacity-40.
7. Small details checklist
-
presson every button, card link and icon button. -
hit-areaon every target under 24px. - Icon buttons have
aria-label; iconsaria-hidden="true". - Links in prose underlined; nav links not.
- Kbd hints use
(mono 11px, border,shadow-1); platform key rendered after mount. - Selection/caret/accent-color come from base (not overridden).
- Inner scroll areas:
overscroll-contain [scrollbar-width:thin]. - Skeletons match final layout height; no spinner-only pages.
- Empty, error and loading states designed for every data view.
- Favicon +
apple-iconpresent (template file conventions). -
theme-colorequals--bgper scheme.