Tailwind CSS v4.0 Upgrade and Astro 5.2 Project Migration Notes

发表于 2025-02-03 19:51 1500 字 8 min read

cos avatar

cos

FE / ACG / 手工 / 深色模式强迫症 / INFP / 兴趣广泛养两只猫的老宅女 / remote

Tailwind CSS 4.0 推出高性能构建引擎,构建速度提升5倍以上,支持现代CSS特性如容器查询、3D变换、渐变和CSS变量,提供更直观的CSS配置体验与丰富的新实用程序和变体。同时发布全面的迁移指南和自动升级工具,帮助项目平滑过渡,但部分旧插件(如tailwind-clip-path)与Astro集成存在兼容问题,需升级至Astro 5.2+并手动配置Vite插件以确保兼容性。

This article has been machine-translated from Chinese. The translation may contain inaccuracies or awkward phrasing. If in doubt, please refer to the original Chinese version.

Personal notes for my own reference.

Tailwind CSS v4.0 - Tailwind CSS

  • New high-performance engine - Full builds are 5x faster, and incremental builds are 100x+ faster — measured in microseconds.
  • Designed for the modern web - Built on cutting-edge CSS features, using cascade layers, @property, and color-mix() and other modern CSS features to register custom properties.
  • Simplified installation - Fewer dependencies, zero configuration, just one line in your CSS file.
  • First-party Vite plugin - Tight integration for maximum performance and minimal configuration.
  • Automatic content detection - All template files are automatically discovered, no configuration needed.
  • Built-in import support - Bundle multiple CSS files without additional tooling.
  • CSS-first configuration - A redesigned developer experience where you customize and extend the framework directly in CSS instead of JavaScript configuration files.
  • CSS theme variables - All your design tokens are exposed as native CSS variables, so you can access them anywhere.
  • Dynamic utility values and variants - No more guessing which values exist in spacing, and no more extending configuration for basic data attributes.
  • Modernized P3 color palette - A redesigned, more vivid color palette that takes full advantage of modern display technology.
  • Container queries - Use container queries to style elements based on container size, no plugin needed anymore.
  • New 3D transform utilities - Transform elements in 3D space directly in your HTML, with added APIs for 3D transforms like rotate-x-*, rotate-y-*, scale-z-*, translate-z-* and more.
  • Expanded gradient API - Radial and conic gradients, interpolation modes, and more. Linear gradients now support angles as values, so you can use utilities like bg-linear-45 to create a gradient at a 45-degree angle. Renamed bg-gradient-* to bg-linear-*.
  • @starting-style support - The new starting variant adds support for the new CSS @starting-style feature, enabling property transitions when an element is first displayed.
  • not-* variant - Style elements only when they don’t match another variant, custom selector, or media/feature query.
  • Even more new utilities and variants, including support for color-scheme, field-sizing, complex shadows, inert, etc.

More New Variants

  • New inset-shadow-* and inset-ring-* utilities - Allowing up to four stacked box shadow layers on a single element.
  • New field-sizing utilities - For automated text columns without writing a single line of JavaScript.
    • Use field-sizing-content to allow form controls to resize based on their content
    • Use field-sizing-fixed for fixed sizing
  • New color-scheme - Reference CSS color scheme related colors using light-dark()
  • New font-stretch utilities - For fine-tuning variable fonts that support different widths.
  • New inert variant - The inert variant lets you style elements marked with the inert attribute, which is useful for adding visual cues that clearly indicate parts of the content are not interactive.
  • New nth-* variants - Use nth-* and nth-last-* variants to style children based on their position in a list. You can pass any number to these by default, and use arbitrary values for more complex expressions like nth-[2n+1_of_li]. e.g., nth-3:underline nth-last-5:underline nth-last-of-type-6:underline
  • New in-* variant - Very similar to group-*, but without needing the group class.
    • The in-* variant responds to state changes of any ancestor, so if you need more fine-grained control, use group instead.
  • Support for :popover-open - The existing open variant also targets open popovers.
  • New descendant variant - For styling all descendant elements, for better or worse.
    • Like *, the ** variant can also be used to style an element’s children. The main difference is that ** applies styles to all descendants, not just direct children. This is especially useful when combined with another variant to narrow down the selection.

