正文

Tailwind CSS 4 带来的变化不只是“版本号变大”。很多团队升级时卡住,并不是因为工具本身难用,而是因为 v4 把配置中心从 tailwind.config.js 推向了 CSS 文件,把 PostCSS 插件拆到了独立包里,还改变了旧插件、旧指令、扫描源、主题变量和部分工具类的迁移方式。

官方升级指南也明确说明:v4 是一个新的大版本,升级时确实有必要处理一些破坏性变更;同时,v4 面向较新的浏览器环境,例如 Safari 16.4+、Chrome 111+ 和 Firefox 128+。

这篇文章不讲空话,直接按真实项目里最容易踩坑的顺序来拆:CSS-first 配置怎么写、PostCSS 怎么改、旧插件怎么迁、tailwind.config.js 还能不能用、@apply 为什么突然失效,以及升级后样式变了该怎么查。

为什么 Tailwind CSS 4 升级会卡住

很多项目从 Tailwind CSS 3 升级到 Tailwind CSS 4 时,第一反应是:“我原来的配置文件去哪了?”这很正常。v3 时代,我们习惯把主题、颜色、断点、插件、content 扫描路径都写在 tailwind.config.js 里;而 v4 的核心思路变成了 CSS-first configuration,也就是尽量把配置写回 CSS 本身。

在 v4 里,一个最小入口通常长这样:

这取代了 v3 里常见的三段式写法:

官方升级指南明确说明,v4 使用普通 CSS @import 导入 Tailwind,而不是继续依赖旧的 @tailwind 指令。

升级卡住的根本原因通常有这些:

所以,迁移时别一上来就到处改 class。先从构建链开始,因为构建链错了,后面的样式排查都会变成盲查。

  • 仍然把 tailwindcss 当 PostCSS 插件用。
  • 继续期待 Tailwind 自动读取 tailwind.config.js
  • 旧插件没有迁到 CSS 里的 @plugin
  • contentsafelisttheme.extend@apply 的写法还停留在 v3。
css
@import "tailwindcss";
css
@tailwind base;
@tailwind components;
@tailwind utilities;

先确认浏览器和 Node 环境

升级前要先问一个现实问题:你的项目是否必须支持旧浏览器?Tailwind CSS 4 依赖一些现代 CSS 能力,比如 @propertycolor-mix(),官方建议如果还需要支持更旧的浏览器,就暂时停留在 v3.4。

另外,官方升级工具需要 Node.js 20 或更高版本。它可以帮助你自动完成很多迁移动作,包括更新依赖、把配置迁到 CSS、处理模板文件中的变化等。

但不要把升级工具当成“魔法按钮”。在复杂项目里,尤其是有自定义插件、monorepo、组件库、CSS Modules、Vue/Svelte <style> 块的项目,自动迁移后仍然要人工检查 diff。

建议流程是:

然后再逐项修复错误。这样升级失败时也能安全回滚。

Bash
npx @tailwindcss/upgrade
Bash
git checkout -b upgrade/tailwind-v4
npx @tailwindcss/upgrade
npm run dev
npm run build

PostCSS 迁移:最常见的报错怎么修

Tailwind CSS 4 最常见的报错之一是:

原因很简单:在 v3 里,tailwindcss 包本身可以作为 PostCSS 插件;但在 v4 里,PostCSS 插件已经移到独立包 @tailwindcss/postcss。官方文档也明确要求安装 tailwindcss@tailwindcss/postcsspostcss,并在 PostCSS 配置中使用 @tailwindcss/postcss

错误写法通常是:

v4 应改成:

安装命令:

如果你的项目里还有 postcss-importautoprefixer,也要重新评估是否还需要。Tailwind CSS 4 已内置 import 处理和 vendor prefixing,官方升级指南指出,在 v4 中可以移除 postcss-importautoprefixer

很多项目升级后最干净的 PostCSS 配置就是:

这一步修好后,至少构建链能先跑起来。

txt
It looks like you're trying to use `tailwindcss` directly as a PostCSS plugin.
js
// postcss.config.js
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};
js
// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};
Bash
npm install tailwindcss @tailwindcss/postcss postcss
js
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

Vite 项目优先使用 @tailwindcss/vite

