Upgrade Astro 4 → 7 (with Tailwind 3 → 4 migration) - #11
Merged
Conversation
…ead dependency) Bump CI Node from 20 to 22 ahead of Astro 6/7's Node >=22.12 requirement, pin the same floor via package.json engines, and drop the unused remark-admonitions dependency (the actual admonitions logic is the custom remarkAdmonitions() function already inline in astro.config.mjs).
Bump astro to 5.18.2 and @astrojs/mdx to 4.3.14 (mandatory at v5). Rewrite src/content/config.ts -> src/content.config.ts using loader-based collections (glob()) instead of the removed type: 'content'|'data' shape, and update every call site from the legacy .slug/.render() entry API to .id/render(post). Verified rendered HTML, RSS, and sitemap output are byte-identical to the pre-upgrade Astro 4 build (aside from the Astro version string and per-build asset hashes).
Bump astro to 5.18.2 and integrations (@astrojs/mdx to 4.3.14, @astrojs/rss to 4.0.19, @astrojs/sitemap to 3.7.3, @astrojs/check to 0.9.10). Rewrite content.config.ts collections to use loader-based glob() instead of the removed type: 'content'|'data' shape, and update every call site from the legacy .slug/.render() entry API to .id/render(post). Verified rendered HTML, RSS, and sitemap output are byte-identical to the pre-upgrade Astro 4 build (aside from the Astro version string and per-build asset hashes).
…css/vite @astrojs/tailwind was never published for Astro 6/7 (peer range tops out at ^5.0.0), so it must go before the next hop. Ran the official @tailwindcss/upgrade codemod: converts tailwind.config.mjs into a CSS @theme block in global.css (preserving all custom color ramps, fonts, zeroed border-radius, and the blink keyframe), migrates @layer components classes to @Utility blocks, and renames affected utility classes across 16 templates (outline-none->outline-hidden, backdrop-blur-sm->-xs, etc). Swapped the @astrojs/tailwind integration for @tailwindcss/vite's Vite plugin in astro.config.mjs, and added @reference imports to the two scoped <style> blocks that use @apply (Tailwind 4 requires it per CSS module). Verified rendered HTML is unchanged aside from the codemod's value-equivalent utility renames (e.g. leading-[1.5] -> leading-normal).
Bump astro to 6.4.8 and @astrojs/mdx to 6.0.3. No code changes required: the legacy content collections API and @astrojs/tailwind (the two things v6 removes/breaks) were already migrated away from in prior phases. Verified behaviorally: the i18n.routing.redirectToDefaultLocale default flip (true->false) is a no-op here since /en/ is a manually-duplicated page tree rather than Astro-generated i18n routing (/ , /en, and /en/ all still resolve 200 with no unexpected redirects). Markdown heading-slug generation is unchanged for existing posts (no diff in rendered <h2-4 id> attributes). Rendered output otherwise matches the Tailwind-4 baseline aside from a stricter/more spec-correct & entity escaping in one template's href attribute.
…rojs/markdown-remark
Bump astro to 7.2.0 and @astrojs/mdx to 7.0.5. Astro 7 replaces the
default Markdown processor with a new Rust-based one ("Satteri") and no
longer installs @astrojs/markdown-remark by default. Installed it
explicitly (7.2.2) to keep the existing unified()/remark pipeline
(remarkDirective + the custom remarkAdmonitions() plugin, plus the Shiki
shikiConfig transformer) working byte-for-byte as before, rather than
porting to Satteri's less-documented native plugin API.
Verified: admonition markup, heading anchor ids, Shiki code-block-wrapper
metadata, RSS, and the sitemap are all unchanged from the Astro 6 build.
The only differences anywhere in rendered output are two documented,
cosmetic Astro 7 changes - the stricter default compressHTML behavior
(whitespace between tags/inline text is now stripped more aggressively)
and the new Vite 8/Rolldown minifier's inline-script output style (var
instead of const, backtick strings instead of double-quoted) - neither
changes visible content or functionality.
Update the Stack line, content collections section, and design-system section to reflect the completed migration: Astro 7 with the @astrojs/markdown-remark opt-in, Tailwind 4 via @tailwindcss/vite with CSS-based config (no more tailwind.config.mjs), content.config.ts's loader-based collections and id-not-slug entries, and @Utility blocks replacing @layer components. Also ran `npm audit fix` for a transitive mdast-util-to-hast advisory surfaced by the markdown-remark pipeline (patch-level bump, no API change) - 0 vulnerabilities remaining. Full regression pass: all 40 pages across both locales return 200 on a production preview build.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
blog | 4954f0d | Aug 11 2026, 06:41 AM |
The lockfile had been regenerated under Node 25/npm 11 locally, which resolved lightningcss's optional platform dependencies differently than npm 10 (bundled with the Node 22 CI uses) - npm ci failed on CI with "Missing: @emnapi/runtime, @emnapi/core from lock file". Reproduced locally under Node 22 via nvm and reinstalled to fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upgrades the site from Astro 4.16.19 to the latest Astro 7.2.0, crossing three major version
boundaries (4→5→6→7), each landed as its own verified, revertable commit:
enginesfield, drop an unuseddependency.
src/content/config.ts→src/content.config.tsonto the Content LayerAPI (
loader: glob(...)), updating every call site from the legacy.slug/.render()entry APIto
.id/render(post).@astrojs/tailwindwas never published for Astro 6/7 (its peer range topsout at
^5.0.0), so this had to land before the next hop. Ran the official@tailwindcss/upgradecodemod, swapped the integration for@tailwindcss/vite, and convertedtailwind.config.mjsinto a CSS@themeblock.i18n.routing.redirectToDefaultLocaledefault flip and the markdown heading-slug generation change are both no-ops for this site.
installed
@astrojs/markdown-remarkexplicitly to keep the existingremarkDirective+custom admonitions plugin + Shiki config working byte-for-byte.
npm audit fix(0vulnerabilities remaining, down from 26 at the start).
At every phase, rendered HTML/RSS/sitemap output was diffed against the prior phase's build. The
only differences anywhere in the final output vs. the pre-upgrade baseline are cosmetic and
expected: the Astro version string, per-build asset hashes, value-equivalent Tailwind utility
renames from the codemod (e.g.
leading-[1.5]→leading-normal), Astro 7's stricter defaultHTML whitespace compression, and the new Vite 8/Rolldown minifier's inline-script syntax style.
Admonitions, heading anchors, Shiki code-block metadata, RSS, and the sitemap are byte-identical
throughout.
Test plan
npm run build(astro check + astro build) passes cleanly at every phase/and/en/trees) return 200 on a production previewbuild
:::tip,:::warning,:::danger,:::note,:::info), heading anchor IDs,and the Shiki copy-button wrapper are unchanged from the pre-upgrade baseline
/rss.xml) and sitemap (/sitemap-index.xml) output are byte-identical to baselinenpm auditreports 0 vulnerabilities (down from 26)output; a human pass over the brutalist design — borders, color ramps, responsive collapse
at 900px, blink-cursor animation — is still worth doing before merge)
🤖 Generated with Claude Code