Table of contents
- Why v2.0?
- The design direction
- Migrating to Tailwind CSS v4
- AI-assisted development
- Deploying to Render.com — a Tailwind 4 gotcha
- Conclusions
Why v2.0?
When I built v1.0 of this site, I was mostly focused on getting something working and learning Astro at the same time. It was a white, light-themed website — perfectly fine, but it didn’t really feel like me. It looked like most developer blogs on the internet. Nothing wrong with that, but I wanted something with a bit more character.
I also wanted to give Tailwind CSS v4 a proper go. The migration from v3 is more significant than a typical minor version bump, and I thought a full redesign was a good excuse to do it properly rather than bolt it onto an existing project.
The design direction
I wanted a dark, terminal-inspired aesthetic. The kind of thing that says “this person writes code for a living and is not embarrassed about it.” I looked at a few reference sites for inspiration and landed on a design that uses:
- Near-black backgrounds (
#080808) - JetBrains Mono for headings and navigation — monospace fonts give a very deliberate, technical feel
- A small set of accent colours: terminal green for interactive elements, purple for .NET references, amber for Zig, and sky blue for AI topics
- Navigation links styled as
/pathrather than plain words, which I thought was a nice touch
The overall vibe I was going for was “10x developer who’s been around long enough not to care about trends” — which, after 20+ years in .NET, felt reasonably accurate.
Migrating to Tailwind CSS v4
This was the most technically interesting part of the rebuild. Tailwind v4 is a significant departure from v3. The biggest change is that configuration moves out of tailwind.config.js and into CSS itself, using a new @theme block.
The old approach:
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
terminal: '#00e87a',
}
}
}
}
The new approach:
/* base.css */
@import "tailwindcss";
@theme {
--color-terminal: #00e87a;
--color-dotnet: #8b7cf8;
--color-zig: #f7a41d;
--color-void: #080808;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
}
That --color-terminal definition automatically generates bg-terminal, text-terminal, border-terminal and so on — which is quite elegant once you get used to it. The old @tailwind base, @tailwind components, @tailwind utilities directives are gone, replaced by a single @import "tailwindcss".
The integration with Astro also changes. You drop @astrojs/tailwind and switch to @tailwindcss/vite instead:
// astro.config.mjs
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
});
One thing to watch out for: after swapping the packages, delete node_modules and package-lock.json and do a clean npm install. Rollup has a known bug with optional dependencies that will stop the dev server from starting if you don’t. I hit this immediately.
AI-assisted development
I built this version with a lot of help from Claude Code — Anthropic’s CLI coding assistant. It handled the bulk of the component rewrites while I directed the design decisions, reviewed the output, and made adjustments. It’s a genuinely different workflow from writing everything yourself.
What worked well: generating boilerplate-heavy components, the Tailwind 4 migration, and keeping the code consistent across all files at once. What still needed a human: the tone of the copy, the design taste calls, and anything where context from previous conversations mattered.
The article you’re reading right now was not written by AI, for what it’s worth. Some things should stay manual.
Deploying to Render.com — a Tailwind 4 gotcha
This one caught me out. Render.com kept failing the build with this error:
Error: Cannot find native binding.
npm has a bug related to optional dependencies.
The root cause: Tailwind CSS v4 uses a native Rust engine (rolldown) under the hood. It needs a platform-specific binary — different ones for Linux x64, Linux ARM64, macOS, and so on. npm has a known bug where it sometimes fails to install these optional platform binaries, and Render’s build environment kept hitting it regardless of how many times I cleared the cache or reinstalled.
I tried several approaches — deleting package-lock.json, adding .npmrc flags, explicitly listing the binary as an optional dependency — and none of them reliably fixed it. The problem is that the native binding for Render’s Linux x64 servers simply wasn’t being picked up by npm no matter what.
The fix I landed on was much simpler: pre-build the CSS locally and commit it.
The idea is that Tailwind only needs to run on your local machine (where the native bindings install fine). You run a small shell script, it generates a minified built.css file, you commit it, and Render never has to touch Tailwind at all. The build becomes a plain astro build with no native dependencies.
# prepare.sh — run this locally whenever you change styles
./node_modules/.bin/tailwindcss -i src/styles/base.css -o src/styles/built.css --minify
Layout.astro imports built.css directly, and astro.config.mjs no longer references @tailwindcss/vite. Render’s build is now pure static Astro — exactly what it used to be.
It’s a slight change to the workflow (remember to run ./prepare.sh before committing style changes), but it’s reliable, fast, and doesn’t depend on npm correctly handling native binaries in a CI environment.
Conclusions
The redesign took a few sessions working alongside Claude Code and a fair bit of back-and-forth on spacing, typography, and copy. The end result is something that feels much more personal and a lot more fun to look at than the original.
Tailwind v4 is genuinely better once you’ve got past the migration. The CSS-first config is cleaner, the tooling is faster, and the custom theme properties are more intuitive than the old config file once you get your head around the naming conventions.
If you built your site with v1 of Astro and Tailwind 3, it’s worth spending a weekend on the upgrade. You’ll end up with something that looks different from every other dev blog out there — which, honestly, is the whole point.