This post is both an introduction to the blog and its living documentation. If future-me forgets how anything here works, this is the page to read.
The stack
- Astro — static site generator. Zero JavaScript by default; the only scripts on this site are the theme toggle, mobile menu, copy-code buttons, and search.
- Markdown content collections — every post is a
.mdfile insrc/content/blog/, validated against a schema with Zod. - Tailwind CSS v4 — with a small set of CSS variables powering the light/dark themes.
- Pagefind — fully static search, indexed at build time. No server, no third-party service.
- Cloudflare Pages — the built
dist/folder is served from Cloudflare’s edge network. There is no runtime at all.
Writing a post
A post is a Markdown file in src/content/blog/. The filename becomes the
URL slug, so my-new-post.md is served at /posts/my-new-post/.
---
title: "My New Post"
description: "One or two sentences shown in cards, search and RSS."
pubDatetime: 2026-09-01T10:00:00Z
tags:
- nodejs
- tips
---
Content goes here. Regular **Markdown** works, including code blocks
with syntax highlighting.
Frontmatter reference
| Field | Required | Notes |
|---|---|---|
title | yes | Post title |
description | yes | Used in cards, meta tags and RSS |
pubDatetime | yes | ISO date; future dates stay hidden until then |
modDatetime | no | Set when you edit a published post |
tags | no | Defaults to ["others"] |
featured | no | Pins the post to the Featured section on home |
draft | no | true = visible in dev, excluded from builds |
ogImage | no | Custom social image, else /og.png is used |
canonicalURL | no | If the post was first published elsewhere |
Callouts
Pull a warning or an aside out of the body copy with GitHub-style alert
syntax. Five types are available — NOTE, TIP, IMPORTANT, WARNING
and CAUTION:
> [!NOTE]
> Useful context that is not essential to the main point.
> [!WARNING] This one has a custom title
> Anything after the marker becomes the heading.
They render like this:
NoteCallouts are plain blockquotes until a remark plugin spots the marker, so they degrade gracefully anywhere else Markdown is read — in your editor, on GitHub, or in the RSS feed.
TipPress / or ⌘K anywhere on the site to open search without reaching for the mouse.
Set your site URL before deploying
SITE.websiteinsrc/config.tsdrives canonical URLs, the sitemap and every Open Graph tag. Publishing with the placeholder in place means search engines index the wrong domain.
Local workflow
npm run dev # write with live reload at localhost:4321
npm run build # build + generate the Pagefind search index
npm run preview # serve the production build locally
Deploying
The site deploys to Cloudflare Pages either from git (push to the production branch and Cloudflare builds it) or directly from the terminal:
npm run deploy # builds, then `wrangler pages deploy dist`
That’s the whole system. Posts are files, the site is static, and the platform is someone else’s problem — exactly how a personal blog should be.