Tailwind-主题变量

使用 utility 类作为您的设计令牌的 API。

使用 utility 类作为您的设计令牌的 API。

概述

Tailwind 是一个用于构建自定义设计的框架,不同的设计需要不同的排版、颜色、阴影、断点等等。

这些底层的设计决策通常被称为设计令牌,在 Tailwind 项目中,您将这些值存储在主题变量中。

什么是主题变量?

主题变量是使用 @theme 指令定义的特殊 CSS 变量,它会影响项目中存在的 utility 类。

例如,您可以通过定义类似 --color-mint-500 的主题变量,向您的项目添加新的颜色

css
@import "tailwindcss";

@theme {
  --color-mint-500: oklch(0.72 0.11 178);
}

现在您可以在 HTML 中使用类似 bg-mint-500text-mint-500fill-mint-500 的 utility 类

html
<div class="bg-mint-500">
  <!-- ... -->
</div>

Tailwind 还会为您的主题变量生成常规 CSS 变量,以便您可以在任意值或内联样式中引用您的设计令牌

html
<div style="background-color: var(--color-mint-500)">
  <!-- ... -->
</div>

主题变量命名空间文档中了解更多关于主题变量如何映射到不同 utility 类的信息。

为什么使用 @theme 而不是 :root

主题变量不仅仅是 CSS 变量 — 它们还指示 Tailwind 创建新的 utility 类(类似text-[23px]这种临时的 utility),您可以在 HTML 中使用它们。

由于它们的功能比常规 CSS 变量更多,Tailwind 使用特殊的语法,以便主题变量的定义始终是显式的。主题变量也必须在顶层定义,而不是嵌套在其他选择器或媒体查询下,使用特殊语法可以强制执行这一点。

当您想要定义一个不打算连接到 utility 类的变量时,使用 :root 定义常规 CSS 变量在 Tailwind 项目中仍然很有用。当您希望设计令牌直接映射到 utility 类时,请使用 @theme,而对于不应具有相应 utility 类的常规 CSS 变量,请使用 :root

与 utility 类的关系

Tailwind 中的一些 utility 类(如 flexobject-cover)是静态的,并且在每个项目中始终相同。但许多其他 utility 类是由主题变量驱动的,并且仅因您定义的主题变量而存在。

例如,在 --font-* 命名空间中定义的主题变量决定了项目中存在的所有 font-family utility 类

css
@theme {
  --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  /* ... */
}

默认情况下,font-sansfont-seriffont-mono utility 类之所以存在,是因为 Tailwind 的默认主题定义了 --font-sans--font-serif--font-mono 主题变量。

如果定义了另一个主题变量(如 --font-poppins),则会提供 font-poppins utility 类来与之配合使用

css
@import "tailwindcss";

@theme {
  --font-poppins: Poppins, sans-serif;
}
html
<h1 class="font-poppins">This headline will use Poppins.</h1>

您可以在这些命名空间内随意命名您的主题变量,并且具有相同名称的相应 utility 类将可用于您的 HTML。

与变体的关系

一些主题变量用于定义变体,而不是 utility 类。例如,--breakpoint-* 命名空间中的主题变量决定了项目中存在的响应式断点变体

css
@import "tailwindcss";

@theme {
  --breakpoint-3xl: 120rem;
}
//自定义断点设置

现在您可以使用 3xl:* 变体,仅当视口宽度为 120rem 或更宽时才触发 utility 类

html
<div class="3xl:grid-cols-6 grid grid-cols-2 md:grid-cols-4">
  <!-- ... -->
</div>

主题变量命名空间文档中了解更多关于主题变量如何映射到不同 utility 类和变体的信息。

主题变量命名空间

主题变量在命名空间中定义,每个命名空间对应于一个或多个 utility 类或变体 API。

在这些命名空间中定义新的主题变量将在您的项目中提供新的相应 utility 类和变体

