How this site is built

Every blog needs a post about how the blog is built. This is that post. It’s short, because the setup is short, which was the point.

What I wanted

  • Write posts as Markdown files, in a text editor, committed to git.
  • No CMS, no database, no admin panel to keep patched.
  • Fast to load and cheap to run — ideally free.
  • Looks like a well-set page, not a dashboard.
  • Nothing I’d have to relearn if I came back to it after six months.

What I picked

Astro for the site itself. Astro’s whole pitch is “content site, ships zero JavaScript by default,” which is exactly the pitch I was looking for. Posts live in src/content/blog/ and Astro’s content collections validate the frontmatter with a schema at build time — so a typo in a date is a build error, not a mystery on the live site:

const blog = defineCollection({
	loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }),
	schema: z.object({
		title: z.string(),
		description: z.string(),
		pubDate: z.coerce.date(),
		updatedDate: z.coerce.date().optional(),
		draft: z.boolean().default(false),
	}),
});

The draft flag is the one bit of workflow I added: drafts show up in the dev server and are filtered out of production builds, so I can keep half-written posts in the repo without accidentally publishing them.

Cloudflare Workers for hosting. I already had the domain on Cloudflare, and Workers with static assets means the build output — plain HTML, CSS, and fonts — is served from their edge, with a small worker sitting in front for anything that isn’t a static file. The free tier covers a personal blog many times over.

Deploying is two commands:

npm run build
npm run deploy

The first produces dist/; the second hands it to wrangler. There’s no CI yet. When the friction of running two commands from a laptop becomes noticeable, I’ll add a GitHub Action, and not before.

The design

Warm off-white background, one accent color, a serif for headings and Atkinson Hyperlegible for body text. The serif comes from whatever your operating system already has (Iowan Old Style on a Mac, Palatino or Georgia elsewhere), so there’s nothing to download. Dark mode is a prefers-color-scheme media query that swaps a dozen CSS variables.

The whole stylesheet is a couple hundred lines. There is no client-side JavaScript at all — view source, if you like; it’s all there.

What I’d change

Nothing yet. Ask me in a year.