核心概念
从一组受限的原始实用工具构建复杂组件。
你可以使用 Tailwind,通过在你的标记中直接组合许多单用途的展示类(实用类)来设置样式
你有一条新消息!
<div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg outline outline-black/5 dark:bg-slate-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10"> <img class="size-12 shrink-0" src="/img/logo.svg" alt="ChitChat Logo" /> <div> <div class="text-xl font-medium text-black dark:text-white">ChitChat</div> <p class="text-gray-500 dark:text-gray-400">You have a new message!</p> </div></div>
例如,在上面的 UI 中,我们使用了
这种样式设置方式与许多传统的最佳实践相悖,但一旦你尝试过,你将很快注意到一些非常重要的好处
这些好处在小型项目中产生了很大的不同,但对于在大型长期项目中工作的团队来说,它们更有价值。
对这种方法的常见反应是想知道,“这不就是内联样式吗?” 在某些方面是的 —— 你正在将样式直接应用于元素,而不是为它们分配一个类名,然后再设置该类的样式。
但是,使用实用类比内联样式有很多重要的优势,例如
这个组件是完全响应式的,并且包含一个带有悬停和激活样式的按钮,完全使用实用类构建
Erin Lindford
产品工程师
<div class="flex flex-col gap-2 p-8 sm:flex-row sm:items-center sm:gap-6 sm:py-4 ..."> <img class="mx-auto block h-24 rounded-full sm:mx-0 sm:shrink-0" src="/img/erin-lindford.jpg" alt="" /> <div class="space-y-2 text-center sm:text-left"> <div class="space-y-0.5"> <p class="text-lg font-semibold text-black">Erin Lindford</p> <p class="font-medium text-gray-500">Product Engineer</p> </div> <button class="border-purple-200 text-purple-600 hover:border-transparent hover:bg-purple-600 hover:text-white active:bg-purple-700 ..."> Message </button> </div></div>
要在悬停或焦点等状态下设置元素的样式,请在你想要定位的状态前缀任何实用程序,例如 `hover:bg-sky-700`
悬停在此按钮上以查看背景颜色变化
<button class="bg-sky-500 hover:bg-sky-700 ...">Save changes</button>
这些前缀在 Tailwind 中被称为 变体,并且它们仅在变体的条件匹配时才应用来自实用类的样式。
这是为 `hover:bg-sky-700` 类生成的 CSS 的样子
.hover\:bg-sky-700 { &:hover { background-color: var(--color-sky-700); }}
注意这个类在元素未悬停时什么都不做?它唯一的任务是提供悬停样式 —— 仅此而已。
这与你编写传统 CSS 的方式不同,在传统 CSS 中,单个类通常会为多种状态提供样式
<button class="btn">Save changes</button><style> .btn { background-color: var(--color-sky-500); &:hover { background-color: var(--color-sky-700); } }</style>
你甚至可以在 Tailwind 中堆叠变体,以便在多个条件匹配时应用实用程序,例如组合 `hover:` 和 `disabled:`
<button class="bg-sky-500 disabled:hover:bg-sky-500 ...">Save changes</button>
在关于 悬停、焦点和其他状态 的文档中了解更多信息。
就像悬停和焦点状态一样,你可以通过在你想要应用样式的断点前缀任何实用程序,在不同的断点处设置元素的样式
调整此示例的大小以查看布局变化
<div class="grid grid-cols-2 sm:grid-cols-3"> <!-- ... --></div>
在上面的示例中,`sm:` 前缀确保 `grid-cols-3` 仅在 `sm` 断点及以上触发,即默认的 40rem
.sm\:grid-cols-3 { @media (width >= 40rem) { grid-template-columns: repeat(3, minmax(0, 1fr)); }}
在 响应式设计 文档中了解更多信息。
在暗黑模式下设置元素的样式只需在你想在暗黑模式激活时应用的任何实用程序前缀添加 `dark:`
亮色模式
倒过来写字
Zero Gravity Pen 可以用于在任何方向书写,包括倒过来。它甚至可以在外太空工作。
暗黑模式
倒过来写字
Zero Gravity Pen 可以用于在任何方向书写,包括倒过来。它甚至可以在外太空工作。
<div class="bg-white dark:bg-gray-800 rounded-lg px-6 py-8 ring shadow-xl ring-gray-900/5"> <div> <span class="inline-flex items-center justify-center rounded-md bg-indigo-500 p-2 shadow-lg"> <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true" > <!-- ... --> </svg> </span> </div> <h3 class="text-gray-900 dark:text-white mt-5 text-base font-medium tracking-tight ">Writes upside-down</h3> <p class="text-gray-500 dark:text-gray-400 mt-2 text-sm "> The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space. </p></div>
就像悬停状态或媒体查询一样,重要的是要理解单个实用类永远不会同时包含亮色和暗色样式 —— 你通过使用多个类来设置暗黑模式下的样式,一个用于亮色模式样式,另一个用于暗黑模式样式。
.dark\:bg-gray-800 { @media (prefers-color-scheme: dark) { background-color: var(--color-gray-800); }}
在 暗黑模式 文档中了解更多信息。
在 Tailwind 中,很多时候你甚至会使用多个类来构建单个 CSS 属性的值,例如向元素添加多个滤镜
<div class="blur-sm grayscale"> <!-- ... --></div>
这两种效果都依赖于 CSS 中的 `filter` 属性,因此 Tailwind 使用 CSS 变量来使组合这些效果成为可能
.blur-sm { --tw-blur: blur(var(--blur-sm)); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-grayscale,);}.grayscale { --tw-grayscale: grayscale(100%); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-grayscale,);}
上面生成的 CSS 略有简化,但这里的技巧是每个实用程序都为它要应用的效果设置一个 CSS 变量。然后 `filter` 属性查看所有这些变量,如果变量尚未设置,则回退到无。
Tailwind 对 渐变、阴影颜色、变形 等使用了相同的方法。
Tailwind 中的许多实用程序都由 主题变量 驱动,例如 `bg-blue-500`、`text-xl` 和 `shadow-md`,它们映射到你的底层调色板、字体大小和阴影。
当你需要使用主题之外的一次性值时,请使用特殊的方括号语法来指定任意值
<button class="bg-[#316ff6] ..."> Sign in with Facebook</button>
这对于调色板之外的一次性颜色(例如上面的 Facebook 蓝色)可能很有用,但也适用于当你需要复杂的自定义值(例如非常特定的网格)时
<div class="grid grid-cols-[24rem_2.5rem_minmax(0,1fr)]"> <!-- ... --></div>
当你需要使用 CSS 功能(如 `calc()`)时,即使你正在使用你的主题值,它也很有用
<div class="max-h-[calc(100dvh-(--spacing(6))]"> <!-- ... --></div>
甚至有一种语法可以生成完全任意的 CSS,包括任意属性名称,这对于设置 CSS 变量可能很有用
<div class="[--gutter-width:1rem] lg:[--gutter-width:2rem]"> <!-- ... --></div>
在关于 使用任意值 的文档中了解更多信息。
Tailwind CSS 不是一个大型静态样式表,就像你可能习惯于其他 CSS 框架一样 —— 它根据你在编译 CSS 时实际使用的类生成所需的 CSS。
它通过扫描项目中的所有文件来查找任何看起来可能是类名的符号来做到这一点
export default function Button({ size, children }) { let sizeClasses = { md: "px-4 py-2 rounded-md text-base", lg: "px-5 py-3 rounded-lg text-lg", }[size]; return ( <button type="button" className={`font-bold ${sizeClasses}`}> {children} </button> );}
在找到所有潜在的类之后,Tailwind 会为每个类生成 CSS,并将它们全部编译成一个只包含你实际需要的样式的样式表。
由于 CSS 是基于类名生成的,因此 Tailwind 可以识别使用任意值的类(如 `bg-[#316ff6]`)并生成必要的 CSS,即使该值不是你主题的一部分。
在 在源文件中检测类名 中了解更多关于其工作原理的信息。
有时你需要在多种条件组合下设置元素的样式,例如在暗黑模式下、在特定断点处、悬停时以及当元素具有特定数据属性时。
这是使用 Tailwind 实现该效果的示例
<button class="dark:lg:data-current:hover:bg-indigo-600 ..."> <!-- ... --></button>
@media (prefers-color-scheme: dark) and (width >= 64rem) { button[data-current]:hover { background-color: var(--color-indigo-600); }}
Tailwind 还支持 `group-hover` 之类的功能,它允许你在特定父元素悬停时设置元素的样式
<a href="#" class="group rounded-lg p-8"> <!-- ... --> <span class="group-hover:underline">Read more…</span></a>
@media (hover: hover) { a:hover span { text-decoration-line: underline; }}
这种 `group-*` 语法也适用于其他变体,例如 `group-focus`、`group-active` 和 更多。
对于真正复杂的场景(尤其是在设置你不控制的 HTML 的样式时),Tailwind 支持 任意变体,它允许你直接在类名中编写任何你想要的选择器
<div class="[&>[data-active]+span]:text-blue-600 ..."> <span data-active><!-- ... --></span> <span>This text will be blue</span></div>
div > [data-active] + span { color: var(--color-blue-600);}
内联样式在 Tailwind CSS 项目中仍然非常有用,尤其是在值来自动态源(如数据库或 API)时
export function BrandedButton({ buttonColor, textColor, children }) { return ( <button style={{ backgroundColor: buttonColor, color: textColor, }} className="rounded-md px-3 py-1.5 font-medium" > {children} </button> );}
对于非常复杂的任意值,当格式化为类名时难以阅读,你也可以使用内联样式
<div class="grid-[2fr_max(0,var(--gutter-width))_calc(var(--gutter-width)+10px)]"><div style="grid-template-columns: 2fr max(0, var(--gutter-width)) calc(var(--gutter-width) + 10px)"> <!-- ... --></div>
另一种有用的模式是使用内联样式基于动态源设置 CSS 变量,然后使用实用类引用这些变量
export function BrandedButton({ buttonColor, buttonColorHover, textColor, children }) { return ( <button style={{ "--bg-color": buttonColor, "--bg-color-hover": buttonColorHover, "--text-color": textColor, }} className="bg-(--bg-color) text-(--text-color) hover:bg-(--bg-color-hover) ..." > {children} </button> );}
当您仅使用实用类构建整个项目时,您不可避免地会发现自己重复某些模式以在不同位置重新创建相同的设计。
例如,这里每个头像图像的实用类重复了五次
<div> <div class="flex items-center space-x-2 text-base"> <h4 class="font-semibold text-slate-900">Contributors</h4> <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span> </div> <div class="mt-3 flex -space-x-2 overflow-hidden"> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> </div> <div class="mt-3 text-sm font-medium"> <a href="#" class="text-blue-500">+ 198 others</a> </div></div>
不要惊慌!实际上,这不是你可能担心的那个问题,并且处理它的策略是你每天都在做的事情。
很多时候,在渲染页面中多次出现的设计元素实际上只编写一次,因为实际的标记是在循环中渲染的。
例如,本指南开头的重复头像几乎肯定会在实际项目中以循环方式渲染
<div> <div class="flex items-center space-x-2 text-base"> <h4 class="font-semibold text-slate-900">Contributors</h4> <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span> </div> <div class="mt-3 flex -space-x-2 overflow-hidden"> {#each contributors as user} <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src={user.avatarUrl} alt={user.handle} /> {/each} </div> <div class="mt-3 text-sm font-medium"> <a href="#" class="text-blue-500">+ 198 others</a> </div></div>
当元素像这样在循环中渲染时,实际的类列表只编写一次,因此没有实际的重复问题需要解决。
当重复局限于单个文件中的一组元素时,处理它的最简单方法是使用 多光标编辑 来快速选择和一次编辑每个元素的类列表
你会惊讶于这最终成为最佳解决方案的频率。 如果你可以快速同时编辑所有重复的类列表,则引入任何额外的抽象都没有好处。
<nav class="flex justify-center space-x-4"> <a href="/dashboard" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Home </a> <a href="/team" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Team </a> <a href="/projects" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Projects </a> <a href="/reports" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Reports </a></nav>
如果你需要跨多个文件重用某些样式,最好的策略是创建一个组件(如果你正在使用像 React、Svelte 或 Vue 这样的前端框架),或者一个模板局部(如果你正在使用像 Blade、ERB、Twig 或 Nunjucks 这样的模板语言)。
export function VacationCard({ img, imgAlt, eyebrow, title, pricing, url }) { return ( <div> <img className="rounded-lg" src={img} alt={imgAlt} /> <div className="mt-4"> <div className="text-xs font-bold text-sky-500">{eyebrow}</div> <div className="mt-1 font-bold text-gray-700"> <a href={url} className="hover:underline"> {title} </a> </div> <div className="mt-2 text-sm text-gray-600">{pricing}</div> </div> </div> );}
现在你可以在任意多的地方使用此组件,同时仍然为样式保留单一的真理来源,以便可以轻松地在一个地方一起更新它们。
如果你正在使用像 ERB 或 Twig 这样的模板语言而不是像 React 或 Vue 这样的东西,那么为像按钮这样小的东西创建模板局部可能会感觉像是过度杀伤,相比于像 `btn` 这样的简单 CSS 类。
虽然强烈建议你为更复杂的组件创建适当的模板局部,但当模板局部感觉笨重时,编写一些自定义 CSS 完全可以。
这是一个 `btn-primary` 类的样子,使用 主题变量 来保持设计一致性
<button class="btn-primary">Save changes</button>
@import "tailwindcss";@layer components { .btn-primary { border-radius: calc(infinity * 1px); background-color: var(--color-violet-500); padding-inline: --spacing(5); padding-block: --spacing(2); font-weight: var(--font-weight-semibold); color: var(--color-white); box-shadow: var(--shadow-md); &:hover { @media (hover: hover) { background-color: var(--color-violet-700); } } }}
再次强调,对于任何比单个 HTML 元素更复杂的东西,我们强烈建议使用模板局部,以便样式和结构可以封装在一个地方。
当你添加两个针对同一 CSS 属性的类时,样式表中稍后出现的类获胜。 因此,在此示例中,即使 `flex` 在实际的 `class` 属性中最后出现,该元素也将接收 `display: grid`
<div class="grid flex"> <!-- ... --></div>
.flex { display: flex;}.grid { display: grid;}
总的来说,你应该永远不要向同一元素添加两个冲突的类 —— 始终只添加你实际想要生效的那个
export function Example({ gridLayout }) { return <div className={gridLayout ? "grid" : "flex"}>{/* ... */}</div>;}
使用像 React 或 Vue 这样的基于组件的库,这通常意味着暴露用于样式自定义的特定 props,而不是让消费者从组件外部添加额外的类,因为这些样式通常会冲突。
当你真的需要强制特定实用类生效并且没有其他管理特异性的方法时,你可以在类名末尾添加 `!` 以使所有声明都变为 `!important`
<div class="bg-teal-500 bg-red-500!"> <!-- ... --></div>
.bg-red-500\! { background-color: var(--color-red-500) !important;}.bg-teal-500 { background-color: var(--color-teal-500);}
如果你正在向具有现有复杂 CSS 和高特异性规则的项目添加 Tailwind,你可以在导入 Tailwind 时使用 `important` 标志将所有实用程序标记为 `!important`
@import "tailwindcss" important;
@layer utilities { .flex { display: flex !important; } .gap-4 { gap: 1rem !important; } .underline { text-decoration-line: underline !important; }}
如果你的项目具有与 Tailwind CSS 实用程序冲突的类名,你可以使用 `prefix` 选项为所有 Tailwind 生成的类和 CSS 变量添加前缀
@import "tailwindcss" prefix(tw);
@layer theme { :root { --tw-color-red-500: oklch(0.637 0.237 25.331); }}@layer utilities { .tw\:text-red-500 { color: var(--tw-color-red-500); }}