
3 min read
Starting Clean With Astro 7 and Tailwind 4
On this page
This is part 3 of my rebuild series. In part 2 I backed up the old site to a git branch.
With the backup in place, it was demolition day. Everything in src/ got deleted except two folders: src/content/ (my actual posts) and src/assets/ (my images). Content is cargo; code is the ship. We were only replacing the ship.
The entire dependency list#
One thing I love about this rebuild: I can explain every single package. Here’s the new package.json, dependencies only:
{
"dependencies": {
"@astrojs/mdx": "^7.0.2",
"@astrojs/rss": "^4.0.19",
"@astrojs/sitemap": "^3.7.3",
"@fontsource-variable/inter": "^5.2.8",
"@jdevalk/astro-seo-graph": "^2.2.0",
"@jdevalk/seo-graph-core": "^0.7.0",
"astro": "^7.0.7"
},
"devDependencies": {
"@astrojs/check": "^0.9.6",
"@tailwindcss/typography": "^0.5.20",
"@tailwindcss/vite": "^4.3.2",
"tailwindcss": "^4.3.2",
"typescript": "^5.8.0"
}
}
Line by line: mdx lets my posts mix components into markdown. rss and sitemap generate the feed and sitemap. fontsource self-hosts my font (no more Google Fonts slowing down first paint). The two @jdevalk packages are the SEO suite from Yoast’s founder (that’s part 7). astro is the framework. Dev side: type checking, Tailwind, and its typography plugin for styling post content.
No React. No UI framework at all. A blog is mostly HTML. Astro renders it at build time, and the few interactive bits (theme toggle, mobile menu) are small vanilla JavaScript.
Why Astro 7 was the right risk#
Astro 7 was about three weeks old when we started, and my first instinct was to play it safe with 6. Here’s what changed my mind.
Astro 7’s headline change is a new Rust compiler that’s stricter: unclosed HTML tags are now build errors instead of silently accepted. That’s a real migration hazard for existing codebases. Old code full of sloppy markup breaks. But we were writing every file from scratch, so we’d just… write valid HTML from day one. The strictness only ever helped us.
The lesson generalizes: a “risky” upgrade is often only risky for code you already have. A clean slate is the cheapest possible time to be on the newest major version, because it means no forced migration six months later.
One real requirement to know about: Astro 7 needs Node 22.12 or newer. This matters again at deploy time (part 8).
The config, in TypeScript#
Astro configs can be TypeScript files, which means typo-checking and autocomplete on your own config:
// astro.config.ts
import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://jpshlk.com',
prefetch: true,
integrations: [mdx(), sitemap()],
vite: {
plugins: [tailwindcss()],
},
});
Two things worth noticing. The site value is my canonical domain, and setting it forced me to fix a typo (jshlk.com) that had been quietly poisoning my old sitemap and canonical URLs. And Tailwind is now a Vite plugin, not an Astro integration, which brings me to the fun part.
Tailwind 4: the config file is gone#
If you learned Tailwind before v4, you knew tailwind.config.js, a JavaScript file describing your colors, fonts, and plugins. Tailwind 4 deleted the whole concept. Configuration now lives in your CSS:
/* src/styles/global.css */
@import 'tailwindcss';
@plugin '@tailwindcss/typography';
@theme inline {
--font-sans: 'Inter Variable', ui-sans-serif, system-ui, sans-serif;
--color-primary-500: var(--color-sky-500);
/* ...the full 50–950 primary scale... */
}
The @theme block replaces the old config: define a CSS variable, get utility classes for free. Declare --color-primary-500 and text-primary-500 just exists. It sounds like a small change, but it makes the design system feel like CSS instead of a JavaScript puzzle, which is exactly where part 4 picks up.
At this point the site built successfully and rendered one nearly-empty page. Felt amazing anyway. Every rebuild needs its “hello world” moment.
Next up: a tiny design system, and a dark mode toggle that finally works.
Thanks for reading and have a good one! 🤙