Why I Build on Astro

Lire en français

After my first clean build of this site, I ran du -sh dist/. Under 150 KB. The whole thing (every page, every stylesheet, every asset) would fit on a 1996 floppy disk with room to spare.

That number, more than any benchmark, is why I stayed.

I had been web-shopping for months. Eleventy, Hugo, plain HTML, even a brief flirt with a hand-rolled Python build. Every option had its grain. Astro is what I ended up shipping, and a few weeks in, I want to write down what worked and what bit me, in the spirit of a field notebook, not a review.

Static, again

Astro is, at heart, a return to the static site. HTML is the artifact. Markdown is the source. The browser receives the page already built, and goes to sleep.

This is not a small thing. For fifteen years the industry told us static was insufficient: that we needed runtimes, hydration, a compute layer between the writer and the reader. The result is that a personal blog now ships a megabyte of JavaScript to render six paragraphs of prose.

Astro’s pitch is the inverse: ship nothing by default. If you want interactivity, opt in, island by island. I have not opted in once. There is no JavaScript on this site that the browser is forced to run. The dark/light theme switches via prefers-color-scheme in pure CSS. The RSS feed is a static XML file produced at build. Dates render at build, not at runtime.

The framework’s most celebrated feature (partial hydration, the islands) is the one I did not need. The discipline of nothing by default is what I did need.

The prose-invert trap

The first real friction was cosmetic, and it cost me an evening.

I am using @tailwindcss/typography to style the prose. The plugin ships with sensible defaults: colors, spacing, a prose-invert mode for dark backgrounds. I had wired up my own CSS variables for theming (one set for dark, one for light, swapped via @media (prefers-color-scheme: light)), and watched the dark-mode prose render in dark-on-dark. Unreadable.

The plugin had hard-coded its own palette under prose-invert, and it was winning the cascade against my variables. The fix was to bind the plugin’s CSS custom properties to my theme variables directly:

.prose {
  --tw-prose-body: var(--text-color);
  --tw-prose-headings: var(--text-color);
  /* ...etc */
}

A handful of lines, declared outside @layer base so they win against the plugin’s own defaults. Obvious in retrospect. Not obvious at 11pm.

Lesson: when a framework offers a “dark mode” and you already have a theming system, expect them to fight. The cleaner contract is to let one own the palette and route everything through it.

Where the framework earns its keep

Three things in Astro that I would miss if I left.

Content collections. Markdown files in a folder, a Zod schema for the frontmatter, and the build refuses to ship a post with a malformed date or a missing title. Editorial guardrails, for free.

The getCollection API. Listing posts, sorting them, filtering anything: three lines of TypeScript. No CMS, no database, no admin panel. The filesystem is the database.

The RSS plugin. @astrojs/rss reads the collection and emits a valid feed. No manual XML, no template, no third-party service. The Small Web’s primary distribution channel, generated as a side effect of the build.

These are not features that demo well. They are the kind of feature you stop noticing, which is the highest compliment a framework gets.

Tailwind v4, with caveats

Tailwind v4 was the other bet. The Vite plugin replaces the old PostCSS toolchain; configuration moves into CSS itself via @theme and @plugin. It is faster, cleaner, and the diff against v3 muscle memory was real.

The caveat: the documentation is mid-migration. Half the tutorials online still assume v3. When something does not work, you are sometimes debugging the version, not the code. I lost an hour to this once. I would lose it again.

If you are starting a project today, v4 is the right call. Just budget for a few “is this me or is this a v3-era answer?” detours.

Why it fits the Small Web

A philosophy is worth what its tools embody.

Astro’s defaults match the Small Web defaults: no JavaScript shipped unless you ask for it; no tracking primitives; no cloud lock-in; the build artifact is a folder of files you could host on a USB stick. The framework does not ask you to surrender sovereignty in exchange for ergonomics.

This is rarer than it sounds. Most modern frameworks assume you want to deploy on a specific cloud, route through a specific edge network, and hand your content over to a specific runtime. Astro lets you produce a dist/ folder and walk away.

I produce. I walk away. The folder gets uploaded, GitHub Pages serves it, and I close the laptop.

That is the whole job.


Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. — Antoine de Saint-Exupéry, Terre des hommes, 1939


Update, May 20, 2026. Since this was written: a French version of the site, a second RSS feed, and dist/ now weighs around 400 KB. The 1996 floppy still has room.