Tailwind CSS 入门教程
@custom-variant:自定义变体
本教程共 50 篇 · 第 41 篇 · 更新于 2026-07-29 · 约 6 分钟阅读
Tailwind CSSTailwind CSS 入门教程@custom-variant变体variant伪类状态
41. @custom-variant:自定义变体
本节目标:掌握
@custom-variant指令的用法,学会创建自定义变体来扩展 Tailwind 的状态系统,实现任意条件触发的工具类。
Tailwind 内置了 hover、focus、dark 这些常见变体,但实际需求往往更复杂。@custom-variant 就是用来突破这个限制的——你自己定义规则。
基本用法
自定义变体的核心是一个选择器,当条件匹配时,内部的样式生效:
@import "tailwindcss";
/* 基于 data-theme 属性的自定义变体 */
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
定义后就可以使用 theme-midnight: 前缀:
<html data-theme="midnight">
<button class="theme-midnight:bg-black theme-midnight:text-white">
在午夜主题下变黑
</button>
</html>
常见自定义变体
暗黑模式(手动类策略)
v4 默认使用 prefers-color-scheme 检测系统主题。如果你想手动切换暗黑模式:
@custom-variant dark (&:where(.dark, .dark *));
<!-- 在 html 或 body 上添加 dark 类即可切换 -->
<html class="dark">
<div class="bg-white dark:bg-gray-900">
手动暗黑模式
</div>
</html>
任意悬停(不区分设备)
v4 的 hover 变体默认只在支持悬停的设备上生效。如果你想让所有设备都支持悬停效果:
@custom-variant any-hover (&:hover);
<div class="any-hover:bg-gray-100">
所有设备都响应悬停
</div>
首项/末项变体
@custom-variant first-of-type (&:first-of-type);
@custom-variant last-of-type (&:last-of-type);
<ul>
<li class="first-of-type:font-bold last-of-type:border-b-0">第一项加粗</li>
<li class="first-of-type:font-bold last-of-type:border-b-0">中间项</li>
<li class="first-of-type:font-bold last-of-type:border-b-0">末项无边框</li>
</ul>
奇偶项变体
@custom-variant odd (&:nth-child(odd));
@custom-variant even (&:nth-child(even));
<table>
<tr class="odd:bg-white even:bg-gray-50">
<tr>奇数行</tr>
</tr>
<tr class="odd:bg-white even:bg-gray-50">
<tr>偶数行</tr>
</tr>
</table>
复杂条件变体
基于属性的变体
/* 当元素有 data-loading 属性时生效 */
@custom-variant loading (&[data-loading]);
/* 当元素有 data-state="open" 属性时生效 */
@custom-variant open (&[data-state="open"]);
<div class="opacity-100 loading:opacity-50 loading:pointer-events-none">
加载状态
</div>
<div class="scale-95 opacity-0 open:scale-100 open:opacity-100">
展开状态
</div>
基于父元素的变体
/* 当父元素有 data-theme="dark" 时 */
@custom-variant parent-dark (&:where([data-theme="dark"] *));
/* 当任意祖先有 .group 类时 */
@custom-variant group-active (&:where(.group:active *));
<div data-theme="dark">
<span class="parent-dark:text-white">父元素是暗色主题</span>
</div>
基于子元素的变体
/* 当元素内部有获得焦点的子元素时 */
@custom-variant is-focus-within (&:has(:focus));
<div class="border-gray-300 is-focus-within:border-blue-500">
<input class="outline-none">
<!-- 当 input 获得焦点时,父 div 边框变蓝 -->
</div>
变体语法详解
完整语法
@custom-variant <name> {
<selector> {
@slot;
}
}
<selector> 中使用 & 代表当前元素,@slot 是变体内容的占位符。
简写语法
当不需要嵌套时,可以用简写:
/* 完整写法 */
@custom-variant dark {
&:where(.dark, .dark *) {
@slot;
}
}
/* 简写 */
@custom-variant dark (&:where(.dark, .dark *));
多规则变体
当变体需要多个条件时,规则可以嵌套:
@custom-variant any-hover {
@media (any-hover: hover) {
&:hover {
@slot;
}
}
}
这个变体的意思是:在支持悬停的设备上,悬停时生效。
实际应用场景
自定义数据主题
@custom-variant theme-ocean (&:where([data-theme="ocean"] *));
@custom-variant theme-forest (&:where([data-theme="forest"] *));
<html data-theme="ocean">
<button class="theme-ocean:bg-blue-600 theme-forest:bg-green-600">
根据主题变色
</button>
</html>
表单状态变体
@custom-variant invalid (&[aria-invalid="true"]);
@custom-variant checked-within (&:has(input:checked));
<div class="border-gray-300 invalid:border-red-500">
<input aria-invalid="true">
</div>
<div class="bg-white checked-within:bg-blue-50">
<input type="checkbox">
</div>
滚动状态变体
@custom-variant scrolled (&[data-scrolled="true"]);
<header class="bg-transparent scrolled:bg-white scrolled:shadow-md">
<!-- 滚动后添加 data-scrolled="true" -->
</header>
与 v3 的对比
在 v3 中,自定义变体写在 tailwind.config.js 里:
// v3 写法
module.exports = {
darkMode: ['class', '[data-theme="dark"]'],
plugins: [
function({ addVariant }) {
addVariant('theme-midnight', '[data-theme="midnight"] &');
}
]
}
v4 的 @custom-variant 直观多了,所有配置都在 CSS 里搞定,不用再去碰 JS 配置文件。
Tip自定义变体让 Tailwind 能适配任意设计需求。但不要过度使用——变体越多,生成的 CSS 也越多。只为真正需要的条件创建变体。