This site is a static Next.js app styled with Tailwind, with posts written in MDX. Here's a quick tour of the decisions, mostly so future-me remembers why.
The stack
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js (static export) | App Router, one mental model for pages and games |
| Styling | Tailwind CSS v4 | Design tokens as CSS variables, dark mode for free |
| Content | MDX + gray-matter | Posts are files in a folder, no CMS to babysit |
| Hosting | Cloudflare | Static assets at the edge, effectively free |
Posts are just files
Every post is an .mdx file with a small frontmatter block:
---
title: "Hello, world"
date: "2026-08-04"
description: "The obligatory first post."
tags: ["meta"]
---
Every site needs a first post, and this is it.At build time a small helper reads the folder, parses frontmatter, and computes reading time:
export function getAllPosts(): Post[] {
return fs
.readdirSync(BLOG_DIR)
.filter((f) => /\.mdx?$/.test(f))
.map(parsePost)
.sort((a, b) => (a.date < b.date ? 1 : -1));
}No database, no admin panel. Publishing is git push.
The design system
The look borrows from editorial print more than from tech landing pages: a
warm cream canvas, warm near-black ink, one ember accent, hairline borders,
and display type with tight negative tracking. Every color lives in a CSS
variable, so dark mode is a single class flip on <html> — the games even
read the variables at draw time, so the Snake board re-inks itself when you
toggle themes.
What I'd tell you to steal
- Static export. If your site can be static, make it static.
- Tokens first. Get the palette into variables before styling anything.
- Click-to-play embeds. Video pages load no third-party JS until a visitor actually presses play.