Next.js 16, React 19, TypeScript, Payload CMS self-hosted in the same app, MongoDB, Tailwind CSS v4, Motion, Cloudflare R2 and PostHog.
The stack wasn't the interesting part though. Most of the work went into making the interactions feel like they weren't fighting you.
Things that took far longer than expected:
Zoomable images: Click to open, drag to dismiss the way the iOS photo viewer does. The zoom itself was never the hard part. The state was. Open, closed and the return transition all had to agree with each other, and the first two versions flickered or glitched on the way out. Took three iterations before it was clean.
A draggable marquee: You can grab it, throw it, and it skews based on its own velocity before easing back to its resting speed. Tiny interaction. Easily the most underestimated thing in the build.
Custom video player: Built it instead of reaching for a library because on this site the videos are the product rather than embedded media. Full control over the controls layout, the loading behavior and the easing was worth the extra work.
The AI Videos page: It doesn't scroll natively. Wheel, drag and keyboard all drive the same transform-based system that snaps to one project at a time. I wanted one video in front of you rather than two half visible. I like the result and it's also the decision I'm least sure about.
One non-interaction thing I'd do again on every project:
Content negotiation for AI crawlers. Anything sending \`Accept: text/markdown\` gets markdown back instead of the rendered page. I also wired up analytics to check whether it's actually doing anything, counting crawler hits and splitting AI agents by intent. GPTBot, Perplexity, OpenAI Search and Bingbot all show up, roughly 2 to 8 requests a day. I can't prove they're reading the markdown over the HTML. Only that they arrive and it's sitting there for them.
Motion is never mandatory here. \`prefers-reduced-motion\` is respected throughout.
I'd really appreciate eyes from people who haven't been staring at this for six weeks:
* Does anything feel slow or awkward? * Any interaction that feels unnecessary or in the way? * How does it hold up on Android Chrome or older iPhones?