如果你用的是 Vite,v4 官方更推荐使用专门的 Vite 插件,而不是继续通过 PostCSS 插件绕一层。官方升级指南提到,Vite 项目建议迁移到新的专用 Vite 插件,以获得更好的性能和开发体验。

安装:

配置:

CSS 入口:

这时通常不再需要在 postcss.config 里配置 Tailwind。很多人升级时同时配置了 @tailwindcss/vite@tailwindcss/postcss,结果出现重复处理、报错或样式异常。一个项目里选一种路径即可:Vite 项目优先走 Vite 插件;非 Vite 或框架需要 PostCSS 时再走 PostCSS 插件。

Bash
npm install tailwindcss @tailwindcss/vite
TypeScript
// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    tailwindcss(),
  ],
});
css
@import "tailwindcss";

CSS-first 配置:tailwind.config.js 里的主题怎么迁

v4 的核心变化是:主题配置尽量放到 CSS 里。官方 v4 发布文章把这称为 CSS-first configuration,即从 JavaScript 配置转向 CSS 配置;你可以在导入 Tailwind 的 CSS 文件里直接配置设计 token、自定义工具和变体。

v3 里你可能这样写:

v4 可以迁到 CSS:

迁移后,你可以继续使用:

这里的重点是:@theme 不是普通的 :root。官方文档说明,@theme 里定义的是特殊 CSS 变量,它们会影响项目里会生成哪些工具类。例如定义 --color-mint-500 后,就可以使用 bg-mint-500text-mint-500fill-mint-500 等工具类。

简单说:

只是定义一个 CSS 变量。

而:

既定义 CSS 变量,也告诉 Tailwind 生成相关工具类。

这是很多升级问题的关键。

js
// tailwind.config.js
export default {
  theme: {
    extend: {
      colors: {
        brand: {
          500: "#2563eb",
          600: "#1d4ed8",
        },
      },
      fontFamily: {
        display: ["Inter", "sans-serif"],
      },
      screens: {
        "3xl": "1920px",
      },
    },
  },
};
css
@import "tailwindcss";

@theme {
  --color-brand-500: #2563eb;
  --color-brand-600: #1d4ed8;
  --font-display: Inter, sans-serif;
  --breakpoint-3xl: 120rem;
}
html
<h1 class="font-display text-brand-500 3xl:text-brand-600">
  Hello Tailwind CSS 4
</h1>
css
:root {
  --color-brand-500: #2563eb;
}
css
@theme {
  --color-brand-500: #2563eb;
}

旧的 tailwind.config.js 还能不能用

能用,但不再是首选。Tailwind CSS 4 仍然支持 JavaScript 配置文件以保持向后兼容,不过它不会像 v3 那样自动检测。你需要在 CSS 入口里显式加载:

这很适合渐进式迁移。例如你可以先保留旧配置,让项目跑起来:

然后逐步把 theme.extend.colorsfontFamilyscreensanimation 等迁到 @theme

不过要注意,v4 的 JS 配置并不是 100% 支持 v3 的所有选项。官方文档说明,JavaScript 配置里的 corePluginssafelistseparator 在 v4.0 中不受支持;如果要 safelist 工具类,应改用 @source inline()

所以推荐策略是:

这样既能降低一次性迁移风险,也能让团队慢慢适应 CSS-first 写法。

css
@import "tailwindcss";
@config "../../tailwind.config.js";
css
@import "tailwindcss";
@config "../tailwind.config.js";
css
@import "tailwindcss";
@config "../tailwind.config.js";

/* 新增或覆盖的配置放这里 */
@theme {
  --color-brand-500: #2563eb;
}

旧插件怎么迁:从 plugins 到 @plugin

旧项目里经常有这样的配置:

在 v4 的 CSS-first 模式下,可以改成:

官方函数与指令文档说明,@plugin 可以加载旧的 JavaScript 插件,既支持包名,也支持本地路径;@config@plugin 还能与 @theme@utility 等 CSS 驱动特性一起使用,帮助你逐步迁移。

本地插件也可以这样迁:

