---
title: "Depth and detail (craft)"
description: "Measured most frequent non-pill radius: Stripe 4, Vercel 6, Linear 8, Attio 8, Raycast 11. Pill CTAs on Linear, Vercel, Mercury, Granola."
canonical: https://void-design.vercel.app/docs/craft/depth-and-detail
lastModified: 2026-09-16
---

# Depth and detail

## 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: `<hr className="border-line-subtle" />` 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 `<Kbd>` (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.
