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, andcolor-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-45to create a gradient at a 45-degree angle. Renamedbg-gradient-*tobg-linear-*. - @starting-style support - The new
startingvariant adds support for the new CSS@starting-stylefeature, 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-*andinset-ring-*utilities - Allowing up to four stacked box shadow layers on a single element. - New
field-sizingutilities - For automated text columns without writing a single line of JavaScript.- Use
field-sizing-contentto allow form controls to resize based on their content - Use
field-sizing-fixedfor fixed sizing
- Use
- New
color-scheme- Reference CSS color scheme related colors using light-dark() - New
font-stretchutilities - For fine-tuning variable fonts that support different widths. - New
inertvariant - Theinertvariant lets you style elements marked with theinertattribute, which is useful for adding visual cues that clearly indicate parts of the content are not interactive. - New
nth-*variants - Usenth-*andnth-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 likenth-[2n+1_of_li]. e.g.,nth-3:underlinenth-last-5:underlinenth-last-of-type-6:underline- For a complete list of available pseudo-class variants, see the pseudo-class reference.
- New
in-*variant - Very similar togroup-*, but without needing thegroupclass.- The
in-*variant responds to state changes of any ancestor, so if you need more fine-grained control, usegroupinstead.
- The
- Support for
:popover-open- The existingopenvariant 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.
- Like
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:
- Upgrade Astro to 5.2+ with
npx @astrojs/upgrade - Uninstall
@astrojs/tailwind - Clean up old PostCSS plugin dependencies
- Follow the Tailwind documentation for manual installation, install
tailwindcss @tailwindcss/vite/astro add tailwind
喜欢的话,留下你的评论吧~