如果插件本身依赖旧的 Tailwind 内部行为,迁移后可能还会出问题。这时建议先判断插件类型:

  • 官方插件,如 typography:优先尝试 @plugin
  • 只添加工具类的插件:考虑改写为 @utility
  • 只添加主题 token 的插件:考虑迁到 @theme
  • 复杂插件,依赖 JS 逻辑:暂用 @plugin,再逐步重构。
  • 不再维护的插件:尽量替换为原生 CSS 或自定义工具。
js
// tailwind.config.js
import typography from "@tailwindcss/typography";
import forms from "@tailwindcss/forms";

export default {
  plugins: [
    typography,
    forms,
  ],
};
css
@import "tailwindcss";

@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
css
@import "tailwindcss";

@plugin "./plugins/my-plugin.js";

自定义工具类别再依赖旧的 @layer utilities 行为

v3 中,我们常用:

在 v4 中,Tailwind 使用原生 cascade layers,不再像过去那样接管 @layer。官方升级指南建议使用新的 @utility API 来注册自定义工具类。

迁移后:

组件类也可以这样写:

这样做的好处是 Tailwind 能真正把它当作工具类处理,变体也更清晰。例如:

如果你的旧项目里有很多 .btn.card.badge.container-page 之类的类,不要一股脑全部塞进 @layer components。建议按用途分开:

  • 纯工具类:迁到 @utility
  • 全局基础样式:保留在 @layer base
  • 复杂组件样式:可以保留普通 CSS,别强行工具化。
  • 设计 token:迁到 @theme
css
@layer utilities {
  .tab-4 {
    tab-size: 4;
  }
}
css
@utility tab-4 {
  tab-size: 4;
}
css
@utility btn {
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
  background-color: ButtonFace;
}
html
<button class="btn hover:bg-blue-600">
  保存
</button>

content 和 safelist 怎么迁:认识 @source

v4 的类名检测也变得更自动。Tailwind 会扫描项目中的源文件,生成实际使用到的 CSS。官方文档说明,Tailwind 会把源文件当作纯文本扫描,而不是把它们按 JS、Vue 或模板语法真正解析。

这意味着老问题仍然存在:不要动态拼 class。

错误示例:

正确示例:

如果你的 Tailwind class 存在于默认不会扫描的位置,比如 monorepo 里的共享组件库,可以用 @source

如果你想设置扫描基准路径:

如果你要 safelist 某些类,v4 推荐用 @source inline()

带变体:

批量生成:

官方文档也说明,@source inline() 可以强制生成内容文件中不存在的工具类,并支持变体与范围写法。这对 CMS、低代码页面、后端模板、Markdown 渲染、用户配置主题尤其有用。

jsx
<button className={`bg-${color}-600 hover:bg-${color}-500`}>
  Click
</button>
jsx
const colorMap = {
  blue: "bg-blue-600 hover:bg-blue-500",
  red: "bg-red-600 hover:bg-red-500",
};

<button className={colorMap[color]}>
  Click
</button>
css
@import "tailwindcss";
@source "../packages/ui";
css
@import "tailwindcss" source("../src");
css
@import "tailwindcss";
@source inline("underline");
css
@source inline("{hover:,focus:,}underline");
css
@source inline("{hover:,}bg-red-{50,{100..900..100},950}");

@apply 升级后失效:Vue、Svelte、CSS Modules 要加 @reference

很多人升级后发现:

在某些地方突然不工作,尤其是 Vue、Svelte、Astro 的 <style> 块,或者 CSS Modules 文件。

原因是:这些样式文件可能被单独打包,它们访问不到主 CSS 文件里定义的主题变量、自定义工具和自定义变体。官方升级指南建议在这些上下文中使用 @reference 引入主 CSS,但不会重复输出 CSS。

例如 Vue:

不过,能不用 @apply 时,建议直接用 class:

如果确实要在 CSS 里写,也可以直接使用主题变量:

官方也提到,直接使用 CSS 变量可以避免 Tailwind 额外处理这些样式,性能更好。

我的建议是:

  • 页面结构样式:优先写 class。
  • 可复用小工具:用 @utility
  • 必须组合多个工具类:谨慎用 @apply
  • Vue/Svelte/CSS Modules:需要 @reference
  • 设计 token:用 CSS 变量或 @theme
css
.title {
  @apply text-2xl font-bold text-red-500;
}
Vue
<template>
  <h1>Hello</h1>
</template>

<style>
@reference "../../app.css";

