ADR-025: Tailwind CSS v4 Migration
Status
Section titled “Status”Accepted (migration completed 2026-03-26) — supersedes ADR-002: Future CSS Tooling Considerations
Context
Section titled “Context”This ADR was originally written on 2026-02-18 as a decision to stay on Tailwind CSS v3 and defer the v4 upgrade. On 2026-03-26, the migration was performed. This document has been updated to reflect the completed state.
The original concerns from the deferred decision were:
@astrojs/tailwindis deprecated for v4 — resolved by switching to@tailwindcss/vite- Design token pipeline would break — resolved by using
@theme inlinein CSS, which references the existing--color-*CSS custom properties fromtokens/dist/tokens.css tailwind.config.tshas no equivalent — resolved; the file was deleted and all theme configuration lives insrc/styles/global.css@tailwindcss/typographycompatibility — resolved; v0.5.19 declares>=4.0.0-beta.1peer dependency support
Decision
Section titled “Decision”Migrate to Tailwind CSS v4. All four preconditions from the original deferred decision were satisfied during the migration sprint.
What Changed
Section titled “What Changed”(Dependency versions below are as of the migration; current pins live in package.json and versions.json.)
Integration
Section titled “Integration”| Before | After |
|---|---|
@astrojs/tailwind v6.x integration | @tailwindcss/vite v4.2.2 Vite plugin |
tailwind.config.ts JS config file | Deleted — replaced by CSS |
@tailwind base/components/utilities | @import 'tailwindcss' |
Configuration model
Section titled “Configuration model”Before: tailwind.config.ts loaded design tokens via a transformTokens() function that read tokens/dist/tailwind-tokens.json at build time.
After: src/styles/global.css uses @theme inline to map existing CSS custom properties (defined in tokens/dist/tokens.css) to Tailwind utility classes — no JSON import, no JS function:
@theme inline { --color-primary-500: hsl(var(--color-primary-500)); /* ... all tokens ... */}Using @theme inline (not @theme) is deliberate: it avoids creating new CSS custom properties that would conflict with the --color-* vars already defined in tokens/dist/tokens.css.
Dark mode
Section titled “Dark mode”darkMode: "class" in the old JS config is replaced by a CSS variant declaration:
@variant dark (&:where(.dark, .dark *));Typography plugin
Section titled “Typography plugin”The JS config’s typography() function with CSS variable overrides is replaced by plain CSS in global.css:
@plugin "@tailwindcss/typography";
.prose { --tw-prose-body: hsl(var(--color-foreground-secondary)); --tw-prose-headings: hsl(var(--color-foreground-primary)); /* ... */}Custom utilities
Section titled “Custom utilities”The inline addUtilities plugin (focus-ring, focus-visible-ring, sr-only) is replaced by @utility blocks in global.css, using native CSS outline for focus rings (more accessible, works in Windows High Contrast mode).
Component @apply fix
Section titled “Component @apply fix”Three component style blocks that used @apply with custom utility classes now include @reference to allow Tailwind to resolve utility names:
src/layouts/ProjectLayout.astrosrc/layouts/BlogLayout.astrosrc/pages/projects/index.astro
Class renames (automated by @tailwindcss/upgrade)
Section titled “Class renames (automated by @tailwindcss/upgrade)”| Old | New |
|---|---|
bg-gradient-to-* | bg-linear-to-* |
flex-shrink-0 | shrink-0 |
flex-grow | grow |
outline-none (focus) | outline-hidden |
supports-[backdrop-filter]: | supports-backdrop-filter: |
Consequences
Section titled “Consequences”Positive
Section titled “Positive”@astrojs/tailwinddeprecation resolved permanently- Build times significantly faster (Tailwind v4 uses a Rust-based engine: ~100x faster incremental builds)
- CSS configuration is now co-located in
src/styles/global.css— single source of truth for styling - Design token pipeline (
tokens/dist/tokens.css) unchanged — no migration of the build-tokens script required - Zero TypeScript errors after migration; 0 warnings from
astro check
Neutral
Section titled “Neutral”tailwindcss-themer(optional dependency) has a peer dependency warning against tailwindcss ^3. If theme switching features are used, this package needs to be replaced with v4-native theming (which has built-in multi-theme support via@variant). (Resolved 2026-07-27: the package was unused — zero references in src/, tokens/, or scripts/ — and was removed rather than replaced. v4-native@varianttheming remains the documented path if multi-theme support is ever needed.)
References
Section titled “References”- Tailwind CSS v4 Upgrade Guide
- Tailwind CSS v4 Release Blog
- Astro 5.2 Release Notes — Tailwind v4 support
- ADR-002: Future CSS Tooling Considerations — superseded
- ADR-000: Starter Template Architecture
Enforcement
Section titled “Enforcement”- Testable consequences:
- TC-1: no
tailwind.config.{js,ts,mjs,cjs}file exists. - TC-2:
@astrojs/tailwindis absent from dependencies and@tailwindcss/viteis present. - TC-3:
src/styles/global.csscontains an@theme inlineblock.
- TC-1: no
- Checks:
- TC-1, TC-2, TC-3 → check
tw4-shape(status: warn)
- TC-1, TC-2, TC-3 → check
- Not machine-checkable: utility-vs-token style judgment inside components.
- Graduation log: (empty at creation; entries added when a check changes status)
Date: 2026-03-26
Participants: Template maintainers
Outcome: Completed