Writing & Thoughts
The Blog
Deep-dives into AI, ML systems, data engineering, and full-stack development — written from the trenches.
Deep-dives into AI, ML systems, data engineering, and full-stack development — written from the trenches.
A behind-the-scenes look at the design decisions, performance optimisations, and animation techniques used to build this very site.
Next.js 16 ships with React 19 and the App Router as the default. The file-system routing, built-in metadata API, and server components make it ideal for a content-heavy portfolio.
Tailwind v4 drops the tailwind.config.js file. Configuration moves into globals.css using @theme:
@import "tailwindcss"; @theme inline { --color-accent-indigo: #6366f1; --color-accent-cyan: #06b6d4; --font-display: "Space Grotesk", sans-serif; }
These become CSS variables and Tailwind utilities automatically. So bg-accent-indigo just works.
"use client"; import { motion, useInView } from "framer-motion"; import { useRef } from "react"; export function Reveal({ children }: { children: React.ReactNode }) { const ref = useRef(null); const inView = useInView(ref, { once: true, margin: "-80px" }); return ( <motion.div ref={ref} initial={{ opacity: 0, y: 24 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }} > {children} </motion.div> ); }
const container = { hidden: {}, show: { transition: { staggerChildren: 0.08 } } }; const item = { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0, transition: { duration: 0.5 } } }; <motion.ul variants={container} initial="hidden" animate="show"> {items.map(i => ( <motion.li key={i} variants={item}>{i}</motion.li> ))} </motion.ul>
"use client" only where needed — server components handle static markup./public — no external image domains needed.next/font/google with display: swap to avoid layout shift.The full source is on GitHub.