The website you are reading this on. Next.js 16 with the App Router, React 19, TypeScript in strict mode, and a design system built from CSS custom properties rather than a component kit.
Problem
Portfolios tend to be either a list of links with good typography, or a motion demo that loads slowly and hides what it is demonstrating. I wanted the second one to be legible as the first one, on a phone, on a bad connection.
The honest framing: this is the project with the least external constraint and therefore the most self-inflicted scope. It is also the one where the engineering decisions are most visible in the result.
What it does
A single-page scroll structure - hero, work, about, blog, books, contact - plus statically generated detail routes for projects and blog posts, a now page, and a server-rendered OG image route. Content lives in markdown files in the repository, not in a CMS.
How it's built
Animation. Framer Motion is loaded through LazyMotion, so the bundle ships the domMax feature set asynchronously and every component uses the m.* primitives rather than motion.*. GSAP handles the preloader timeline and the scroll-driven work; Motion handles component state. A shared token module exports the easing curves and durations, so no component writes its own bezier literal. Reduced-motion preferences are respected at the provider level, not per component.
Content. Projects are a single YAML-fronted markdown file parsed at build time with gray-matrix's sibling gray-matter; blog posts are one file per post. Both are validated during the build - every referenced image path is asserted to exist under public/, which turns a dead image into a failed build rather than a 404.
Theming. next-themes with semantic colour tokens exposed as CSS custom properties (--color-bg, --color-primary, --color-surface, --color-accent), consumed through Tailwind so bg-background and text-secondary flip with the theme without a single dark: variant. Theme changes go through the View Transitions API for the cross-fade.
Typography. Locally hosted Mona Sans with three optical width axes exposed as font-stretch utilities, and a fluid type scale defined in tailwind.config.js via clamp, so text-display-2 is one token rather than five breakpoints.
Status
Live at iamkhalid.xyz, deployed on Vercel. Currently mid-rebuild toward an editorial layout system - the work detail pages you are reading were part of it.
Credits
- Vercel and the Next.js team
- Motion (Framer Motion) and GSAP
- Mono, for Mona Sans
- The authors of the black-hole references listed on the simulation's own page, whose physics is reused here

