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.