告别 Tailwind,学习如何组织 CSS
原文由 Julia Evans 于 发布,订阅该博客
大家好!8 年前,我曾兴致勃勃地写过一篇关于发现 Tailwind 的文章。
那时候我完全不知道该怎么组织自己的 CSS 代码,在一团乱麻和 Tailwind 之间做选择时,我非常乐意地选择了 Tailwind。它帮我做了好多小网站!
过去一周左右,我把几个网站从 Tailwind 迁移到了更语义化的 HTML 加原生 CSS,整个过程非常有趣、也非常有启发,所以想在这里分享一下我的收获!
和往常一样,我不是全职前端开发者,所以这些年来我对 CSS 的学习一直是断断续续的。
原来 Tailwind 教会了我很多
刚开始思考如何组织 CSS 时,我一开始还有点发怵:我本来就不太擅长组织 CSS!但后来我读了一些讲如何组织 CSS 的博文(比如A whole cascade of layers 或How I write CSS in 2024),才意识到几件事:
- 每个 CSS 代码库里都有各种各样的东西(布局!字体!颜色!通用组件!)
- 为每一类东西都建立一套系统或规范非常有用,否则很快就会陷入混乱
- Tailwind 本身就为其中一些提供了系统,而我已经熟悉这些系统了!也许我可以直接模仿我喜欢的那些系统!
比如,Tailwind 就有:
接下来要聊的几个系统
接下来我会聊聊 CSS 代码库的几个方面,以及到目前为止我对每一部分想制定什么样的规则。有些是从 Tailwind 照搬来的,有些则不是。
- 重置
- 组件
- 颜色
- 字号
- 工具类
- 基础样式
- 间距
- 响应式设计
- 构建系统
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)
基本想法是:
- 每个“组件”都有一个唯一的类名
- 一个组件的 CSS 永远不会覆盖另一个组件的 CSS
- 每个组件都有自己独立的 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 就完事了!如果还不够大,就换成 xl 或 2xl。完全不用去记到底该用 em、px 还是 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 是实现不了的。
一些灵感来源:
- 无需媒体查询的响应式网格布局 来自 CSS Tricks
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 Tricks、Smashing Magazine 等),我在本文中已经尽量附上了其中一些链接,非常感谢 CSS 社区的朋友们如此慷慨地分享他们的实践。
随机一篇博客
评论
登录后参与讨论