添加自定义样式
Best practices for adding your own custom styles in Tailwind projects.
使用框架时,最大的挑战往往是:当框架没有替你处理某件事时,你该怎么做。
Tailwind 从一开始就按可扩展、可定制来设计,无论你在构建什么,都不会觉得自己在和框架较劲。
本指南涵盖如何自定义设计令牌、必要时如何突破这些约束、如何添加自己的自定义 CSS,以及如何用插件扩展框架。
自定义主题
若要更改调色板、间距比例、字体比例或断点等,请在 CSS 中用 @theme 指令添加自定义:
@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);
--color-avocado-400: oklch(0.92 0.19 114.08);
--color-avocado-500: oklch(0.84 0.18 117.33);
--color-avocado-600: oklch(0.53 0.12 118.34);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
--ease-snappy: cubic-bezier(0.2, 0, 0, 1);
/* ... */
}更多自定义主题的内容,见主题变量文档。
使用任意值
大多数精心打磨的设计,用一套受约束的设计令牌就能完成;偶尔你需要突破这些约束,才能做到像素级精确。
当你确实需要类似 top: 117px 才能把背景图放到正确位置时,用 Tailwind 的方括号记法,按任意值即时生成类名:
<!-- [!code classes:top-[117px]] -->
<div class="top-[117px]">
<!-- ... -->
</div>这基本上类似于内联样式,但一大好处是可以和 hover 这类交互修饰符、以及 lg 这类响应式修饰符组合使用:
<!-- [!code classes:top-[117px],lg:top-[344px]] -->
<div class="top-[117px] lg:top-[344px]">
<!-- ... -->
</div>这对框架中的一切都适用,包括背景色、字号、伪元素内容等:
<!-- [!code classes:bg-[#bada55],text-[22px],before:content-['Festivus']] -->
<div class="bg-[#bada55] text-[22px] before:content-['Festivus']">
<!-- ... -->
</div>如果任意值引用的是 CSS 变量,可以使用自定义属性语法:
<!-- [!code classes:fill-(--my-brand-color)] -->
<div class="fill-(--my-brand-color) ...">
<!-- ... -->
</div>这只是 fill-[var(--my-brand-color)] 的简写,会自动帮你加上 var() 函数。
任意属性
如果要用的 CSS 属性 Tailwind 没有开箱即用的工具类,也可以用方括号记法写完全任意的 CSS:
<!-- [!code classes:[mask-type:luminance]] -->
<div class="[mask-type:luminance]">
<!-- ... -->
</div>这真的很像内联样式,但同样的好处是你可以使用修饰符:
<!-- [!code classes:[mask-type:luminance],hover:[mask-type:alpha]] -->
<div class="[mask-type:luminance] hover:[mask-type:alpha]">
<!-- ... -->
</div>这对 CSS 变量也很有用,尤其是它们需要在不同条件下变化时:
<!-- [!code classes:[--scroll-offset:56px],lg:[--scroll-offset:44px]] -->
<div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]">
<!-- ... -->
</div>任意变体
任意变体类似于任意值,但用于即时修改选择器:就像内置伪类变体 hover:{utility} 或响应式变体 md:{utility} 那样,只不过直接在 HTML 里用方括号记法。
<!-- [!code word:\[&\:nth-child(-n+3)\]] -->
<ul role="list">
{#each items as item}
<li class="lg:[&:nth-child(-n+3)]:hover:underline">{item}</li>
{/each}
</ul>更多内容见任意变体文档。
处理空白
当任意值需要包含空格时,改用下划线(_),Tailwind 会在构建时自动把它转成空格:
<!-- [!code classes:grid-cols-[1fr_500px_2fr]] -->
<div class="grid grid-cols-[1fr_500px_2fr]">
<!-- ... -->
</div>在下划线很常见、但空格不合法的场景(例如 URL)中,Tailwind 会保留下划线,而不会把它转成空格:
<!-- [!code classes:bg-[url('/what_a_rush.png')]] -->
<div class="bg-[url('/what_a_rush.png')]">
<!-- ... -->
</div>极少数情况下,你确实需要下划线,但又因为空格也合法而会产生歧义,这时用反斜杠转义下划线,Tailwind 就不会把它转成空格:
<!-- [!code classes:before:content-['hello\_world']] -->
<div class="before:content-['hello\_world']">
<!-- ... -->
</div>如果使用 JSX 这类会从渲染后的 HTML 中去掉反斜杠的环境,请使用 String.raw(),这样反斜杠就不会被当成 JavaScript 转义字符:
<div className={String.raw`before:content-['hello\_world']`}>
<!-- ... -->
</div>消除歧义
Tailwind 中许多工具类共用命名空间,却对应不同的 CSS 属性。例如 text-lg 和 text-black 都使用 text- 命名空间,但一个对应 font-size,另一个对应 color。
使用任意值时,Tailwind 通常能根据你传入的值自动处理这种歧义:
<!-- [!code classes:text-[22px],text-[#bada55]] -->
<!-- Will generate a font-size utility -->
<div class="text-[22px]">...</div>
<!-- Will generate a color utility -->
<div class="text-[#bada55]">...</div>不过有时确实会有歧义,例如使用 CSS 变量时:
<!-- [!code classes:text-(--my-var)] -->
<div class="text-(--my-var)">...</div>这时,可以在值前面加上 CSS 数据类型,向 Tailwind「提示」底层类型:
<!-- [!code classes:text-(length:--my-var),text-(color:--my-var)] -->
<!-- Will generate a font-size utility -->
<div class="text-(length:--my-var)">...</div>
<!-- Will generate a color utility -->
<div class="text-(color:--my-var)">...</div>使用自定义 CSS
虽然 Tailwind 旨在覆盖大部分样式需求,但需要时你完全可以写普通 CSS:
@import "tailwindcss";
.my-custom-style {
/* ... */
}添加基础样式
如果只是想给页面设一些默认值(如文字颜色、背景色或字体系列),最简单的办法是给 html 或 body 元素加上一些类:
<!-- [!code classes:bg-gray-100,font-serif,text-gray-900] -->
<!doctype html>
<html lang="en" class="bg-gray-100 font-serif text-gray-900">
<!-- ... -->
</html>这样基础样式决策就和其余样式一起放在标记里,而不是藏在单独的文件中。
如果要为特定 HTML 元素添加自己的默认基础样式,用 @layer 指令把这些样式加到 Tailwind 的 base 层:
@layer base {
h1 {
font-size: var(--text-2xl);
}
h2 {
font-size: var(--text-xl);
}
}添加组件类
把更复杂、但仍希望能用工具类覆盖的类放到 components 层。
传统上这类类名会是 card、btn、badge 之类。
@layer components {
.card {
background-color: var(--color-white);
border-radius: var(--radius-lg);
padding: --spacing(6);
box-shadow: var(--shadow-xl);
}
}把组件类定义在 components 层后,必要时仍可用工具类覆盖它们:
<!-- [!code classes:card,rounded-none] -->
<!-- Will look like a card, but with square corners -->
<div class="card rounded-none">
<!-- ... -->
</div>使用 Tailwind 时,这类类可能没有你想的那么常用。关于我们的建议,请阅读管理重复指南。
components 层也很适合放置第三方组件的自定义样式:
@layer components {
.select2-dropdown {
/* ... */
}
}使用变体
在自定义 CSS 中用 @variant 指令应用 Tailwind 变体:
.my-element {
background: white;
@variant dark {
background: black;
}
}.my-element {
background: white;
@media (prefers-color-scheme: dark) {
background: black;
}
}如果要同时应用多个变体,用和 HTML 中一样的语法把它们叠在一起:
.my-element {
background: white;
@variant hover:focus {
background: black;
}
}.my-element {
background: white;
&:hover {
@media (hover: hover) {
&:focus {
background: black;
}
}
}
}要为多个变体应用相同样式,用逗号分隔各个变体:
.my-element {
background: white;
@variant hover, focus {
background: black;
}
}.my-element {
background: white;
&:hover {
@media (hover: hover) {
background: black;
}
}
&:focus {
background: black;
}
}添加自定义工具类
简单工具类
除了使用 Tailwind 自带的工具类,你也可以添加自己的自定义工具类。当你想用某个 CSS 特性,而 Tailwind 没有开箱即用的工具类时,这会很有用。
用 @utility 指令向项目添加自定义工具类:
@utility content-auto {
content-visibility: auto;
}现在可以在 HTML 中使用这个工具类:
<!-- [!code classes:content-auto] -->
<div class="content-auto">
<!-- ... -->
</div>它也适用于 hover、focus 和 lg 等变体:
<!-- [!code classes:hover:content-auto] -->
<div class="hover:content-auto">
<!-- ... -->
</div>自定义工具类会自动插入 utilities 层,与框架内置工具类放在一起。
复杂工具类
如果自定义工具类比单个类名更复杂,用嵌套来定义:
@utility scrollbar-hidden {
&::-webkit-scrollbar {
display: none;
}
}函数式工具类
除了用 @utility 注册简单工具类,还可以注册接受参数的函数式工具类:
@utility tab-* {
/* prettier-ignore */
tab-size: --value(--tab-size-*);
}特殊函数 --value() 用于解析工具类的值。
匹配主题值
用 --value(--theme-key-*) 语法,对照一组主题键解析工具类的值:
@theme {
--tab-size-2: 2;
--tab-size-4: 4;
--tab-size-github: 8;
}
@utility tab-* {
/* prettier-ignore */
tab-size: --value(--tab-size-*);
}这会匹配 tab-2、tab-4 和 tab-github 这类工具类。
裸值
要把值解析为裸值,使用 --value({type}) 语法,其中 {type} 是你希望用来校验裸值的数据类型:
@utility tab-* {
tab-size: --value(integer);
}这会匹配 tab-1 和 tab-76 这类工具类。
可用的裸值数据类型有:number、integer、ratio 和 percentage。
字面量值
要支持字面量值,使用 --value('literal') 语法(注意引号):
@utility tab-* {
tab-size: --value("inherit", "initial", "unset");
}这会匹配 tab-inherit、tab-initial 和 tab-unset 这类工具类。
任意值
要支持任意值,使用 --value([{type}]) 语法(注意方括号),告诉 Tailwind 哪些类型可作为任意值:
@utility tab-* {
tab-size: --value([integer]);
}这会匹配 tab-[1] 和 tab-[76] 这类工具类。
可用的任意值数据类型有:absolute-size、angle、bg-size、color、family-name、generic-name、image、integer、length、line-width、number、percentage、position、ratio、relative-size、url、vector 和 *。
同时支持主题值、裸值和任意值
--value() 函数的三种形式都可以作为多条声明写在同一条规则里,解析失败的声明会在输出中被省略:
@theme {
--tab-size-github: 8;
}
@utility tab-* {
tab-size: --value([integer]);
tab-size: --value(integer);
/* prettier-ignore */
tab-size: --value(--tab-size-*);
}这样就可以在不同情况下区别处理值,例如把裸整数转成百分比:
@utility opacity-* {
opacity: --value([percentage]);
opacity: calc(--value(integer) * 1%);
/* prettier-ignore */
opacity: --value(--opacity-*);
}如果你不需要在不同情况下区别处理返回值,--value() 也可以接受多个参数,并从左到右解析:
@theme {
--tab-size-github: 8;
}
@utility tab-* {
/* prettier-ignore */
tab-size: --value(--tab-size-*, integer, [integer]);
}
@utility opacity-* {
opacity: calc(--value(integer) * 1%);
/* prettier-ignore */
opacity: --value(--opacity-*, [percentage]);
}默认值
在 --value() 里使用 --default(),以便在工具类未显式传值时使用默认值:
/* [!code word:--default(4)] */
@utility tab-* {
tab-size: --value(integer, --default(4));
}这既匹配 tab-2 和 tab-4,也匹配只用默认值 4 的 tab:
.tab {
tab-size: 4;
}
.tab-2 {
tab-size: 2;
}--default() 也可以用在 --modifier() 里,在没有修饰符时提供默认值:
/* [!code word:--default(1)] */
@utility tab-* {
tab-size: --value(integer);
line-height: --modifier(integer, --default(1));
}这会匹配 tab-2/3(line-height 为 3),以及 tab-2(line-height 为 1)。
负值
要支持负值,请把正、负工具类分别注册为独立声明:
@utility inset-* {
inset: --spacing(--value(integer));
inset: --value([percentage], [length]);
}
@utility -inset-* {
inset: --spacing(--value(integer) * -1);
inset: calc(--value([percentage], [length]) * -1);
}修饰符
修饰符用 --modifier() 处理,它的工作方式和 --value() 完全一样,只不过作用在(若存在的)修饰符上:
@utility text-* {
/* prettier-ignore */
font-size: --value(--text-*, [length]);
/* prettier-ignore */
line-height: --modifier(--leading-*, [length], [*]);
}如果没有修饰符,任何依赖修饰符的声明都不会出现在输出中。
分数
处理分数时,我们依赖 CSS 的 ratio 数据类型。如果把它和 --value() 一起用,就是在告诉 Tailwind 把值和修饰符当作一个整体:
@utility aspect-* {
/* [!code word:ratio, \[ratio\]] */
/* prettier-ignore */
aspect-ratio: --value(--aspect-ratio-*, ratio, [ratio]);
}这会匹配 aspect-square、aspect-3/4 和 aspect-[7/9] 这类工具类。
添加自定义变体
除了使用 Tailwind 自带的变体,还可以用 @custom-variant 指令添加自己的自定义变体:
@custom-variant theme-midnight {
&:where([data-theme="midnight"] *) {
@slot;
}
}现在可以在 HTML 中使用 theme-midnight:<utility> 变体:
<!-- [!code classes:theme-midnight:bg-black] -->
<html data-theme="midnight">
<button class="theme-midnight:bg-black ..."></button>
</html>不需要嵌套时,可以用简写语法创建变体:
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));自定义变体有多条规则时,可以把它们互相嵌套:
@custom-variant any-hover {
@media (any-hover: hover) {
&:hover {
@slot;
}
}
}