升级指南
将 Tailwind CSS 项目从 v3 升级到 v4。
Tailwind CSS v4.0 是框架的新主版本。我们已尽力减少破坏性变更,但仍有一些更新不可避免。本指南列出了将项目从 v3 升级到 v4 所需的全部步骤。
Tailwind CSS v4.0 面向 Safari 16.4+、Chrome 111+ 和 Firefox 128+。 如果需要支持更旧的浏览器,请继续使用 v3.4,直到浏览器支持要求发生变化。
使用升级工具
如果要把项目从 v3 升级到 v4,可以使用我们的升级工具完成绝大部分繁重工作:
$ npx @tailwindcss/upgrade对大多数项目而言,升级工具会自动化整个迁移过程,包括更新依赖、把配置文件迁移到 CSS,以及处理模板文件中的变更。
升级工具需要 Node.js 20 或更高版本,运行前请确保环境已更新。
建议在新分支上运行升级工具,然后仔细审查 diff,并在浏览器中测试项目,确认所有变更看起来都正确。复杂项目可能需要少量手工调整,但无论如何工具都能节省大量时间。
另外最好通读 v4 的全部破坏性变更,弄清改了什么,以防项目中还有升级工具未覆盖、需要你自行更新的地方。
手动升级
使用 PostCSS
在 v3 中,tailwindcss 包本身就是 PostCSS 插件;到了 v4,PostCSS 插件独立到专用的 @tailwindcss/postcss 包中。
此外,v4 会自动处理导入和厂商前缀,因此如果项目里有 postcss-import 和 autoprefixer,可以移除它们:
export default {
plugins: {
"postcss-import": {},
tailwindcss: {},
autoprefixer: {},
"@tailwindcss/postcss": {},
},
};使用 Vite
如果使用 Vite,建议从 PostCSS 插件迁移到新的专用 Vite 插件,以获得更好的性能和开发体验:
export default defineConfig({
plugins: [
tailwindcss(),
],
});使用 Tailwind CLI
在 v4 中,Tailwind CLI 位于专用的 @tailwindcss/cli 包。请把构建命令更新为使用这个新包:
npx tailwindcss -i input.css -o output.css
npx @tailwindcss/cli -i input.css -o output.css相对 v3 的变更
以下是 Tailwind CSS v4.0 中全部破坏性变更的完整列表。
我们的升级工具 会自动处理其中大多数变更,因此强烈建议尽可能使用它。
浏览器要求
Tailwind CSS v4.0 面向现代浏览器,目标是 Safari 16.4、Chrome 111 和 Firefox 128。核心框架功能依赖 @property、color-mix() 等现代 CSS 特性,因此 Tailwind CSS v4.0 无法在更旧的浏览器中工作。
如果需要支持更旧的浏览器,目前建议继续使用 v3.4。我们正在积极探索兼容模式,以便大家更早升级,后续会分享更多消息。
移除 @tailwind 指令
在 v4 中,使用常规 CSS @import 语句导入 Tailwind,而不再使用 v3 中的 @tailwind 指令:
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";移除已弃用的 utility
我们移除了在 v3 中已弃用、并且多年未再写入文档的 utility。下面列出已移除项及其现代替代:
| 已弃用 | 替代 |
|---|---|
bg-opacity-* | 使用透明度修饰符,例如 bg-black/50 |
text-opacity-* | 使用透明度修饰符,例如 text-black/50 |
border-opacity-* | 使用透明度修饰符,例如 border-black/50 |
divide-opacity-* | 使用透明度修饰符,例如 divide-black/50 |
ring-opacity-* | 使用透明度修饰符,例如 ring-black/50 |
placeholder-opacity-* | 使用透明度修饰符,例如 placeholder-black/50 |
flex-shrink-* | shrink-* |
flex-grow-* | grow-* |
overflow-ellipsis | text-ellipsis |
decoration-slice | box-decoration-slice |
decoration-clone | box-decoration-clone |
重命名的 utility
为了更一致、更可预期,我们在 v4 中重命名了下列 utility:
| v3 | v4 |
|---|---|
shadow-sm | shadow-xs |
shadow | shadow-sm |
drop-shadow-sm | drop-shadow-xs |
drop-shadow | drop-shadow-sm |
blur-sm | blur-xs |
blur | blur-sm |
backdrop-blur-sm | backdrop-blur-xs |
backdrop-blur | backdrop-blur-sm |
rounded-sm | rounded-xs |
rounded | rounded-sm |
outline-none | outline-hidden |
ring | ring-3 |
更新了 shadow、radius 和 blur 比例
我们重命名了默认的 shadow、radius 和 blur 比例,确保每个 utility 都有命名值。「裸」版本仍可向后兼容,但 <em><utility></em>-sm 这类 utility 看起来会不同,除非更新为对应的 <em><utility></em>-xs 版本。
要适配这些变更,请把所有 v3 utility 替换为对应的 v4 版本:
<input class="shadow-sm" />
<input class="shadow-xs" />
<input class="shadow" />
<input class="shadow-sm" />重命名了 outline utility
outline utility 现在默认设置 outline-width: 1px,以便与 border、ring utility 更一致。此外,所有 outline-<number> utility 都会把 outline-style 默认为 solid,无需再与 outline 组合使用:
<input class="outline outline-2" />
<input class="outline-2" />此前的 outline-none utility 实际上并没有设置 outline-style: none,而是设置了不可见的轮廓,以便在强制颜色模式下仍能显示,满足无障碍需求。
为了更清晰,我们把该 utility 重命名为 outline-hidden,并新增真正设置 outline-style: none 的 outline-none utility。
要适配此变更,请把所有 outline-none 替换为 outline-hidden:
<input class="focus:outline-none" />
<input class="focus:outline-hidden" />默认 ring 宽度变更
在 v3 中,ring utility 会添加 3px 的 ring。v4 中改为 1px,以便与 border、outline 保持一致。
要适配此变更,请把所有 ring 替换为 ring-3:
<input class="ring ring-blue-500" />
<input class="ring-3 ring-blue-500" />Space-between 选择器
我们更改了 space-x-* 和 space-y-* utility 使用的选择器,以解决大页面上的严重性能问题:
/* Before */
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
margin-top: 1rem;
}
/* Now */
.space-y-4 > :not(:last-child) {
margin-bottom: 1rem;
}如果你曾把这些 utility 用在行内元素上,或曾给子元素额外加外边距来微调间距,项目中可能会看到变化。
如果此变更在项目中造成问题,建议迁移到 flex 或 grid 布局,改用 gap:
<div class="space-y-4 p-4">
<div class="flex flex-col gap-4 p-4">
<label for="name">Name</label>
<input type="text" name="name" />
</div>Divide 选择器
我们更改了 divide-x-* 和 divide-y-* utility 使用的选择器,以解决大页面上的严重性能问题:
/* Before */
.divide-y-4 > :not([hidden]) ~ :not([hidden]) {
border-top-width: 4px;
}
/* Now */
.divide-y-4 > :not(:last-child) {
border-bottom-width: 4px;
}如果你曾把这些 utility 用在行内元素上、曾给子元素额外加外边距/内边距来微调间距,或调整过特定子元素的边框,项目中可能会看到变化。
在渐变上使用变体
在 v3 中,用变体覆盖渐变的一部分会「重置」整个渐变,因此这个例子里,深色模式下 to-* 颜色会变成透明而不是黄色:
<!-- [!code classes:dark:from-blue-500] -->
<div class="bg-gradient-to-r from-red-500 to-yellow-400 dark:from-blue-500">
<!-- ... -->
</div>在 v4 中,这些值会被保留,这与 Tailwind 中其他 utility 的行为更一致。
这意味着,如果要在特定状态下把三色标渐变「取消」回两色标渐变,可能需要显式使用 via-none:
<!-- [!code classes:dark:via-none] -->
<div class="bg-linear-to-r from-red-500 via-orange-400 to-yellow-400 dark:via-none dark:from-blue-500 dark:to-teal-400">
<!-- ... -->
</div>Container 配置
在 v3 中,container utility 有 center、padding 等若干配置选项,这些在 v4 中已不存在。
要在 v4 中自定义 container utility,请使用 @utility 指令扩展它:
@utility container {
margin-inline: auto;
padding-inline: 2rem;
}默认边框颜色
在 v3 中,border-* 和 divide-* utility 默认使用你配置的 gray-200 颜色。v4 中改为 currentColor,让 Tailwind 少一些主张,并与浏览器默认值一致。
要适配此变更,请在使用 border-* 或 divide-* utility 的地方都指定颜色:
<!-- [!code classes:border-gray-200] -->
<div class="border border-gray-200 px-2 py-3 ...">
<!-- ... -->
</div>或者,把这些基础样式加到项目中以保留 v3 行为:
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}默认 ring 宽度和颜色
我们把 ring utility 的宽度从 3px 改为 1px,并把默认颜色从 blue-500 改为 currentColor,以便与 border-*、divide-*、outline-* utility 更一致。
要适配这些变更,请把所有 ring 替换为 ring-3:
<!-- prettier-ignore -->
<button class="focus:ring ...">
<button class="focus:ring-3 ...">
<!-- ... -->
</button>然后,在依赖默认 ring 颜色的地方加上 ring-blue-500:
<!-- [!code classes:focus:ring-blue-500] -->
<button class="focus:ring-3 focus:ring-blue-500 ...">
<!-- ... -->
</button>或者,把这些主题变量加到 CSS 中以保留 v3 行为:
@theme {
--default-ring-width: 3px;
--default-ring-color: var(--color-blue-500);
}但请注意,这些变量仅为兼容而支持,并不被视为 Tailwind CSS v4.0 的惯用写法。
Preflight 变更
我们在 v4 中对 Preflight 的基础样式做了几处小改动:
新的默认占位符颜色
在 v3 中,占位符文本默认使用你配置的 gray-400 颜色。v4 中简化为使用当前文本颜色的 50% 不透明度。
你可能根本注意不到这个变化(甚至可能让项目更好看),但如果想保留 v3 行为,把这段 CSS 加到项目中:
@layer base {
input::placeholder,
textarea::placeholder {
color: var(--color-gray-400);
}
}按钮使用默认光标
按钮现在使用 cursor: default 而不是 cursor: pointer,以匹配浏览器默认行为。
如果希望默认继续使用 cursor: pointer,把这些基础样式加到 CSS 中:
@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}移除了 dialog 外边距
Preflight 现在会重置 <dialog> 元素的外边距,与其他元素的重置方式保持一致。
如果仍希望 dialog 默认居中,把这段 CSS 加到项目中:
@layer base {
dialog {
margin: auto;
}
}hidden 属性优先
block 或 flex 等 display class 不再优先于元素上的 hidden 属性。如果希望元素对用户可见,请移除 hidden 属性。注意这不适用于 hidden="until-found"。
使用前缀
前缀现在看起来像变体,并且始终位于 class 名的开头:
<!-- [!code classes:tw:bg-red-500,tw:flex,tw:hover:bg-red-600] -->
<div class="tw:flex tw:bg-red-500 tw:hover:bg-red-600">
<!-- ... -->
</div>使用前缀时,主题变量仍应按「未使用前缀」的方式配置:
@import "tailwindcss" prefix(tw);
@theme {
--font-display: "Satoshi", "sans-serif";
--breakpoint-3xl: 120rem;
--color-avocado-100: oklch(0.99 0 0);
--color-avocado-200: oklch(0.98 0.04 113.22);
--color-avocado-300: oklch(0.94 0.11 115.03);
/* ... */
}生成的 CSS 变量会带上前缀,以免与项目中已有变量冲突:
:root {
--tw-font-display: "Satoshi", "sans-serif";
--tw-breakpoint-3xl: 120rem;
--tw-color-avocado-100: oklch(0.99 0 0);
--tw-color-avocado-200: oklch(0.98 0.04 113.22);
--tw-color-avocado-300: oklch(0.94 0.11 115.03);
/* ... */
}important 修饰符
在 v3 中,可以在 utility 名开头(但在所有变体之后)放一个 ! 来标记为 important。在 v4 中,应把 ! 放在 class 名的最末尾:
<!-- [!code classes:bg-red-500!,flex!,hover:bg-red-600/50!] -->
<div class="flex! bg-red-500! hover:bg-red-600/50!">
<!-- ... -->
</div>旧写法仍为兼容而支持,但已弃用。
添加自定义 utility
在 v3 中,你在 @layer utilities 或 @layer components 中定义的自定义 class 会被 Tailwind 识别为真正的 utility class,并自动与 hover、focus、lg 等变体一起工作;区别在于 @layer components 在生成的样式表中始终排在前面。
在 v4 中我们使用原生层叠层,不再劫持 @layer at-rule,因此引入了 @utility API 作为替代:
@layer utilities {
.tab-4 {
tab-size: 4;
}
}
@utility tab-4 {
tab-size: 4;
}自定义 utility 现在还会按所定义属性的数量排序。这意味着像 .btn 这样的组件 utility 可以被其他 Tailwind utility 覆盖,无需额外配置:
@layer components {
.btn {
border-radius: 0.5rem;
padding: 0.5rem 1rem;
background-color: ButtonFace;
}
}
@utility btn {
border-radius: 0.5rem;
padding: 0.5rem 1rem;
background-color: ButtonFace;
}关于注册自定义 utility 的更多信息,见添加自定义 utility 文档。
变体叠加顺序
在 v3 中,叠加的变体从右到左应用;v4 中改为从左到右,更接近 CSS 语法。
要适配此变更,请把项目中任何对顺序敏感的叠加变体顺序反过来:
<!-- prettier-ignore -->
<ul class="py-4 first:*:pt-0 last:*:pb-0">
<ul class="py-4 *:first:pt-0 *:last:pb-0">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>这类写法即使有也通常很少——最可能用到的是直接子元素变体(*)以及 typography 插件的变体(prose-headings),而且也只有在它们与其他变体叠加时才需要关心。
任意值中的变量
在 v3 中,可以把 CSS 变量当作任意值使用而无需 var();但 CSS 的近期更新使这种写法常常产生歧义,因此 v4 把语法改成使用圆括号而不是方括号。
要适配此变更,请把旧的变量简写语法替换为新的变量简写语法:
<div class="bg-[--brand-color]"></div>
<div class="bg-(--brand-color)"></div>grid 与 object-position utility 中的任意值
此前,grid-cols-*、grid-rows-* 和 object-* utility 的任意值中,逗号会被替换为空格。这种特殊行为存在于 Tailwind CSS v3,是为了兼容 v2。v4.0 不再提供该兼容,必须用下划线表示空格。
要适配此变更,请把本意表示空格的逗号替换为下划线:
<div class="grid-cols-[max-content,auto]"></div>
<div class="grid-cols-[max-content_auto]"></div>移动端的 hover 样式
在 v4 中,我们更新了 hover 变体,仅在主输入设备支持悬停时才应用:
@media (hover: hover) {
.hover\:underline:hover {
text-decoration: underline;
}
}如果你的站点依赖触摸设备点按时触发 hover,这可能会带来问题。若这是你的情况,可以用采用旧实现的自定义变体覆盖 hover 变体:
@custom-variant hover (&:hover);不过通常我们建议把 hover 功能当作增强,而不是让站点依赖它才能工作,因为触摸设备并没有真正的悬停能力。
过渡 outline-color
transition 和 transition-colors utility 现在包含 outline-color 属性。
这意味着,如果你在 focus 时添加带自定义颜色的轮廓,会看到颜色从默认色过渡过来。为避免这种情况,请无条件设置轮廓颜色,或在两个状态都显式设置:
<button class="transition hover:outline-2 hover:outline-cyan-500"></button>
<button class="outline-cyan-500 transition hover:outline-2"></button>独立的 transform 属性
rotate-*、scale-* 和 translate-* utility 现在基于 CSS 中各自独立的 rotate、scale、translate 属性。通常这不应影响行为,但有几种情况需要注意:
重置 Transform
以前可以通过 transform-none「重置」rotate、scale、translate utility。现在不再有效,需要分别重置各个属性:
<button class="scale-150 focus:transform-none"></button>
<button class="scale-150 focus:scale-none"></button>过渡
如果自定义过渡属性列表并包含 transform(例如写成 transition-[opacity,transform]),这些 utility 将不再产生过渡。修复方法是在列表中包含各个独立属性。例如,若希望在使用 opacity-* 和 scale-* utility 时过渡变化,应改用 transition-[opacity,scale]。
<button class="transition-[opacity,transform] hover:scale-150"></button>
<button class="transition-[opacity,scale] hover:scale-150"></button>禁用核心插件
在 v3 中有一个 corePlugins 选项,可以彻底禁用框架中的某些 utility。v4 不再支持该选项。
使用 theme() 函数
由于 v4 会为所有主题值提供 CSS 变量,我们建议尽可能使用这些变量,而不是 theme() 函数:
.my-class {
background-color: theme(colors.red.500);
background-color: var(--color-red-500);
}如果仍需使用 theme() 函数(例如在不支持 CSS 变量的媒体查询中),应使用 CSS 变量名,而不是旧的点记法:
@media (width >= theme(screens.xl)) {
@media (width >= theme(--breakpoint-xl)) {
/* ... */
}使用 JavaScript 配置文件
JavaScript 配置文件仍为向后兼容而支持,但在 v4 中不再自动检测。
如果仍需使用 JavaScript 配置文件,可以用 @config 指令显式加载:
@config "../../tailwind.config.js";基于 JavaScript 的配置中的 corePlugins、safelist、separator 选项在 v4.0 中不受支持。要在 v4 中把 utility 加入 safelist,请使用 @source inline()。
在 JavaScript 中使用主题值
在 v3 中我们导出了 resolveConfig 函数,可以把基于 JavaScript 的配置转换成扁平对象,供其他 JavaScript 使用。
我们在 v4 中移除了它,希望大家直接使用我们生成的 CSS 变量——这样更简单,也能显著减小打包体积。
例如,流行的 React 库 Motion 支持从 CSS 变量值做动画:
// [!code word:var(--color-blue-500)]
<motion.div animate={{ backgroundColor: "var(--color-blue-500)" }} />如果需要在 JS 中访问已解析的 CSS 变量值,可以使用 getComputedStyle 获取文档根元素上的主题变量值:
let styles = getComputedStyle(document.documentElement);
let shadow = styles.getPropertyValue("--shadow-xl");在 Vue、Svelte 或 CSS modules 中使用 @apply
在 v4 中,与主 CSS 文件分开打包的样式表(例如 CSS modules 文件,以及 Vue、Svelte、Astro 中的 <style> 块等)无法访问其他文件中定义的主题变量、自定义 utility 和自定义变体。
要在这些上下文中使用这些定义,请用 @reference 导入它们,而不会在打包结果中重复 CSS:
<template>
<h1>Hello world!</h1>
</template>
<style>
@reference "../../app.css";
h1 {
@apply text-2xl font-bold text-red-500;
}
</style>或者,也可以完全不用 @apply,直接使用 CSS 主题变量;这还能提升性能,因为 Tailwind 不必处理这些样式:
<template>
<h1>Hello world!</h1>
</template>
<style>
h1 {
color: var(--text-red-500);
}
</style>使用 Sass、Less 和 Stylus
Tailwind CSS v4.0 并不设计为与 Sass、Less 或 Stylus 等 CSS 预处理器一起使用。把 Tailwind CSS 本身当作预处理器——你不应该把 Tailwind 和 Sass 一起用,就像你不会把 Sass 和 Stylus 一起用一样。因此,无法在样式表或 Vue、Svelte、Astro 等的 <style> 块中使用 Sass、Less 或 Stylus。
更多信息见兼容性文档。