Blueprint
TL;DR: Go to my Astro 7 Starter Theme blueprint on GitHub. Then follow the steps to recreate my Northbeam corporate theme. The starter theme is built with Astro 7.0.2 + Tailwind CSS 4.3, and it utilizes custom oklch design tokens, a complete SEO stack (URL Canonicalization + Open Graph + Twitter Card + JSON-LD), RSS, an auto-sitemap, and a custom 404 page. All without a "why is Tailwind not working" detour along the way.
In my humble opinion, Astro is the best and fastest growing JavaScript web framework available today. I recently started writing Astro blueprints because there is a nice amount of confusion and miscommunication between versions 5-7. Most Astro blueprints, tutorials, and GitHub projects you’ll find searching around are based off older versions of Astro that no longer fully work in versions 6 or 7. If you want to save yourself hours or days of frustration you’ve found the right resource. This is the second Astro blueprint in a series I created — the Astro 6 starter theme covered the fundamentals; this one adds the full JSON-LD package to the SEO stack, a custom 404 page, and a modern corporate design that doesn’t look like stock Tailwind.
No mystery dependencies
Every layer here earns its place. Astro 7 ships with zero JavaScript by default, so you’re only rendering HTML/CSS and adding interactivity only where it’s needed. The styling is done with Tailwind CSS 4 via the Vite plugin (this matters — more on that in a second). Content runs through native Astro and MDX files. All the SEO is handled with a single <Seo /> component that emits Canonicalization, Open Graph, Twitter, and JSON-LD. The XML Sitemap and RSS generate themselves. Typography comes from the Tailwind plugin. Astro 7 requires Node 22.12+, because Node 18 and 20 were dropped with the release of Astro 6.
Read this before you install anything
Here’s the single thing that sends people to Google: Tailwind 4 is not an Astro integration anymore. If a tutorial tells you to run npm install @astrojs/tailwind, it’s outdated. That package targets Tailwind 3, and on a v4 project it will either toss errors or — worse — silently misbehave, leading to a fun debug until you realizing the install itself was wrong.
On Astro 7, npx astro add tailwind installs tailwindcss plus @tailwindcss/vite and registers the plugin under vite.plugins — never under integrations. There’s also no tailwind.config.js; Tailwind 4 is CSS-first, so your entire design lives in an @theme { … } block inside global.css. Internalize this diagram and hopefully dodge the most common Astro 7 failure outright.
oklch palette, one @theme block
A Tailwind site that leans on the out of the box gray-* and blue-* looks exactly like every other Tailwind site, and your eye clocks it instantly. My Astro 7 blueprint defines a custom palette in @theme using oklch — which keeps lightness perceptually even across hues, so your hover states and band don’t drift. You get an ink-* ramp for text, a teal brand-* accent, a warm signal-* for CTAs, plus named surface and line tokens.
The payoff is that Tailwind auto-generates real utilities from those names — text-ink-700, bg-brand-500, border-line — and the moment you use those instead of the defaults, the whole thing stops reading as “a Tailwind project” and starts reading as your brand.
One component, every page
This is the part that earns this the “production-ready” label. A single <Seo /> component resolves the canonical URL against your configured site, builds an absolute Open Graph image URL (social scrapers reject relative paths — a classic silent failure), emits Twitter card tags, and outputs a schema.org @graph. The graph defines your Organization once and references it by @id from a per-page node — WebPage for normal pages, BlogPosting for articles — so Google sees a clean, connected entity rather than three disconnected blobs.
The trap that gets everyone: JSON-LD must be emitted with
set:html={JSON.stringify(data)}. Drop a raw object between the script tags and Astro HTML-escapes your quotes into", producing invalid JSON that crawlers reject without a word of complaint.
@graph wins: one source of truth for your brand entity, reused by every page type.Hard-won, so you don't waste your time
The blueprint ships sixteen documented gotchas. These are the few I want to tattoo on my junior dev’s arm:
site in astro.config.mjs — non-negotiable.Canonical URLs, the sitemap, and RSS links all derive from it. Leave it out and canonicals resolve to undefined while the sitemap silently skips itself.
The old Go compiler quietly fixed bad markup; Rust does not. A <div> inside a <p> closes the paragraph early and wrecks your layout. Audit those MDX wrappers.
backdrop-blur header breaks a fixed mobile menu.Any ancestor with filter, backdrop-filter, or transform becomes the containing block for fixed children — that's the "transparent menu" bug. Render the overlay as a sibling of the header, not a child.
RSS adds a trailing slash by default regardless of config, so set trailingSlash: false in the rss() call or your feed links won't match your real URLs.
Managed hosts handle this; a naïve static server can serve a "soft 404" with status 200 that Google happily indexes. Verify with curl -I on your actual host.
Copy. Paste. Ship.
The complete blueprint — every config file, component, sample post, and all sixteen gotchas — is free on GitHub. No newsletter wall, no "premium tier."
If this has saved you some time, that was the entire point — go build something. If you know something I missed, post it to the repo. I also plan to publish more blueprints as new versions of Astro come out. You can find the Astro 7 starter theme here, and the earlier Astro 6 version here if you’re not ready for the jump yet.