h1 {
  @apply text-2xl font-bold text-red-500;
}
</style>
Vue
<h1 class="text-2xl font-bold text-red-500">
  Hello
</h1>
css
h1 {
  color: var(--color-red-500);
}

升级后样式变了先查这些破坏性变化

Tailwind CSS 4 改了一些默认行为。它们不一定会报错,但会让页面看起来不一样。

border 默认颜色变了

v3 中,borderdivide 默认倾向于使用灰色;v4 中默认颜色改为 currentColor,更接近浏览器默认行为。官方升级指南建议,如果你依赖旧外观,就显式加颜色,比如 border-gray-200

html
<div class="border border-gray-200">
  内容
</div>

ring 默认从 3px 变成 1px

如果你原来写:

v4 后可能视觉变细。应改成:

官方文档也列出 ringring-3 的迁移建议。

html
<button class="focus:ring">
  保存
</button>
html
<button class="focus:ring-3 focus:ring-blue-500">
  保存
</button>

一些工具类改名

常见映射包括:

这些改名不是简单的别名变化,有些视觉结果会变,所以升级后一定要检查关键组件,比如按钮、输入框、卡片、弹窗和表单。

  • shadow-sm 迁到 shadow-xs
  • shadow 迁到 shadow-sm
  • rounded-sm 迁到 rounded-xs
  • rounded 迁到 rounded-sm
  • blur-sm 迁到 blur-xs
  • outline-none 迁到 outline-hidden
  • ring 迁到 ring-3

hover 在触摸设备上更严格

v4 的 hover 变体只在主要输入设备支持 hover 时生效。也就是说,如果你的移动端交互依赖点击触发 hover 样式,升级后可能会变。官方建议不要把 hover 当作触摸设备上的核心交互。

space 和 divide 选择器变了

space-*divide-* 的选择器也有变化,主要是为了改善大页面性能。升级后如果列表间距或分割线方向异常,优先检查这些工具类。官方建议在出现问题时考虑迁到 flex/grid 加 gap

前缀、important 和 CSS 变量写法也要改

如果你的项目用了 prefix,例如 v3 里常见的 tw-

v4 中 prefix 更像 variant,写在最前面:

CSS 入口:

官方说明,使用 prefix 时,主题变量仍然按未加前缀的方式配置,但生成的 CSS 变量会带前缀,以避免项目冲突。

important 写法也变了。v3 常见:

v4 推荐:

旧写法仍兼容,但已经不推荐继续使用。

CSS 变量任意值也有变化。v3 里可能写:

v4 推荐:

这些细节看似小,但在大型项目里会造成大量视觉差异。

html
<div class="tw-flex tw-bg-red-500"></div>
html
<div class="tw:flex tw:bg-red-500 tw:hover:bg-red-600"></div>
css
@import "tailwindcss" prefix(tw);

@theme {
  --color-brand-500: #2563eb;
}
html
<div class="!flex !bg-red-500"></div>
html
<div class="flex! bg-red-500!"></div>
html
<div class="bg-[--brand-color]"></div>
html
<div class="bg-(--brand-color)"></div>

一个实用迁移模板

假设你的 v3 项目是 React + Vite + Tailwind,旧配置大概这样:

迁移后可以变成:

如果你有共享组件库:

如果你暂时不想迁完旧配置:

这种方式最稳:先保证项目能跑,再逐步把旧 JS 配置迁到 CSS。

js
// tailwind.config.js
import typography from "@tailwindcss/typography";

export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {
      colors: {
        brand: {
          500: "#2563eb",
          600: "#1d4ed8",
        },
      },
      fontFamily: {
        display: ["Inter", "sans-serif"],
      },
    },
  },
  plugins: [typography],
};
TypeScript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
});
css
/* src/index.css */
@import "tailwindcss";

@plugin "@tailwindcss/typography";

@theme {
  --color-brand-500: #2563eb;
  --color-brand-600: #1d4ed8;
  --font-display: Inter, sans-serif;
}
css
@source "../packages/ui";
css
@import "tailwindcss";
@config "../tailwind.config.js";

@theme {
  --color-brand-500: #2563eb;
}

推荐的升级检查清单

