Skip to content
All projects
SystemsDesigner & Developer2026

This Website

The site you're reading right now: statically generated Next.js 16 with MDX content, aurora-grade animations, and a Docker + Nginx + Certbot pipeline that deploys itself.

Next.js 16Tailwind CSS 4MDXReact BitsDocker

The brief I gave myself#

Build a personal site that stays maintainable in five years: content lives as MDX in Git, pages render statically, animations impress without turning the bundle into a landfill, and deployment is one git push. No CMS, no database, no server babysitting.

Architecture#

  • Next.js 16 App Router with static generation for every page. Articles and project case studies are .mdx files under src/content/, read at build time.
  • MDX pipeline — @next/mdx with remark-gfm, rehype-slug and rehype-pretty-code for syntax-highlighted, anchor-linked articles.
  • Counting-free components: the aurora hero, split-text headings, spotlight cards and marquee are adapted from React Bits building blocks, tuned to share one accent palette.
  • Route handlers for the only dynamic bit: a minimal health check for the container (the contact page is deliberately static and hands off to the visitor's mail client — no form endpoint, no stored messages).

The deployment pipeline#

git push → GitHub Actions
  ├─ build & push Docker image to GHCR (cached)
  └─ ssh to server → docker compose pull && up -d
                      └─ Nginx reverse proxy + Certbot SSL

The server runs two containers — the Next.js standalone image and Nginx — plus a Certbot renewal timer. Nothing else. The whole footprint fits in a small VM beside the family server it shares.

Details I refused to skip#

  • Real metadata. Per-page titles, canonical URLs, Open Graph cards rendered at build time, an RSS feed and a sitemap — SEO hygiene, done once.
  • Content/code separation. Publishing a post means adding one .mdx file; changing a design means touching components. The two never mix.
  • Performance as respect. Fonts subset, animations respect prefers-reduced-motion, and WebGL backdrops are scoped so the writing stays readable.

What's next#

A uses page for the tools I actually use, per-post social cards, and — when a feature genuinely needs state — a tiny SQLite read model behind the route handlers. Not before.