对于复杂网站的 CSS 管理问题,Tailwind 似乎是一个反直觉的解决方案。Tailwind 是由很多很多小的实用 CSS 类名组成的,其中大部分都是将一个特定的 CSS 属性设置为一个特定的值。在 Tailwind 中获得复杂行为的首选方式是在 HTML 元素上将多个 CSS 类组合在一起。

这种模式违背了许多多年来形成的 CSS 命名惯例。许多 CSS 框架和命名惯例建议使用反映页面上元素语义的名称 —— 如按钮、导航栏或菜单项。

Tailwind 类根本不是语义上的。它们是实用类,这意味着一个 Tailwind 类代表了一个特定的 CSS 属性,比如文本格式化的 font-oldm-6margin。其他的 CSS 框架包括实用类,但是认为语义类的名称更重要。使用 Tailwind 和实用类表明潜在的大量重复,因为 Tailwind 实用类经常在多个 DOM 元素上重复。

尽管有潜在的重复,但 Tailwind 可以在较大的网站上工作。

一个原因是,当你在任何一个特定点上应用 Tailwind 类时,造型变化的性质和该变化的范围都异常清晰。Tailwind 的短名称一开始可能看起来很神秘,但命名模式是一致的,变得更容易阅读。另外,Tailwind 的修改器使得在 HTML 中定义特殊行为变得容易,比如在不同大小的屏幕上的悬停和响应行为。修改器还使一个元素的整体风格更清晰,只需看一下 HTML。

因为你可以以任意的方式组合 Tailwind 的类,你在 Tailwind 中写的外部 CSS 代码比你在其他 CSS 样式中写的要少得多。使用 Tailwind 时,你不需要命名那么多的自定义 CSS 类。而且,由于 Tailwind 的变化与 HTML 标记紧密相连,所以更容易预测变化的结果。

通过 Tailwind,你可以从 Tailwind 工具列表中提取一个普通的 CSS 类,并给它一个更有语义的名字。与其这样创建你自己的类,Tailwind 建议利用你在前端堆栈中使用的相同工具来减少重复。例如,与其为按钮样式创建一个单独的 CSS 类,Tailwind 建议你创建一个可重用的 React 组件或一个 Rails 局部或辅助方法,并为该可重用项目只定义一次 CSS 样式。

Tailwind 由几个不同的部分组成:我们将在本书中花大部分时间处理的实用类,一个重置样式表,以及使 Tailwind 工作更容易的函数。

实用类

Tailwind 的实用类是 Tailwind 中最重要的部分,需要理解。下面是它们的工作原理,以及我将在书中如何谈论它们。

Tailwind 是由数以千计,甚至数以百万计的实用类组成的,其中大多数都是设置一个单一的 CSS 属性的值。例如,Tailwind 的 font-old 实用类是 CSS 属性 font-weight: 700 的别名。你可以在一个 HTML 元素中使用该工具,作为 class 属性的一部分,如 class="font-old"

Tailwind 潜在的实用类比你在一个项目中使用的要多得多,或者你想发送给你的浏览器。为了限制生成的 CSS,Tailwind 有一个命令行工具,可以根据你的代码生成一组实用的 CSS 类。此外,Tailwind 的配置文件让你对 Tailwind 寻找的模式和名称有更多的控制。除非我明确指出,在本书中,我将谈论最小配置所使用的默认类集,而在 第 8 章 “自定义 Tailwind”,我将谈论如何调整你所寻找的名称。

Tailwind 的实用类通常都有一个共同的开头或结尾模式的系列。当我谈到这些时,我会使用这样的语法:.text-{size},来表示一个包括 .text-xs.text-sm.text-xl 等的实用类家族。当使用这种语法时,只有在大括号中的部分不是空的情况下才需要破折号,所以你会使用 text-sm,但也可能只是 text

实用类名称中的变量部分不一定要在名称的最后。例如,在 Margin 大小的实用程序中,.m{方向}-{大小} 表示一个实用程序系列,如 .m-0.mt-10。正如您所看到的,实用工具的变量部分在 Tailwind 的不同部分通常是一致的。例如,Margin 实用类中的 {size}{direction} 选项是由 Padding 实用类和其他几个实用类系列共享的。

尽管 Tailwind 为诸如尺寸和颜色之类的东西提供了一套默认值,你也可以通过把它们放在方括号里来使用任意值。例如,如果你有一个一次性的边距,你可以使用 m-[104px]来表示 104 像素的边距,这并不是所提供的默认尺寸之一。一般来说,在你看到变量占位符的任何地方,你都可以使用方括号来插入一个任意的值。使用这些任意值是为了进行一次性的修复。如果你反复使用同一个任意值,你可能想把这个值添加到配置文件中,使其普遍可用,并保持设计的一致性。

如果你需要使用一个 Tailwind 不支持的 CSS 样式属性,比如 [mask-type:alpha],你甚至可以使用方括号来插入整个 CSS 样式属性。

Preflight