命名空间Utility 类
--color-*颜色 utility 类,如 bg-red-500text-sky-300 等等
--font-*字体族 utility 类,如 font-sans
--text-*字体大小 utility 类,如 text-xl
--font-weight-*字体粗细 utility 类,如 font-bold
--tracking-*字母间距 utility 类,如 tracking-wide
--leading-*行高 utility 类,如 leading-tight
--breakpoint-*响应式断点变体,如 sm:*
--container-*容器查询变体,如 @sm:* 和尺寸 utility 类,如 max-w-md
--spacing-*间距和尺寸 utility 类,如 px-4max-h-16 等等
--radius-*边框半径 utility 类,如 rounded-sm
--shadow-*盒阴影 utility 类,如 shadow-md
--inset-shadow-*内阴影 utility 类,如 inset-shadow-xs
--drop-shadow-*阴影投射滤镜 utility 类,如 drop-shadow-md
--blur-*模糊滤镜 utility 类,如 blur-md
--perspective-*透视 utility 类,如 perspective-near
--aspect-*纵横比 utility 类,如 aspect-video
--ease-*过渡 timing 函数 utility 类,如 ease-out
--animate-*动画 utility 类,如 animate-spin

有关所有默认主题变量的列表,请参阅默认主题变量参考

默认主题变量

当您在 CSS 文件的顶部导入 tailwindcss 时,它会包含一组默认主题变量以帮助您入门。

以下是您在导入 tailwindcss 时实际导入的内容

css
@layer theme, base, components, utilities;

@import "./theme.css" layer(theme);
@import "./preflight.css" layer(base);
@import "./utilities.css" layer(utilities);
@layer 名称用途生效范围典型场景
theme定义设计系统变量全局自定义 CSS 变量、扩展 theme 值
base设置全局标签样式全局(自动应用)修改 h1/h2 默认样式、重写 reset
components复用的组件样式需手动添加类自定义按钮、卡片、布局组件
utilities新增/覆盖原子类需手动添加类增加自定义工具类,例如 .text-shadow

theme.css 文件包含默认的调色板、字体比例、阴影、字体等等

node_modules/tailwindcss/theme.css

css
@theme {
  --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

  --color-red-50: oklch(0.971 0.013 17.38);
  --color-red-100: oklch(0.936 0.032 17.717);
  --color-red-200: oklch(0.885 0.062 18.334);
  /* ... */

  --shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
  --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  /* ... */
}

这就是为什么像 bg-red-200font-serifshadow-sm 这样的 utility 类开箱即用的原因 — 它们是由默认主题驱动的,而不是像 flex-colpointer-events-none 那样硬编码到框架中的。

有关所有默认主题变量的列表,请参阅默认主题变量参考

自定义您的主题

默认主题变量非常通用,适用于构建截然不同的设计,但它们仍然只是一个起点。自定义调色板、字体和阴影等内容以构建您心中所想的确切设计非常常见。

扩展默认主题

使用 @theme 定义新的主题变量并扩展默认主题

css
@import "tailwindcss";

@theme {
  --font-script: Great Vibes, cursive;
}

这使得新的 font-script utility 类可用,您可以在 HTML 中使用它,就像默认的 font-sansfont-mono utility 类一样

html
<p class="font-script">This will use the Great Vibes font family.</p>

主题变量命名空间文档中了解更多关于主题变量如何映射到不同 utility 类和变体的信息。

覆盖默认主题

通过在 @theme 中重新定义默认主题变量值来覆盖它

app.css

css
@import "tailwindcss";

@theme {
  --breakpoint-sm: 30rem;
}

现在 sm:* 变体将在 30rem 而不是默认的 40rem 视口尺寸下触发

要完全覆盖默认主题中的整个命名空间,请使用特殊的星号语法将整个命名空间设置为 initial

css
@import "tailwindcss";

@theme {
  --color-*: initial;
  --color-white: #fff;
  --color-purple: #3f3cbb;
  --color-midnight: #121063;
  --color-tahiti: #3ab7bf;
  --color-bermuda: #78dcca;
}