升级 Tailwind CSS 4 时,不要靠感觉排查。按下面顺序做,会快很多:

  • Node 版本:升到 Node.js 20+,尤其要用官方升级工具时。
  • CSS 入口:把 @tailwind base/components/utilities 改成 @import "tailwindcss"
  • PostCSS:把 tailwindcss 插件改成 @tailwindcss/postcss
  • Vite:优先使用 @tailwindcss/vite
  • 主题配置:把 theme.extend 逐步迁到 @theme
  • 旧 JS 配置:需要时用 @config 显式加载。
  • 插件:用 @plugin 迁移旧插件。
  • safelist:改成 @source inline()
  • content 路径:改用自动扫描或 @source
  • 自定义工具类:从旧 @layer utilities 迁到 @utility
  • Vue/Svelte/CSS Modules:@apply 前加 @reference
  • 样式差异:检查 border、ring、shadow、rounded、hover、space、divide。

FAQ:Tailwind CSS 4 升级常见问题

Tailwind CSS 4 还需要 tailwind.config.js 吗?

不一定需要。v4 推荐 CSS-first 配置,大多数主题 token 可以直接写在 @theme 中。但如果你要渐进迁移旧项目,仍然可以用 @config 显式加载旧的 tailwind.config.js

为什么 PostCSS 报错说不能直接用 tailwindcss?

因为 v4 的 PostCSS 插件已经移到 @tailwindcss/postcss。把 PostCSS 配置里的 tailwindcss: {} 改成 "@tailwindcss/postcss": {},并安装对应依赖即可。

Vite 项目还需要 PostCSS 配置吗?

通常不需要。Vite 项目建议使用 @tailwindcss/vite,然后在 CSS 中写 @import "tailwindcss";。不要同时重复配置 Vite 插件和 PostCSS 插件。

旧插件还能继续用吗?

多数旧插件可以先用 @plugin 加载,例如 @plugin "@tailwindcss/typography";。如果插件只是添加简单工具类,长期看可以迁成 @utility;如果只是主题 token,可以迁到 @theme

safelist 在 v4 怎么写?

JavaScript 配置里的 safelist 在 v4.0 中不受支持。推荐改用 @source inline(),例如 @source inline("{hover:,}bg-red-{50,{100..900..100},950}");

为什么 Vue 或 CSS Modules 里的 @apply 失效?

因为这些样式文件可能单独打包,访问不到主 CSS 文件中的主题变量和自定义工具。用 @reference "../../app.css"; 引入主 CSS 的定义即可,或者直接使用 class 和 CSS 变量。

结论:Tailwind CSS 4 迁移的关键不是全改,而是分层改

Tailwind CSS 4 升级最怕一口气乱改:一边换 PostCSS,一边改主题,一边删配置,一边重写插件,最后页面炸了也不知道是哪一步导致的。

更稳的办法是分层迁移:先修构建链,把 PostCSS 或 Vite 配好;再改 CSS 入口,用 @import "tailwindcss" 替换旧指令;接着用 @config 保留旧配置,让项目先跑起来;然后把颜色、字体、断点、动画迁到 @theme;最后处理旧插件、@utility@source@apply 和视觉差异。

说白了,Tailwind CSS 4 不是让你把过去的经验全部推翻,而是把配置更靠近 CSS 本身。只要理解 CSS-first、@theme@plugin@source@reference 这几件事,升级就不会再像卡在黑盒里。真正的好处也会慢慢显现:配置更直观,设计 token 更容易共享,构建链更清爽,团队协作也更接近 CSS 的原生心智模型。

参考来源

Upgrade guideTailwind CSS DocsTailwind CSS v4.0Tailwind CSS BlogTheme variablesTailwind CSS DocsFunctions and directivesTailwind CSS DocsDetecting classes in source filesTailwind CSS Docs

相关文章

Vite 7 升级卡住:为什么要求 Node 20.19+ / 22.12+工程实践 / 约 15 分钟Next.js 16 迁移清单:middleware.ts 改 proxy.ts、缓存 API 怎么改工程实践 / 约 18 分钟Nuxt 4 升级前先查什么:目录结构、useFetch、TypeScript 和模块兼容性工程实践 / 约 16 分钟7 个关键洞察:AI Coding 工具真正改变的不是写代码,而是验证代码智能编程 / 约 18 分钟