Moving away from Tailwind, and learning to structure my CSS

Julia Evans

告别 Tailwind,学习如何组织 CSS

原文由 Julia Evans 发布,订阅该博客

大家好!8 年前,我曾兴致勃勃地写过一篇关于发现 Tailwind 的文章

那时候我完全不知道该怎么组织自己的 CSS 代码,在一团乱麻和 Tailwind 之间做选择时,我非常乐意地选择了 Tailwind。它帮我做了好多小网站!

过去一周左右,我把几个网站从 Tailwind 迁移到了更语义化的 HTML 加原生 CSS,整个过程非常有趣、也非常有启发,所以想在这里分享一下我的收获!

和往常一样,我不是全职前端开发者,所以这些年来我对 CSS 的学习一直是断断续续的。

原来 Tailwind 教会了我很多

刚开始思考如何组织 CSS 时,我一开始还有点发怵:我本来就不太擅长组织 CSS!但后来我读了一些讲如何组织 CSS 的博文(比如A whole cascade of layersHow I write CSS in 2024),才意识到几件事:

  1. 每个 CSS 代码库里都有各种各样的东西(布局!字体!颜色!通用组件!)
  2. 为每一类东西都建立一套系统或规范非常有用,否则很快就会陷入混乱
  3. Tailwind 本身就为其中一些提供了系统,而我已经熟悉这些系统了!也许我可以直接模仿我喜欢的那些系统!

比如,Tailwind 就有:

接下来要聊的几个系统

接下来我会聊聊 CSS 代码库的几个方面,以及到目前为止我对每一部分想制定什么样的规则。有些是从 Tailwind 照搬来的,有些则不是。

  1. 重置
  2. 组件
  3. 颜色
  4. 字号
  5. 工具类
  6. 基础样式
  7. 间距
  8. 响应式设计
  9. 构建系统

1. 重置

我直接把 Tailwind 的“Preflight 样式”复制了过来,做法就是打开 tailwind.css,把前面大概 200 行抄下来。

我发现这么多年下来,我已经和 Tailwind 的 CSS 重置产生了依赖,比如 Tailwind 会给所有元素设置 box-sizing: border-box(这意味着元素的宽度包含了内边距):

* { box-sizing: border-box; }

我觉得如果不用这些来写 CSS,我会很不适应,而且我敢肯定,Tailwind 重置里还有很多东西(比如 html {line-height: 1.5;})是我已经在潜意识里习惯了、甚至没意识到它们存在的。

2. 组件

接下来的这部分是 CSS 的主体!

这里的思路是按“组件”来组织 CSS,在精神上和 Vue 或 React 的组件有点像。(虽然网站里可能根本没有任何 JavaScript)

基本想法是:

  1. 每个“组件”都有一个唯一的类名
  2. 一个组件的 CSS 永远不会覆盖另一个组件的 CSS
  3. 每个组件都有自己独立的 CSS 文件

这样一来,修改一个组件的 CSS 就不会莫名其妙地把另一个组件搞坏。而且我真正想改的 CSS 大概有 80% 都在各个组件文件里,所以当我在编辑一个 100 行的组件时,只需要考虑这 100 行。对我来说思考起来要轻松得多。

例如,这段 HTML 可能就是 .zine 这个“组件”。

<figure class="zine horizontal">
    <img src="whatever.jpg">
</figure>

而 CSS 用嵌套选择器大概长这样:

.zine {
  ...
  &.horizontal {
    ...
  }
  &.vertical {
    ...
  }
  &:hover {
    ...
  }
}

我并没有做什么程序上的保障(比如 Web Components 或@scope)来确保组件之间互不干扰,但仅仅是定下这个约定并尽量遵守,就已经感觉是很大的进步了。

接下来,是用来在全站保持一致性、让这些组件彼此协调的约定!

3. 颜色

colours.css 里有一堆像这样的变量,需要时直接用就行。颜色本身就很难处理,这次重构我也不想重新折腾配色,所以就原样保留了。

我在这里想强制执行的唯一规范是:网站里用到的所有颜色都必须在这个文件里列出来。

:root {
  --pink: #fea0c2;
  --pink-light: #F9B9B9;
  --red: #f91a55;
  --orange: rgb(222, 117, 31);
  ...
}

4. 字号

我很喜欢 Tailwind 的一点是,如果我想设置字号,只需要想“嗯,我想让文字大一点”,然后写个 text-lg 就完事了!如果还不够大,就换成 xl2xl。完全不用去记到底该用 empx 还是 rem

所以我照搬 Tailwind 定义了一堆变量,像这样:

  --size-xs: 0.75rem;
  --line-height-xs: 1rem;

  --size-sm: 0.875rem;
  --line-height-sm: 1.25rem;

然后如果我想设置字号,就可以这么写。虽然比 Tailwind 啰嗦一点,但目前我挺满意的。

h3 {
  font-size: var(--size-lg);
  line-height: var(--line-height-lg);
}

5. 工具类

有些东西比如按钮,会出现在很多不同的组件里。我把它们叫做“工具类”。

我从 Tailwind 复制了一些工具类(比如 .sr-only,用于只对屏幕阅读器用户可见的内容)。

这一部分很小,我会尽量谨慎地在这里做改动。

6. 基础样式

“基础”样式是我自己定的、作用于全站的样式。这一部分我必须保持得非常精简,因为我还没自信到能在全站强制推行很多样式。目前我觉得能接受的只有这两条,而且 <section> 那条我可能还会改:

/* put a 950px column in the middle of each <section> */
section {
  --inner-width: 950px;
  padding: 3rem max(1rem, (100% - var(--inner-width))/2);
}

