The original portfolio was built on Create React App — a tool that is now unmaintained and no longer the right foundation for a production site. This rewrite had clear goals: static generation, proper routing, a scalable theming system, and zero runtime bloat.
The Problem
CRA produces a single-page app with no server rendering and no route-level code splitting. Every page — even a simple blog post — ships the entire React bundle. There is also no path to dark mode without introducing a third-party library or accepting a flash of wrong theme on load.
The Approach
Next.js 16 with the App Router solves routing, static generation, and code splitting in one move. Every blog post and project page is pre-rendered at build time via generateStaticParams — no JavaScript required to read them.
Tailwind 4 replaces the CSS Modules approach from v1. The CSS-first configuration model means no tailwind.config.js — design tokens live in an @theme block inside globals.css alongside the runtime CSS custom properties that power dark mode.
:root {
--color-bg: #ffffff;
--color-ink: #111111;
}
[data-theme="dark"] {
--color-bg: #0a0a0a;
--color-ink: #ededed;
}
The anti-flash pattern is a synchronous inline <script> in <head> that sets data-theme before the first paint. No library, no flash, no hydration mismatch.
The Result
The build produces fully static HTML for every page. The theme system has zero runtime cost — it's a CSS attribute selector switching custom properties. The marching-ants animation from v1 is preserved as a @utility class shared across every bordered component.