This portfolio.
A production-grade Next.js site — every effect hand-rolled, every page statically generated.
This site is its own case study: a fully static Next.js 15 app with a custom cursor engine, a procedural three.js hero and a ⌘K palette — built to prove visual ambition and engineering discipline aren't in tension.
- Role
- Design + engineering, solo
- Type
- Open source
- Stack
- Next.js 15 · React 19 · Tailwind 4
- Hosting
- Vercel · fully static
01 — The goal
The goal
A portfolio has one job — get a recruiter to "I want to talk to this person" in under a minute — and one trap: demoing animation libraries instead of engineering judgment. My constraint was that every effect had to be written from scratch, and none of it could cost the first paint anything.
02 — How it's built
How it's built
A Next.js 15 App Router app that renders to fully static output. All copy lives in one typed content layer, so every section, this case study, the resume and the metadata generate from a single source of truth. Interactive surfaces are client components at the leaves; everything else is a server component.
→One typed content layer
Copy, projects and case studies are TypeScript modules — the compiler catches a missing field before a recruiter sees a broken card.
→Server by default
Only the pieces that need the DOM — cursor, palette, 3D, reveals — opt into the client. Every route pre-renders at build time.
→Motion is a garnish
Every reveal honors prefers-reduced-motion and collapses to instant. Keyboard-navigable, focus-styled, skip-link'd throughout.
03 — The performance budget
The performance budget
The three.js particle sphere is the most expensive thing here, so it's treated like a liability: dynamically imported with SSR off, downloaded only after hydration, mounted only on desktop. First paint is pure HTML and CSS. Fonts load with swap so text renders immediately; animations run on transforms and opacity only; the aurora, grain and marquees are pure CSS at zero JavaScript cost. A portfolio that claims to care about performance while scoring poorly is self-refuting — keeping it static and lazy was the whole point.
If you only remember four things
- Fully static Next.js 15 output — no runtime dependencies, instant first paint.
- three.js hero lazy-loaded post-hydration, desktop-only.
- Reduced-motion aware, keyboard navigable, SEO-complete.
- One typed content layer drives every page, including this one.