Skip to content

Reference · craft

Depth and detail

craft/references/depth-and-detail.md109 linesupdated 16 Sept 2026

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.

tsx
// 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)

tsx
<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-3 is enough.
  • Only the LCP image gets fetchPriority="high" loading="eager"; priority is deprecated in Next 16 (see speed).

4. Hairlines and rules

  • Divider:
    or border-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-line parent, bg-bg cells (see layout.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 follows border-radius in 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-focus on the wrapper.
  • Never outline-none/outline-hidden without a replacement. Every hover effect needs a focus-visible: twin.

6. Texture

  • grain utility: 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):
tsx
<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

  • press on every button, card link and icon button.
  • hit-area on every target under 24px.
  • Icon buttons have aria-label; icons aria-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-icon present (template file conventions).
  • theme-color equals --bg per scheme.