You are browsing as a guest. Sign up (or log in) to start making projects!

Open comments for this post

1h 5m 49s logged

intro animation

I wanted the site to feel personal from the first second — not a template fade-in. So I built a layered intro: a hand-drawn signature on load, a curved bubble curtain on navigation, and smooth scroll underneath.

The idea
Three cases, three behaviors:

Hard reload on home → signature draws, fades, done
First visit → signature on dark overlay, then bubble wipes up underneath
Other nav → quick bubble cover so you never flash the old page
Sounds clean. Getting the layers right was not.

What broke
Vivus + React — Vivus owns the DOM; React owns the DOM. Strict Mode double-mounting left ghost SVGs and half-drawn signatures. Fix: runId refs, replaceChildren() on mount, aggressive cleanup on unmount.

The blank frame — The bubble wiped before the new route painted. Fix: double requestAnimationFrame + a 600ms minimum cover so content exists before the curtain lifts.

Three scroll systems — Browser scroll, Lenis, and GSAP ScrollTrigger all had to stay in sync. Route changes kept you scrolled to the previous page; async content (images, fonts, widgets) broke trigger positions. Fix: resetScrollTop() on navigation, ResizeObserver on the body, and a debounced ScrollTrigger.refresh().

Callback restarts — An unstable onComplete re-ran Vivus mid-draw. Stable useCallback + refs fixed it.

Takeaway
The intro is the one thing every visitor sees. It took more debugging than any project page — but that’s why it doesn’t feel like a reskinned template.

0
4

Comments 0

No comments yet. Be the first!