Writing
Shaving 400ms off first paint
The hero on this site used to cost 400ms it didn't need to. Lab LCP sat around 2.4s on a throttled mid-tier phone; the Phase 1 budget is under 2.0s. This is how I closed the gap without removing the part that makes the page feel alive.
Where the time actually went
The first version animated the gradient mesh on a <canvas> driven by
requestAnimationFrame. It looked great on my laptop and terrible everywhere
else: the canvas blocked the main thread during hydration, pushing the largest
text block's paint behind a layout it was waiting on. The trace was blunt — a
long task at 1.6s that had no business existing.
The fix was to stop running code for something that was, fundamentally, a picture. The whole mesh is now three radial gradients composited by the GPU:
.hero {
background: var(--mesh-hero);
}
That token (--mesh-hero) is layered radial gradients in OKLCH, defined once in
tokens.css. No canvas, no rAF loop, no main-thread work. The hero paints with
the rest of the document.
Motion without the cost
I didn't want a static page. The trick is that motion shouldn't be on the
critical path. The subtle drift on the mesh is a CSS animation gated behind
prefers-reduced-motion and started only after the LCP element has painted, via
a one-line IntersectionObserver that adds a class. If the animation never
starts, the page is still complete. It's decoration, and it's treated like
decoration.
The other 150ms
Two smaller wins finished the job:
- Font display. The display face was blocking text. Switching to
font-display: swapon the variable display font and preloading only the Latin subset removed the invisible-text stall. - Prerender, don't fetch. The content routes read Markdown at build time and ship as static HTML. There's no API round-trip on the critical path, so the hero text is in the initial response, not a second waterfall.
Result
Lab LCP landed at 1.98s; field data is still coming in but trending the same way. The lesson I keep relearning: before optimizing the JavaScript, ask whether the JavaScript should run at all. A picture is cheaper than a program.