当你安装Tailwind时,你需要用命令导入三个不同的文件。@tailwind base, @tailwind components, 和 @tailwind utilities。这些文件中的每一个都包含了一套不同的 CSS 规则。(在某些安装中,你使用更通用的文件导入命令 @import 而不是 @tailwind

@tailwind base 包含了 Tailwind 的重置样式表,称为 Preflight。重置样式表是对所有基本的 HTML 元素进行重新造型,使之成为一套最小的造型属性。如果没有重置样式表,每个浏览器都会为如何呈现没有进一步CSS属性的单个 HTML 元素而定义自己的默认样式属性集。使用重置样式表可以让我们的应用程序控制这个基线,消除不同浏览器之间的差异,并提供一个更简单的背景,让我们插入自己的自定义样式。

你可以通过查看 node_modules/tailwindcss/dist/base.css 文件,看到 Tailwind 使用的全套重置样式。基本上,Preflight 做了几件事:

  • 它覆盖了标题的所有样式,因此,例如,h1 在视觉上与基本文本是一样的。
  • 它从 ulol 列表中移除样式,导致默认情况下没有 Bullet,在一个 Bullet 列表中提到这一点是很讽刺的。
  • 它将所有边框设置为 0 像素宽度、实心、默认定义的边框颜色。
  • 它给按钮一个默认的边框。
  • 它将图像和类似图像的对象设置为 display: block 而不是 display: inline,这意味着它们会将自己设置为独立的段落(就像它们是 div 标签),而不是 inline(就像它们是 span 标签)。

如果你只使用 Preflight 的样式,你会得到一个相当无聊的页面。但这就是问题的关键。使用 Preflight 可以确保对显示属性的任何改变都是由我们肯定地、明确地添加的。

@tailwind component 文件相当小,它只包括容器 CSS 类的定义,这些类通常用于页面的顶层,以定义整个页面被画在其中的盒子。我将在 第 5 章 “页面布局” 中进一步讨论这个问题。

大部分被认为是 Tailwind 的东西都在 @tailwind utilities 文件中,它定义了所有的实用类和它们的修改变体。我将用本书的大部分时间来描述这个文件的内容。

重复

当关注 Tailwind 和你经常需要完成设计目标的长组类目时,一个共同的担忧是如何管理重复。也就是说,如果你需要为每一个 h1 键入 class="text-6xl font-bold text-blue-700",就像我们在前言章节中所做的那样,每次需要 h1 的时候,这不是大量的键入需要一致吗?如果你的 h1 设计改变了怎么办?

管理代码重复

Tailwind 确实有办法管理 CSS 类列表的重复,但我们也鼓励你把重复问题看作是你更大的代码设置的一部分,而不仅仅是一个 CSS 问题。无论你用什么工具来构建你的 HTML 标记,它都可能有你已经在使用的组件或功能机制来减少代码的重复。在使用 Tailwind 时,将你的 CSS 类列表作为该代码的一部分,是个不错的主意。

例如,如果你在使用 React,你就有组件。许多其他客户端框架也提供组件。与其在 CSS 中管理重复,你可以用常见的 Tailwind 类创建 React 组件:

  1. export const Header = ({children}) => {
  2. return (
  3. <div className="text-6xl font-bold text-blue-700">
  4. {children}
  5. </div>
  6. )
  7. }
  8. export const SubHeader = ({children}) => {
  9. return (
  10. <div className="text-4xl font-semibold">
  11. {children}
  12. </div>
  13. )
  14. }
  15. export const SubSubHeader = ({children}) => {
  16. return (
  17. <div className="text-lg font-medium italic">
  18. {children}
  19. </div>
  20. )
  21. }

然后,您可以这样使用组件:

  1. <Header>Cool Text</Header>
  2. <SubHeader>Less Cool Text</SubHeader>
  3. <SubSubHeader>Kind of boring text</SubSubHeader>

在普通的 JavaScript 中,你也可以创建一个函数,返回 Tailwind 类的列表:

  1. const title = () => { return "text-6xl font-bold text-blue-700" }

而在 React 中,你会使用这个:

  1. <Component className={title}>Cool Text</Component>

使用 @apply 来处理重复

@apply 指令让你在其他 CSS 选择器的定义中使用 Tailwind 类。因此,我们可以在 CSS 中这样重新定义我们的标题类:

  1. @layer components {
  2. .title { @apply text-6xl font-bold }
  3. .subtitle { @apply text-4xl font-semibold }
  4. .subsubtitle { @apply text-lg font-medium italic }
  5. }

然后你就可以像其他的 CSS 类一样使用这些类:

  1. <div class="title">Title</div>

@layer 指令可以作用于是 basecomponent,也可以是 utilites。就浏览器而言,如果你使用 @layer,选择器就会被定义为你所声明的任何层的一部分,不管选择器的定义在你的 CSS 文件中实际位于何处。

使用 @layer 组件则将选择器定义为组件的一部分,而在实用类之前。这意味着如果你将我们自己的一个定义与 Tailwind 的一个实用类结合起来,这个实用类就会优先生效,这正是我们想要的。因此,我们可以定义,比如,一个额外的大标题,用:

  1. <div class="title text-5xl">Title</div>

要理解为什么 @layer 很重要,你需要知道 CSS 的一个一般原则:在其他条件相同的情况下,如果两个 CSS 类试图正确地调整相同的底层,最后定义的那个会赢。(如果你熟悉 CSS,你就会知道还有一个特殊性原则,即最特殊的定义获胜,但是因为所有的 Tailwind 工具都有相同的特殊性,这在这里就不是一个问题)。

在一个 CSS 文件中,如果你有两个定义相同的 CSS 选择器的定义,并且定义了相同的属性,那么文件中定义较晚的选择器获胜。在 Tailwind 中,如果你有两个定义相同属性的实用类,那么在列表中靠后的那个就会获胜,所以 class="text-xl text-2xl" 会给你尺寸为 2xl 的文本。

  1. @layer base {
  2. h1 { @apply text-4xl font-bold }
  3. h2 { @apply text-2xl font-semibold }
  4. h3 { @apply text-lg font-medium italic }
  5. }

在这里,我们直接重新定义 h1h2h3 元素,所以我们可以使用这个:

  1. <h1>Title</h1>

由于在基础层,这些定义在所有的实用工具之前,所以 <h1 class="text-6xl"> 的行为是你想要的,6xl 优先。如果 h1 被定义在实用工具层,那么 h1 会有优先权,因为它的定义晚于 text-6xl。而且,因为我们已经把图层移到了底层,Tailwind 将认为这是预检样式的一部分,并在任何组件之前定义它。同样,这个位置允许你按照预期混合标签、组件和实用程序。

这都是相当有用的,它允许你有效地使用 Tailwind 的工具作为构建块来建立你自己的框架。但是,你要意识到,你实际上是在建立一个框架,并承担所有相关的命名和维护责任。

修饰符

在我们讨论实用类之前,还有一个 Tailwind 的功能要谈:修饰符。

修饰符是 Tailwind 在 HTML 标记中使用 CSS 伪类、伪元素和媒体查询的方式。例如,当用户将鼠标拖到对象上时,通常希望对象以不同的方式显示,这对应于CSS 伪类 —— 悬停。

在 Tailwind 中,你可以通过向其他 Tailwind 实用类添加修饰符来定义 CSS 伪类方面的工具。如果你想让一个锚点标签在鼠标经过它时有一个下划线,你可以这样做:

  1. <a class="hover:underline">Click me</a>

这很紧凑,阅读起来相当直接,并与 HTML 一起定义,所以它在什么时候适用很清楚。你可以在任何 Tailwind 工具中使用 hover:。你甚至可以将 hover: 与一个任意的 CSS 样式一起使用,如 hover:[mask-type:luminance]。你还可以结合修改器:hover:dark:underline

正如你将在 第 7 章 “响应式设计” 中看到的,Tailwind 也使用修饰符,允许根据屏幕的宽度调用不同的工具,所以你可以写 class="sm:m-2 lg:m-4",当屏幕变宽时,你的元素会增长更大的边距。Tailwind 定义了 二十多个修改器,当你使用它们时,所产生的 CSS 会由 Tailwind CLI 自动生成。

你甚至可以将修改器与 @apply 结合起来使用。因此,@apply hover:underline 是定义一个新的 CSS 类的合法方式。

CSS 单位

CSS 中大多数定义长度或宽度的值都可以取一个带单位的数字。高度和宽度的定义也可以采用百分比。CSS 定义了两种单位:绝对和相对。

绝对单位是以现实世界的单位来定义的,所以你可以把宽度定义为,比如说,5 英寸(in)。更常见的是,你会看到 px 代表像素。在很久以前,一个 px 代表一个实际的显示像素,但现在电脑和手机的显示屏密度更高,一个 CSS 像素被定义为 1/96 英寸。(你知道的,就是常用的把一英寸分成 96 分之一)对于字体,你经常会看到点,如 font-size: 20pt。一个点是一英寸的 1/72,这是一种远早于计算机的测量方法。

在 CSS 中,你更有可能看到相对单位,其中最常见的是 em,它是指元素的大小,如 width: 10em。用相对术语来定义字体大小是很常见的,但是因为 font-size: 1.5em 是一个循环定义,为了排版属性的目的,em 指的是父元素的字体大小,而不是被匹配的元素。

如果这让人困惑的话 —— 确实如此 —— 它也是不稳定的,因为改变字体大小会对任何用 em 定义的东西产生奇怪的下游影响。一个更稳定的选择是 rem,它是根元素的字体大小,在 Tailwind 的重置系统中默认为 16 点。在 Tailwind 中,大多数距离要么被定义为百分比,要么是以 rem 为单位。

现在,让我们看看 Tailwind 为我们的字体造型需求提供了什么。