← cd /blog

Article

Building This Site

·
buildsnext.jsmeta

Here's how this site works. Not because it's particularly novel (it's a Next.js blog), but because writing it down is how I figure out if I actually understand what I built.

The stack

  • Next.js 16 with the App Router and static generation
  • TypeScript throughout
  • Tailwind CSS v4 with CSS-first configuration (@theme inline)
  • MDX for blog posts, parsed with gray-matter and rendered with next-mdx-remote/rsc
  • Dark-only. No toggle, no light mode
  • Deployed on Vercel with a custom domain

Design system

The visual language is documented in the brand book: typography, colour tokens, components, and the retro layer. Go look at it, it's pretty.

Blog architecture

Posts live as .mdx files in /content. Each file has YAML frontmatter:

---
title: "Post Title"
date: "2026-02-13"
description: "SEO description under 160 chars."
tags: ["builds", "meta"]
---

At build time, gray-matter parses frontmatter and next-mdx-remote/rsc renders the MDX as a React Server Component. The blog index reads all files, sorts by date, and statically generates each page via generateStaticParams:

export async function generateStaticParams() {
  const posts = getAllPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

No CMS, no database, no API. Just files in a repo.

Dark-only

No theme toggle. No light mode. The <html> tag gets className="dark" and that's it. CSS custom properties in :root define the palette once; Tailwind v4 maps them via @theme inline. One colour scheme to maintain, zero runtime JS for theming.

It started with next-themes and a toggle, but having two palettes meant testing two palettes. Removing it cut a dependency, deleted two components, and simplified the CSS. Dark felt right for a site called testing in production.