a {
  color: var(--orange);
}

我觉得对于基础样式,对我来说最容易的方式是自下而上——先让基础样式里几乎什么都没有,然后随着发现一些通用的需求,再把样式从组件里慢慢移到基础样式中。

7. 间距

对于如何管理内边距和外边距,我还没完全想好一套方法。不过我肯定想比以前用 Tailwind 时更有原则一些——那时我只是随手到处加内边距和外边距,直到看起来顺眼为止。

现在我的方向是尽量让外层的布局组件来负责间距。比如,如果我有一个 <section>,里面有一堆子元素想让它们之间保持间距,我可能会用这种方式来均匀地分隔子元素:

section > *+* {
  margin-top: 1rem;
}

一些启发我的博文:

8. 响应式设计:多用 Grid!

我以前在 Tailwind 里做响应式设计的方式是用大量的媒体查询。Tailwind 有一种 md:text-xl 的写法,意思是“在 md 及以上尺寸应用 text-xl 样式”。

现在我尝试了一种很不一样的做法,就是做出更灵活的 CSS Grid 布局,从而不需要那么多断点。这很难,但学习 Grid 能做到什么真的很有意思,而且这也是一个我觉得用 Tailwind 很难实现的好例子。

比如,我最近在学怎么用 auto-fit 来自动实现在大屏幕上显示 2 列、小屏幕上显示 1 列,像这样:

  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
  justify-content: center;

我还大量使用了grid-template-areas,这是个非常棒的功能,我觉得用 Tailwind 是实现不了的。

一些灵感来源:

9. 构建系统:esbuild

在开发环境中,我不需要构建系统:现在的 CSS 已经内置了导入语句,像这样:

@import "reset.css";
@import "typography.css";
@import "colors.css";

以及内置的嵌套选择器,像这样:

.page {
  h2 { ...}
}

如果需要,我可以用 esbuild 来打包生产环境的 CSS 文件,大概是这样。

esbuild style.css --bundle --loader:.svg=dataurl  --loader:.woff2=file --outfile=/tmp/out.css

虽然我平时会尽量避免使用 CSS 和 JS 的构建系统,但我不介意用 esbuild(我在 2021 年这里写过),因为它是基于 Web 标准的,而且是一个 Go 语言编译的静态二进制文件。

为什么要弃用 Tailwind?

有几个人问我为什么要弃用 Tailwind。有几个原因:

  • 自 2018 年以来,Tailwind 对构建系统的依赖越来越重,我觉得不用构建系统几乎(?)不可能使用新版本的 Tailwind。所以这些年我一直用的是 Tailwind v2。(据说还有一个 litewind
  • 本来就一直推荐配合构建系统使用 Tailwind,但我从来没真正这么做过,所以很多项目里都放着 2.8MB 的 tailwind.min.css 文件(gzip 后 270K),感觉有点傻。
  • 我现在的 CSS 水平比刚开始用 Tailwind 时好多了
  • 说到底 Tailwind 是有局限的:如果你想在 CSS 里做些奇怪的操作,用 Tailwind 不一定能实现。这些限制有时候非常有用(这篇文章的很多内容就是我在重新实现 Tailwind 的一些限制!),但到现在这个阶段,我更想能自己取舍。
  • 最后我的网站里同一个项目混用了原生 CSS 和 Tailwind,维护起来一点也不好玩
  • 我也开始好奇,写更语义化的 HTML 会是什么感觉。

我感到好奇的 CSS 特性

在做这些的过程中,我了解到很多我还没用过、但以后想学一学的 CSS 特性:

我弃用 Tailwind 的最后一个原因

在这篇文章里我说了很多从 Tailwind 中学到的东西,这些都是真的。

但 3 年前我读过一篇叫Tailwind and the Femininity of CSS 的文章,一直让我印象深刻。说实话,我一开始对 CSS 的态度可能就有点像那篇文章里描述的那样:

他们听说它很简单,所以就以为它很容易。但当他们尝试使用时,却行不通。那一定是这门语言的错,因为他们知道自己很聪明,而这本该是很容易的。

但在过去的 10 年里,我真正学会了热爱并尊重 CSS 这门技术。

所以几年前我就决定,与其贬低 CSS,不如通过把 CSS 学得更好、认真对待这门技术来回应“CSS 很难”这种说法。这样做彻底改变了我:我发现曾经让我抓狂的许多问题(比如“居中是不可能的”)其实早就在 CSS 里解决了,而且“居中”本身的含义也并不总是那么明确,有多种实现方式是很合理的。CSS 很难,是因为它在解决一个难题!

过去 10 到 15 年里新增的 CSS 特性(其中一些我在这篇文章里已经聊过!)以及它们如何让 CSS 变得更好用,一直让我惊叹不已,而花时间提升自己的 CSS 技能也是一段非常酷的体验。

而那篇文章让我觉得,Tailwind 在一定程度上助长了对 CSS 专业能力的轻视,而我不想成为其中的一部分,即使 Tailwind 对我个人来说曾是一个有用的工具。尤其是在大语言模型的时代,重视人类的专业能力比以往任何时候都更重要。

另一篇影响了我的、批评 Tailwind 的博文:

就说到这里!

感谢Melody Starling,她最初为wizardzines.com 设计并编写了 CSS,网站上所有酷炫有趣的东西都要归功于 Melody。

在做这个的过程中,我读了大量关于 CSS 的精彩博文(来自CSS TricksSmashing Magazine 等),我在本文中已经尽量附上了其中一些链接,非常感谢 CSS 社区的朋友们如此慷慨地分享他们的实践。

本文章由 muse-spark-1.2-contributor 进行翻译

评论