当您这样做时,所有使用该命名空间的默认 utility 类*(如 bg-red-500都将被删除,并且只有您的自定义值(如 bg-midnight)*可用。

主题变量命名空间文档中了解更多关于主题变量如何映射到不同 utility 类和变体的信息。

使用自定义主题

要完全禁用默认主题并仅使用自定义值,请将全局主题变量命名空间设置为 initial

css
@import "tailwindcss";

@theme {
  --*: initial;

  --spacing: 4px;

  --font-body: Inter, sans-serif;

  --color-lagoon: oklch(0.72 0.11 221.19);
  --color-coral: oklch(0.74 0.17 40.24);
  --color-driftwood: oklch(0.79 0.06 74.59);
  --color-tide: oklch(0.49 0.08 205.88);
  --color-dusk: oklch(0.82 0.15 72.09);
}

现在,由主题变量驱动的所有默认 utility 类都将不可用,您将只能使用与您的自定义主题变量匹配的 utility 类,如 font-bodytext-dusk

定义动画关键帧

@theme 中定义 --animate-* 主题变量的 @keyframes 规则,以将它们包含在生成的 CSS 中

app.css

css
@import "tailwindcss";

@theme {
  --animate-fade-in-scale: fade-in-scale 0.3s ease-out;

  @keyframes fade-in-scale {
    0% {
      opacity: 0;
      transform: scale(0.95);
    }
    100% {
      opacity: 1;
      transform: scale(1);
    }
  }
}

如果您希望您的自定义 @keyframes 规则始终包含在内,即使在不添加 --animate-* 主题变量时也是如此,请在 @theme 外部定义它们。

如何把自定义动画和 Tailwind 的“主题系统”整合在一起,并解释 @theme 里定义和在外面定义的区别。

在主题里定义一个动画变量 --animate-fade-in-scale,它的值是 fade-in-scale 0.3s ease-out → 这样你就可以在 Tailwind 里用类似 animate-[var(--animate-fade-in-scale)] 之类的写法引用

定义了一个 @keyframes fade-in-scale,用来描述动画过程 → 因为它写在 @theme 里,所以只有当 --animate-fade-in-scale 被用到时,Tailwind 才会把这段 keyframes 生成进最终 CSS

重点:条件生成 vs 永久生成

“如果您希望您的自定义 @keyframes 规则始终包含在内,即使在不添加 --animate-* 主题变量时也是如此,请在 @theme 外部定义它们。”

意思是:

  • @theme 里写 keyframes → Tailwind 会把它当成“按需生成”的一部分,只有当你实际用到这个动画变量,才会把 keyframes 输出到最终 CSS
  • @theme 外面写 keyframes → Tailwind 会始终把 keyframes 输出到最终 CSS,不管你有没有用到

Tailwind 追求 按需生成 (tree-shaking),减少最终 CSS 体积:

  • 如果你的动画只偶尔用到,就放在 @theme 里 → Tailwind 只在你真的用到的时候生成
  • 如果你的动画是全局必须的(比如全局 loading 动画),就放在 @theme 外面 → 永远都会输出,避免缺失
定义位置行为适用场景
@theme 内按需生成,只有引用了 --animate-* 才会输出 keyframes想保持最终 CSS 尽量小,动画不是每个页面都用
@theme 外始终生成,哪怕没用到 --animate-*动画是全局必须,或者不怕多输出一些 CSS

--animate-fade-in-scale: fade-in-scale 0.3s ease-out; --animate-fade-in-scale:定义了一个 CSS 自定义属性 (CSS variable)

值是 fade-in-scale 0.3s ease-out,对应 animation 属性的值:

fade-in-scale → 动画的 @keyframes 名称

0.3s → 动画持续时间

ease-out → 动画的 timing function

这行的作用就是: ✅ 给动画取了一个名字,并把完整的动画声明打包成主题变量

引用其他变量

当定义引用其他变量的主题变量时,请使用 inline 选项

app.css

css
@import "tailwindcss";

@theme inline {
  --font-sans: var(--font-inter);
}

使用 inline 选项,utility 类将使用主题变量,而不是引用实际的主题变量

css
.font-sans {
  font-family: var(--font-inter);
}

如果不使用 inline,您的 utility 类可能会因 CSS 中变量的解析方式而解析为意外的值。

html
<div id="parent" style="--font-sans: var(--font-inter, sans-serif);">
  <div id="child" style="--font-inter: Inter; font-family: var(--font-sans);">
    This text will use the sans-serif font, not Inter.
  </div>
</div>

CSS 自定义属性 (CSS variables) 的作用域 + 计算时机 问题,Tailwind 里的 inline 是专门用来解决这种“作用域错位”导致的解析 bug 的。我们来拆开分析一下:

#parent 定义了 --font-sans: var(--font-inter, sans-serif)

  • 注意:这里的 var(--font-inter) 会在 #parent 的上下文 里立即解析

但此时 #parent 没有 --font-inter,所以解析结果就是 sans-serif

当浏览器渲染 #child 时,它会使用从父级继承下来的 --font-sans(已经被解析成 sans-serif),即使此时 #child 上有 --font-inter: Inter,也不会重新计算

为什么 Tailwind 会受影响

Tailwind 在 v4 里大量用 CSS 变量生成 utility class(例如字体、颜色、间距等),如果你在父级定义了一个变量依赖另一个变量,而那个被依赖的变量只在子级才定义,就会发生和上面一样的“解析提前”的问题。

inline 的作用

Tailwind 提供的 inline 关键字就是解决这个问题的。 它会告诉 Tailwind:

不要把这个变量的值提前算好,而是直接原样保留到最终 CSS,让它在使用位置才解析。

换句话说:

  • 没有 inline--font-sans 会被当场解析 → 可能得到错误的值
  • 有了 inline--font-sans 保留原始表达式,等真正用到时才解析 → 能拿到最新的 --font-inter

4️⃣ 用 inline 改造后的写法

Tailwind v4 的推荐写法可能是这样(假设你在 theme 里定义 font family):

@theme {
  --font-sans: inline var(--font-inter, sans-serif);
}

这样,Tailwind 会生成的 CSS 保留 var(--font-inter) 原样:

:root {
  --font-sans: var(--font-inter, sans-serif); /* 不会提前解析 */
}

结果就是:

  • #child 定义 --font-inter: Inter;
  • 当浏览器渲染 #childfont-family: var(--font-sans)
  • 变量会在 #child 的上下文解析,能正确拿到 Inter

生成所有 CSS 变量

默认情况下,只有使用的 CSS 变量才会生成在最终的 CSS 输出中。如果您想始终生成所有 CSS 变量,可以使用 static 主题选项

css
@import "tailwindcss";

@theme static {
  --color-primary: var(--color-red-500);
  --color-secondary: var(--color-blue-500);
}

Tailwind 现在用 CSS 变量来实现 theme 系统static 选项就是控制这些变量是否“按需生成”的开关。

以前(v3 及以前),Tailwind 的 theme 是编译时常量:

  • 你在 tailwind.config.js 里改颜色
  • Tailwind 会直接生成 .bg-red-500 { background-color: #f87171; }
  • 没有 CSS 变量,值是直接写死的

但从 v4 开始,Tailwind 把 theme 值编译成了 CSS 自定义属性 (CSS variables),例如:

css
:root {
  --color-red-500: #f87171;
  --spacing-4: 1rem;
}

然后 utility class 变成用变量的形式引用:

css
.bg-red-500 {
  background-color: var(--color-red-500);
}

.p-4 {
  padding: var(--spacing-4);
}

好处:

  • 可以在运行时动态修改主题(比如暗色模式切换、动态换色)
  • 可以更好地 tree-shaking(按需生成)

2️⃣ “只有使用的 CSS 变量才会生成”是什么意思

Tailwind v4 默认会“按需生成” CSS 变量:

  • 你用了 bg-red-500,它就会生成 --color-red-500
  • 你没用 bg-blue-500,它就不会生成 --color-blue-500

这样最终输出的 CSS 更小。


3️⃣ 什么是 static 选项

如果你不想按需生成,而是希望无论是否用到,都把所有 theme 值变成 CSS 变量输出,就可以在配置里加:

js
// tailwind.config.js
export default {
  theme: {
    static: true,
  },
};

这样就会一次性生成所有变量:

css
:root {
  --color-red-500: #f87171;
  --color-blue-500: #3b82f6;
  --color-green-500: #22c55e;
  /* ...所有 theme 颜色都会生成 */
}

跨项目共享

因为 Tailwind v4 的 主题变量是用 CSS 变量写出来的,所以要在多个项目里用同一套主题,只需要把这些变量写进一个单独的 CSS 文件,然后在每个项目里引入这个文件就行。

./packages/brand/theme.css

css
@theme {
  --*: initial;

  --spacing: 4px;

  --font-body: Inter, sans-serif;

  --color-lagoon: oklch(0.72 0.11 221.19);
  --color-coral: oklch(0.74 0.17 40.24);
  --color-driftwood: oklch(0.79 0.06 74.59);
  --color-tide: oklch(0.49 0.08 205.88);
  --color-dusk: oklch(0.82 0.15 72.09);
}

然后,您可以使用 @import 将您的主题变量包含在其他项目中

./packages/admin/app.css

css
@import "tailwindcss";
@import "../brand/theme.css";

您可以将像这样的共享主题变量放在 monorepo 设置中的自己的包中,甚至将它们发布到 NPM 并像导入任何其他第三方 CSS 文件一样导入它们。

使用您的主题变量

当您编译 CSS 时,您的所有主题变量都会转换为常规 CSS 变量

dist.css

css
:root {
  --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

  --color-red-50: oklch(0.971 0.013 17.38);
  --color-red-100: oklch(0.936 0.032 17.717);
  --color-red-200: oklch(0.885 0.062 18.334);
  /* ... */

  --shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
  --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  /* ... */
}

这使得在您的任何自定义 CSS 或内联样式中轻松引用您的所有设计令牌。

使用自定义 CSS

如果你自己写了额外的 CSS 样式,而这些样式里需要用到和 Tailwind 一样的颜色、字体、间距,就直接用 Tailwind 生成的 主题变量,这样才能保证你写的 CSS 和 Tailwind 样式用的是同一套设计值,不会不一致。

app.css

css
@import "tailwindcss";

@layer components {
  .typography {
    p {
      font-size: var(--text-base);
      color: var(--color-gray-700);
    }

    h1 {
      font-size: var(--text-2xl--line-height);
      font-weight: var(--font-weight-semibold);
      color: var(--color-gray-950);
    }

    h2 {
      font-size: var(--text-xl);
      font-weight: var(--font-weight-semibold);
      color: var(--color-gray-950);
    }
  }
}

当你拿到一些不是自己写的 HTML(比如从数据库、API 里拿到的 Markdown 转成的 HTML),你没办法给它加上 Tailwind 的类名,这时候就可以用全局样式(比如 @layer base)去统一设置它的样式。

比如你从后端拿到一段文章内容,渲染出来是这样:

<div class="prose-content">
  <h1>标题</h1>
  <p>正文文本</p>
  <a href="#">一个链接</a>
</div>

你没办法给 <h1><p> 每个都加 text-xlmb-4 这些 Tailwind 类,这时候你可以在 CSS 里写:

@layer base {
  .prose-content h1 {
    @apply text-2xl font-bold mb-4;
  }
  .prose-content p {
    @apply mb-2 leading-relaxed;
  }
  .prose-content a {
    @apply text-blue-500 underline;
  }
}

这样所有渲染出来的文章都能自动带样式。

使用任意值

在任意值中使用主题变量可能很有用,尤其是在与 calc() 函数结合使用时。

html
<div class="relative rounded-xl">
  <div class="absolute inset-px rounded-[calc(var(--radius-xl)-1px)]">
    <!-- ... -->
  </div>
  <!-- ... -->
</div>

在上面的示例中,我们从嵌套的内嵌元素的 --radius-xl 值中减去 1px,以确保它具有同心边框半径。

在 JavaScript 中引用

大多数时候,当您需要在 JS 中引用您的主题变量时,您可以像使用任何其他 CSS 值一样直接使用 CSS 变量。

例如,流行的 React Motion 库允许您动画到和从 CSS 变量值

jsx
<motion.div animate={{ backgroundColor: "var(--color-blue-500)" }} />

如果您需要在 JS 中访问已解析的 CSS 变量值,您可以使用 getComputedStyle 获取文档根目录上主题变量的值

js
let styles = getComputedStyle(document.documentElement);
let shadow = styles.getPropertyValue("--shadow-xl");

评论

评论加载中……