Migration Notes

For existing projects, we’ve published a comprehensive upgrade guide and built an automated upgrade tool to get on the latest version as quickly and painlessly as possible.

Let me try it on a small project first by running the automated migration command.

npx @tailwindcss/upgrade@next

Migration output:

 tailwindcss v4.0.2
 Searching for CSS files in the current directory and its subdirectories…
 Linked `./tailwind.config.js` to `./src/styles/theme/shadcn.css`
 Migrating JavaScript configuration files…
 The configuration file at `./tailwind.config.js` could not be automatically migrated to the new CSS configuration format, so your CSS has been
   updated to load your existing configuration file.
 Migrating templates…
 Migrated templates for configuration file: `./tailwind.config.js`
 Migrating stylesheets…
 Migrated stylesheet: `./src/styles/theme/shadcn.css`
 Migrating PostCSS configuration…
 Installed package: `@tailwindcss/postcss`
 Removed package: `autoprefixer`
 Removed package: `postcss-import`
 Migrated PostCSS configuration: `./postcss.config.js`
 Updating dependencies…
 Updated package: `prettier-plugin-tailwindcss`
 Updated package: `tailwindcss`
 Verify the changes and commit them to your repository.
npm verb exit 0
npm info ok

As you can see, it automatically completed most of the work, including class name updates like flex-grow -> grow / flex-shrink -> shrink. That’s a successful migration. Hmm, it seems like nothing went wrong on the surface. Let me try upgrading another Astro 5.1 project — let’s just go ahead and run the update script.

% sudo npx @tailwindcss/upgrade@next
npm verb cli /usr/local/bin/node /usr/local/lib/node_modules/npm/bin/npm-cli.js
npm info using npm@10.5.0
npm info using node@v20.12.2
npm verb title npm exec @tailwindcss/upgrade@next
npm verb argv "exec" "--" "@tailwindcss/upgrade@next"
npm verb logfile logs-max:10 dir:/Users/cosine/.npm/_logs/2025-02-03T10_01_41_235Z-
npm verb logfile /Users/cosine/.npm/_logs/2025-02-03T10_01_41_235Z-debug-0.log
npm sill logfile start cleaning logs, removing 1 files
npm sill logfile done cleaning log files
npm http fetch GET 200 https://registry.npmjs.org/@tailwindcss%2fupgrade 489ms (cache updated)
 tailwindcss v4.0.2

 Searching for CSS files in the current directory and its subdirectories…

 Linked `./tailwind.config.mjs` to `./src/styles/global/tailwind.css`

 Migrating JavaScript configuration files…

 Could not load the configuration file: undefined is not a function

npm verb exit 1
npm verb code 1

Great, we got the error Could not load the configuration file: undefined is not a function. After looking into it, the issue is an incompatible plugin. (Could not load the configuration file: v is not a function - tailwindlabs/tailwindcss - Discussion #15781 - GitHub)

Looking at the previously successful migration project, it had these two plugins: tailwindcss-animate and @tailwindcss/typography.

The problematic Astro project had an additional tailwind-clip-path plugin. After commenting it out, the migration ran normally.

// tailwind.config.js
module.exports = {
  plugins: [
    require('@tailwindcss/container-queries'),
    require('tailwindcss-animate'),
    // require("tailwind-clip-path"),
    require('@tailwindcss/typography'),
  ],
};

Then let’s start the dev server:

> cos-space@0.0.1 dev /Users/cosine/Documents/Programming/cos-space
> astro dev

Package subpath './nesting/index.js' is not defined by "exports" in /Users/cosine/Documents/Programming/cos-space/node_modules/.pnpm/@astrojs+tailwind@5.1.4_astro@5.1.1_@types+node@22.10.5_jiti@2.4.2_rollup@4.29.1_terser@5.37._5qh5alcn5ztelwntajptz64i4q/node_modules/tailwindcss/package.json imported from /Users/cosine/Documents/Programming/cos-space/node_modules/.pnpm/@astrojs+tailwind@5.1.4_astro@5.1.1_@types+node@22.10.5_jiti@2.4.2_rollup@4.29.1_terser@5.37._5qh5alcn5ztelwntajptz64i4q/node_modules/@astrojs/tailwind/dist/index.js
  Stack trace:
    at exportsNotFound (node:internal/modules/esm/resolve:304:10)
    at packageResolve (node:internal/modules/esm/resolve:837:14)
    at defaultResolve (node:internal/modules/esm/resolve:1157:11)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:359:25)
    at ModuleLoader.import (node:internal/modules/esm/loader:322:34)
 ELIFECYCLE  Command failed with exit code 1.

