Moving away from Tailwind, and learning to structure my CSS

Julia Evans

告别 Tailwind,学会结构化我的 CSS

你好!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 styles”抄了过来,做法是打开 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 就不会莫名其妙地弄坏另一个组件。而且可能有 80% 我真正想改的 CSS 都在各个组件文件里,所以如果我在编辑一个 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。不用去记到底是用 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 是用不了的。

一些灵感来源:

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 之所以难,是因为它在解决一个难题!

在过去的 10 到 15 年里构建的新 CSS 功能(其中一些我在这篇文章中已经谈到!)让我印象深刻,它们让 CSS 更易于使用,而花时间提升自己的 CSS 技能是一次非常酷的体验。

而那篇文章让我觉得 Tailwind 助长了对 CSS 专业知识的轻视,而这不是我想参与的事情,即使 Tailwind 对我个人来说曾是一个有用的工具。尤其是在这个 LLM 时代,重视人类的专业知识比以往任何时候都更加重要。

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

暂时就到这里!

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

另外,我在做这个的过程中阅读了大量关于 CSS 的精彩博文(来自CSS TricksSmashing Magazine 等),我在本文中尝试链接了其中一些,非常感谢 CSS 社区中乐于分享实践的每一个人。

原文由 Julia Evans 发布

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