How this blog was built
A series on the technical and design decisions behind Sourcier
- Published
- 30 March 2026
- Read time
- 6 min read
Was this useful?
When I launched this blog last week I wrote a post called I Should Start a Blog, the short version of why I’d stopped putting it off. Part of what I mentioned there was that building the site itself had been an enjoyable project: a proper excuse to use Astro in earnest, deploy serverless functions on Netlify, wire up email delivery through Resend.
What I didn’t write about was how any of that actually works.
The series
I’ve decided to fix that. Starting today, I’m publishing a series of posts that document how this blog was built: the technical decisions, the design choices, the problems that needed solving and how I solved them.
It is, admittedly, a slightly self-referential thing to write a blog about the blog. But I think there’s genuine value in it. These are patterns and decisions that come up on almost any content-driven site. The answers I landed on aren’t unique to this project. They’re worth writing up properly.
The series covers:
- Choosing the tech stack — Live — the full stack decision: Astro for static output and content collections, Bulma for a no-runtime CSS foundation, Netlify for hosting and serverless functions, Font Awesome, Resend, and the other tools that hold it together.
- Typed content collections — Live — Zod schemas, build-time validation, the
draftflag pattern, and what typed frontmatter actually buys you. - Dark/light theme toggle — Live — CSS custom properties,
localStoragepersistence, and why not getting this right causes the flash of wrong theme. - Blog card and post hero design — Live — typography hierarchy, cover image pipeline, the draft overlay.
- Building a tag system in Astro — Live — slug normalisation, publication-aware counts, the blog archive cloud, a concentric ring layout, paginated tag pages, and the post sidebar browser.
- OpenGraph and SEO metadata — Live — OG tags, Twitter Cards, canonical URLs, article metadata — all centralised in one place.
- RSS in Astro — Live — the
@astrojs/rsspackage, draft filtering, autodiscovery. - Breadcrumb navigation with Schema.org markup — Live — auto-generated crumbs,
BreadcrumbListstructured data, accessiblearia-current. - A share widget with the Clipboard API — Live — LinkedIn, Reddit, email, and copy-link with in-place feedback and a graceful clipboard fallback.
- Page history and credits on a static blog — Live — transparent revision logs, attribution as a first-class concern, and why both belong in the schema.
- Deploying to Netlify — Live —
netlify.toml, cache headers, serverless and edge functions, environment variables, and passcode-gated deploy previews. - Post notification emails with Resend — Live — a Node.js script that reads frontmatter, builds an HTML email, and sends a broadcast to subscribers.
- Scheduled publishing — Live — a
isPublished()helper that gates on both draft status and pubDate, a Netlify scheduled function, and a daily build hook so future-dated posts go live automatically. - Adding a mailing list — Live — Resend’s Segments API, a serverless subscribe function, a shared form utility, honeypot spam protection, and a welcome email with Resend template support.
- Comments on a static site — 26 May — Netlify Forms as a queue, HMAC-signed approve/delete links, three serverless functions, no database.
- Mermaid diagrams — 28 May — bypassing Expressive Code, client-side rendering, theme-aware SVGs, a fullscreen lightbox.
- Better code blocks — 2 June — syntax highlighting with Expressive Code, dual themes, line numbers, frames, and markers.
- Sticky table of contents — 4 June — build-time heading extraction, IntersectionObserver active state.
- Pagination — 9 June — clean URLs, skeleton placeholder cards, no client-side JavaScript.
- A custom 404 page — 11 June — outlined text, a ghost effect, and why a dead end is worth designing properly.
- Web analytics on a static Astro blog — 16 June — picking PostHog from the free options, loading the snippet only in production, and wiring up credentials with Netlify environment variables.
- Moving blog content to a private repository — 18 June — splitting Markdown content into a private GitHub repo, a build-time clone with a fine-grained token, and a GitHub Actions workflow to trigger deploys on content changes.
- Adding search — 19 June — Pagefind for static site search, a command-palette header modal with keyboard navigation, and the non-obvious problems with meta elements, asset hashing, and scoped styles.
- Emoji reactions with Netlify Blobs — 23 June — serverless key-value storage, a Netlify Function API, and an optimistic UI with pop animations and localStorage deduplication.
- Automating Dev.to cross-posting — 26 June — a Node.js script that reads your frontmatter, normalises tags, makes image URLs absolute, sets the canonical URL, and publishes in one command.
- Reading times in Astro — 30 June — the
reading-timenpm package, flowing the value through blog cards and post pages, and fixing a lurking TypeScript JSX misconfiguration. - Setting up ESLint, Prettier, and Husky — 2 July — flat config linting for Astro and TypeScript, Prettier with Astro support, staged pre-commit checks, and build-time enforcement through Netlify.
That’s twenty-six posts rolling out through to early July. Some are short, covering a single technique worth documenting cleanly. Others go deeper. All of them are grounded in code that actually runs on this site right now. Subscribe below and you’ll get each one the morning it drops: no noise, just the article.
Why write this at all
Two reasons.
The first is that documenting things properly forces me to understand them better. When I built the comment system I had a rough sense of how the HMAC signing worked. Writing it up properly, in a way that another engineer could follow, required me to be precise about the details. Every single post in this series has taught me something about my own implementation.
The second is that I genuinely couldn’t find a comprehensive write-up for most of these patterns when I was building them. There are fragments: a hint in the Astro docs, a Stack Overflow answer that’s half-right, a GitHub issue with a workaround. Writing them up in one place, with enough context to be useful, seems worth doing.
Working on something similar?
Need help raising the bar?
I help teams improve engineering practice through hands-on delivery, pragmatic reviews, and mentoring. If you want a second pair of eyes or practical support, let's talk.
- Engineering practice review
- Hands-on delivery
- Team mentoring
If this has been useful, you can back the writing with a one-off tip through a secure Stripe checkout.
Free · Practical · One email per post
Get practical engineering notes
One short email when a new article goes live. Useful if you are breaking into tech, growing as an engineer, or improving engineering practice on your team.
Comments
Loading comments…
Leave a comment