FILE 002 / DISPATCH
How this site is built
2 min readrevised
This site is static. Every page is rendered once at build time and served as a plain file — no server, no database, nothing to patch at 2 a.m.
The stack
Astro generates the site. Posts are Markdown files in
src/content/posts/, validated at build time against a schema, so a typo in a
date or a missing description fails the build instead of shipping.
Styling is Tailwind CSS v4, which is configured in
CSS rather than JavaScript. The whole palette is a handful of custom properties
that swap under a data-theme attribute — that is the entire light/dark
mechanism.
A few pieces worth calling out:
- Search is Pagefind, which builds a static index during the build. Full-text search with no backend.
- Code blocks go through Expressive Code, which follows the same theme toggle as the rest of the page.
- Social preview images are generated at build time, one per post.
- Images are optimised by Astro into AVIF and WebP at several widths. The source photo for the USB-C post is a 5 MB camera PNG; what actually gets served is a few tens of kilobytes.
Hosting and deployment
Hosting is Cloudflare, on the free tier, with Cloudflare handling DNS and the CDN.
Deployment goes through GitHub Actions rather than a git-connected build. The reason is gating: type checking, linting, unit tests, Playwright end-to-end tests, accessibility checks and a Lighthouse budget all have to pass before anything is uploaded. A connected build would deploy first and tell me afterwards.
Pull requests get their own preview URL, so a change can be looked at before it becomes the site.
The whole source is public: github.com/kajoj2/kajoj.com.
What came before
The first version of this site ran on Hugo with the LoveIt theme, deployed to S3. It worked well for two years and there was nothing wrong with it.
The rewrite was about two things. First, a design of my own instead of a theme. Second, wanting the checks above to be part of publishing rather than something I remember to run.
Markdown carried over unchanged, which was the point of writing in Markdown in the first place — notes drafted in Obsidian still paste in and work, callouts included.