首页 / Tailwind CSS 入门教程 / @custom-variant:自定义变体

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 也越多。只为真正需要的条件创建变体。