Great, I knew migrating within Astro wouldn’t be that simple. After checking astro/packages/integrations/tailwind/CHANGELOG.md at main - withastro/astro - GitHub, Tailwind CSS now provides a Vite plugin, which is the preferred method for using Tailwind 4 in Astro. Uninstall the original @astrojs/tailwind, then follow the Tailwind documentation for manual installation.

We also need to upgrade the Astro version to Astro 5.2 | Astro.

npx @astrojs/upgrade
pnpm rm @astrojs/tailwind
pnpm i tailwindcss @tailwindcss/vite

Success! Let’s compare the configuration files:

diff --git a/astro.config.mjs b/astro.config.mjs
index bf8e9b1..20513bd 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,22 +1,17 @@
 // @ts-check
 import react from '@astrojs/react';
-import tailwind from '@astrojs/tailwind';
 import { siteConfig } from './src/constants/site-config';
 import icon from 'astro-icon';
 import { defineConfig } from 'astro/config';
 import svgr from 'vite-plugin-svgr';
 import umami from '@yeskunall/astro-umami';
+import tailwindcss from '@tailwindcss/vite';

 // https://astro.build/config
 export default defineConfig({
   site: siteConfig.site,
   integrations: [
     react(),
-    tailwind({
-      // Allow writing nested CSS declarations on top of Tailwind's syntax
-      nesting: true,
-      applyBaseStyles: false,
-    }),
     icon({
       include: {
         gg: ['*'],
@@ -35,7 +30,7 @@ export default defineConfig({
     enabled: true,
   },
   vite: {
-    plugins: [svgr()],
+    plugins: [svgr(), tailwindcss()],
   },
   trailingSlash: 'never',
 });
diff --git a/tailwind.config.mjs b/tailwind.config.mjs
index a7de2d0..28e5899 100644
--- a/tailwind.config.mjs
+++ b/tailwind.config.mjs
@@ -140,10 +140,5 @@ export default {
       },
     },
   },
-  plugins: [
-    require('@tailwindcss/container-queries'),
-    require('tailwindcss-animate'),
-    require('tailwind-clip-path'),
-    require('@tailwindcss/typography'),
-  ],
+  plugins: [require('@tailwindcss/container-queries'), require('tailwindcss-animate'), require('@tailwindcss/typography')],
 };

The original plugins stopped working, and @plugin imports didn’t work either, but they can be written directly in CSS so it’s not a big deal. There were also layer hierarchy issues causing some style inheritance problems. Overall, migrating these two small projects was fairly smooth. I’ll keep updating this article as I migrate more projects.

Summary of Upgrade Problem Solutions

Problem 1: Astro PostCSS Configuration Conflict

Error: Package subpath './nesting/index.js' is not defined by "exports" in...

Resolution strategy:

  1. Upgrade Astro to 5.2+ with npx @astrojs/upgrade
  2. Uninstall @astrojs/tailwind
  3. Clean up old PostCSS plugin dependencies
  4. Follow the Tailwind documentation for manual installation, install tailwindcss @tailwindcss/vite / astro add tailwind

喜欢的话,留下你的评论吧~

© 2020 - 2026 cos @cosine
Powered by theme astro-koharu · Inspired by Shoka