美国制造业的超级周期要来了!

青年就业难,可能低估了

《簡帛》 | 袁開慧、趙懷舟:老官山漢墓醫簡《醫馬書》簡27字詞考釋

云南永德芒果节开幕式:以节为媒 迎八方来客共享盛宴

曾建斌案的罪与罚

生成图片,分享到微信朋友圈

自由微信安卓APP发布,立即下载! | 提交文章网址
查看原文

【第2631期】浅谈 Atomic CSS 的发展背景与 Tailwind CSS

huli 前端早读课 2022-06-02

前言

最近对岸对这个Atomic CSS的话题讨论蛮多的。今日前端早读课文章由@huli授权分享。

正文从这开始~~

Atomic CSS 这个词由 Thierry Koblentz 所提出,最早出自于这篇 2013 年发表的必读的经典:。

那什么是 Atomic CSS?这边直接取用 Let’s Define Exactly What Atomic CSS is 这篇文章给的定义:

Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.

例如说像这样子的东西就是 Atomic CSS:

.bg-blue {background-color: #357edd; }
.margin-0 { margin: 0; }

而 Tailwind CSS 就是实现了 Atomic CSS 这个概念的一个 CSS 框架。

在 2019 年的时候我也写过一篇在讲 Atomic CSS 的文章,但那时用的是另外一个同义词叫做 Functional CSS:邪魔歪道还是苦口良药?Functional CSS 经验分享,在那篇里面已经有提到一些这篇想讲的东西,但我觉得还不够完整,因此才又写了一篇。

在这篇文章中,我希望跟大家一起读一下这些经典文章,因为你会发现有些争论的点可能早在八九年前就已经被提出、讨论或甚至是解决了。接著就可以来看看最早的 Atomic CSS 跟现在的 Tailwind CSS 的差别在哪,优缺点又是什么?

大纲如下:

  • Atomic CSS 的诞生背景

  • Atomic CSS 到底想解决什么问题?

  • 针对 Atomic CSS 的问题与反驳

  • 我对 Atomic CSS 的看法

  • Tailwind CSS 改良了哪些部分?

  • 结语

Atomic CSS 的诞生背景

如开头所述, Atomic CSS 一词出自于 Yahoo ! 的工程师 Thierry Koblentz(以下简称 TK)在 2013 年所发表的 Challenging CSS Best Practices。

在看这篇文章之前,我们可以先看一下 2022 年 2 月的这篇专访:The Making of Atomic CSS: An Interview With Thierry Koblentz,在这篇里面提及了更多 Atomic CSS 出现的背景以及早期在 Yahoo ! 内部的应用。

根据文章中的说法,有天他的主管来问他有没有一种可以不改到 stylesheet 但还是可以动到页面的方法,因为他想避免把东西改坏。

于是 TK 就做了一个“utility-sheet”,让工程师可以在不改到 stylesheet 的状况下依然能改到前端的样式。听起来这个 utility-sheet 应该就是一个静态的 CSS 档案,然后里面有着各种 utility class。

接着过了几年,一个工程主管问他是否能“全部都用 utility class”来改写 Yahoo ! 的首页,在当时可以说是先驱中的先驱了。

最后他们写了一个纯静态( static )的 CSS 并取名为 Stencil 来完成这件事(这边会讲到纯静态是为了跟等一下会出现的东西来做对比),并且从中发现了很多这样子使用的好处。

这套纯静态的 CSS 的特色之一是可以强迫遵从一些 design style,例如说只写了 margin-left-1、margin-left-2、margin-left-3 之类的 class,然后每一个对应到的是 x4,因此你的 margin 就只有 4px、8px 跟 12px 这些 4 的倍数可以用,利用这个来强迫设计遵循既有的规则。

不过后来他们发现这套系统行不通。因为在现实世界中,每个 design team 都有自己不同的要求,他们想要的 padding、margin、字体、颜色全部都不同,所以静态是不行的,需要定制化,需要动态产生。

于是 Atomizer 就诞生了,一个帮你产生相对应 CSS 文档的工具。

例如说你有个页面写了:

<div class="D(b) Va(t) Fz(20px)">Hello World!</div>

Atomizer 就会自动帮你产生底下的 CSS 出来:

.D(b) {
display: block;
}

.Va(t) {
vertical-align: top;
}

.Fz(20px) {
font-size: 20px;
}

如此一来,工程师们就可以有更大的弹性去符合设计的需求。

上面看到的这些语法叫做 ACSS,其实功能基本上跟现在的 Tailwind CSS 已经满类似的了,只是使用的语法不太一样而已。这套 ACSS 系统的命名规则的灵感是来自于 Emmet,一个可以利用语法帮你快速构建 HTML 的套件,而 class name 中的 () 的灵感则是来自于函式呼叫。

接著 TK 谈到了在像是 Yahoo ! 这种大型企业写 CSS 跟其他地方有什么不同,你会面临到的状况超级复杂,包括跨国跨时区的沟通、分布各地的团队成员、几百个共用的 component、l10n 跟 i18n、一堆 legacy code,以及一堆的办公室政治。

在需要维护一个超级复杂的项目的状况之下,他开始反思一些常见做法(common practice)是否真的能带来益处,最后却发现有些概念除了没有带来益处以外,甚至是有害的。

在复杂的项目之中,有很多你可能没想过的状况会发生,所以维护变得很艰难,必须要小心翼翼去避开一些陷阱。

另外,在内部推广 ACSS 的旅程刚开始并不顺利,看起来有许多 team 都对那样的语法却步(我猜就如同我在之前的文章写的一样,一开始看到都会觉得这是什么邪魔歪道),但是 ACSS 带来的好处反映在数据上面,采用了 ACSS 的项目少了大约 36% 的 CSS 与 HTML 的大小,因此至今依然有许多专案还用着 ACSS。

如果你把 A 网页的 HTML 复制,贴上到 B 网页去,你会发现 UI 完全没变,使用了 ACSS 之后不会因为你在别的页面就有不同的样式,这就是 ACSS 所带来的好处,原文是这样写的:

This is because ACSS makes these components page agnostic .

“page agnostic ”是我觉得很重要的一个性质,这个之后会再提到。

原文的专访还有提到更多故事背景跟挑战,不过在这边我就不继续再提了,有兴趣的读者们可以去看原文。而之前 TechBridge 的好伙伴 Arvin 以前在 Yahoo ! 待过,在公司内有写过 ACSS,他在 2017 年的时候有写过一篇文章,也很值得一看:浅谈 CSS 方法论与 Atomic CSS。

这篇专访中其实对于 Atomic CSS 想解决的问题并没有到这么多的着墨,不过从中可以看见 TK 在工作上需要维护大型的项目,因此自然也会碰到许多痛点,不难想像出 Atomic CSS 诞生的背景也与此有关。

想知道 Atomic CSS 要解决什么问题,就要来看经典之作了。

Atomic CSS 到底想解决什么问题?

底下我会引用许多来自于 Challenging CSS Best Practices 的内容,因为原文写得很清楚。如果英文阅读能力 ok 的话,也强烈推荐读者们自己看过一遍。

在文章开头的 quick summary,就有这样一段:

When it comes to CSS, I believe that the sacred principle of “separation of concerns” (SoC) has lead us to accept bloat , obsolescence , redundancy , poor caching and more. Now, I’m convinced that the only way to improve how we author style sheets is by moving away from this principle.

大家都知道在写网页的时候要注重关注点分离(separation of concerns),让 HTML 做好它的事情,只关注内容,而 CSS 只关注样式,两者透过 class name 连结在一起。可是作者发现这样子的概念,其实会带来许多负面影响,因此这篇文章就是来说服大家不要再把这种做法奉为信条,如果有更好的路,那干嘛执着在这呢?

接着文中举了一个简单的例子,称之为 media object,HTML 长这样:

<div class="media">
<a href="https://twitter.com/thierrykoblentz" class="img">
<img src="thierry.jpg" alt="me" width="40" />
</a>
<div class="bd">
@thierrykoblentz 14 minutes ago
</div>
</div>

CSS 长这样:

media {
margin: 10px;
}

.media,
.bd {
overflow: hidden;
_overflow: visible;
zoom: 1;
}

.media .img {
float: left;
margin-right: 10px;
}

.media .img img {
display: block;
}

最后呈现出来的结果如下:

接着第一个需求来了,有些地方需要把图片显示在右边而不是左边,于是我们可以在 HTML 的元素上增加新的 class imgExt,并且新增底下的 CSS:

.media .imgExt {
float: right;
margin-left: 10px;
}

然后第二个需求来了,当这一块内容出现在某个右侧区块(原文为 right rail)时,文字要变小。于是我们可以包一个 div 在外面,像这样:

<div id="rightRail">
<div class="media">
<a href="https://twitter.com/thierrykoblentz" class="img">
<img src="thierry.jpg" alt="me" width="40" />
</a>
<div class="bd">
@thierrykoblentz 14 minutes ago
</div>
</div>
</div>

然后针对这个 #rightRail 去调整样式,调整完的全部样式如下:

media {
margin: 10px;
}

.media,
.bd {
overflow: hidden;
_overflow: visible;
zoom: 1;
}

.media .img {
float: left;
margin-right: 10px;
}

.media .img img {
display: block;
}


.media .imgExt {
float: right;
margin-left: 10px;
}


#rightRail .bd {
font-size: smaller;
}

这些调整样式的方法应该都蛮直观的,但作者点出其实有几个问题:

  • 每次 UI 要支持不同的样子,就要新增一个 CSS rule

  • .media 跟 .bg 共用同样的样式,如果还有别的要共用,CSS selector (选择) 就会越来越多,越来越大

  • 在最后的六个 CSS selector (选择) 中,有四个是基于 context 的,不好维护也不好重用

  • RTL(Right To Left)跟 LTR(Left To Right)会变得很复杂

第一点其实乍看之下蛮正常,你要在不同状况支持不同的样子,不就一定要写新的 CSS 规则吗?但作者却说有更好的方法来处理,不一定要新增。

第二点其实看起来也蛮正常,要共用 style 的话,写成 .media.bg 不是很常见吗?文档大的话也是必然的吧?

第三点的话这个 context 是个很重要的概念,例如说我们最后这个规则:#rightRail .bd,让在 #rightRail 底下的 .bd 改变字体大小,有不同的样式。

所以我们的 media object 会根据 context(是否在 #rightRail) 底下有不同的样式,就写了不同的 CSS 规则去处理。

一旦让你的 CSS 规则跟 context 有关,在大型项目中维护就会变得困难。

举例来说,如果有人手贱去改了 rightRail 这个 id,想说改成 blockRightRail 会更好,那你的样式就坏了。你可能会质疑说:“不对啊,这是他的错啊,他要改的话应该就要确认其他地方不会坏”,有改过的人都知道,要确认其他地方有没有坏,是多么困难的一件事情,更何况是在大型项目之中。很可能你改 A 的时候,根本不会预期 B 会坏掉,因为你根本不知道他们有关。

或如果别的 team 想把你这个 media object 拿去用,于是就连同 CSS 一起复制贴上到他们项目,可是却发现他们的 id 并没有 rigthRails,那就要去改动 style。

第四点的话也是只有在 Yahoo (雅虎) ! 这种大型公司才比较会做到的事情(至少我是没做过),就是在做 l10n 的时候会有很多细节要考虑,例如说有些国家的阅读方向是左到右,有些是右到左。

上面的 case 如果要改变方向,就要加上这两个规则:

.rtl .media .img {
margin-right: auto; /* reset */
float: right;
margin-left: 10px;
}

.rtl .media .imgExt {
margin-left: auto; /* reset */
float: left;
margin-right: 10px;
}

接着,作者就提出了 Atomic (原子) CSS 的概念,然后以 Atomic (原子) CSS 来改写,并告诉你这样改的好处在哪,HTML 跟 CSS 如下:

<div class="Bfc M-10">
<a href="https://twitter.com/thierrykoblentz" class="Fl-start Mend-10">
<img src="thierry.jpg" alt="me" width="40" />
</a>
<div class="Bfc Fz-s">
@thierrykoblentz 14 minutes ago
</div>
</div>
.Bfc {
overflow: hidden;
zoom: 1;
}

.M-10 {
margin: 10px;
}

.Fl-start {
float: left;
}

.Mend-10 {
margin-right: 10px;
}

.Fz-s {
font-size: smaller;
}

针对第一点的问题,还记得一开始的新需求吗?现在我们不需要新增一个 CSS 规则,只需要在 HTML 加上 class="Fl-sart Mend-10",就可以改变 UI 的样式,但是没有新增任何规则。

第二点,现在所有需要 overflow:hidden 跟 zoom:1 的元素,我都只要用一个 class name 叫做 .Bfc就可以搞定了,无论有多少个元素要用,我都只有一个 CSS selector (选择) 。

第三点,现在的 class name 已经跟 context 无关了,我上面讲的问题完全不会发生。今天我样式要变,我可以很安心地把 class name 删掉,因为我知道其他地方绝对不会坏掉。这就是开头第一段所讲的“page agnostic (不可知论者) ”,没有 context 的 class name 才能做到容易删改,而且可以搬来搬去还能保证相同样式。

换句话说,它解决的是 scope 的问题,就如同原文所说:

I believe that this approach is a game-changer because it narrows the scope dramatically. We are styling not in the global scope (the style sheet), but at the module (模块) and block level. We can change the style of a module (模块) without worrying about breaking something else on the page.

最后关于刚刚第四点的方向问题,已经透过 class name 抽象化了,如果要改方向的话,只需要把 CSS 改成这样即可:

.Fl-start {
float: right;
}

.Mend-10 {
margin-left: 10px;
}

透过改写成 Atomic (原子) CSS,我们成功解掉了传统 CSS 写法上会碰到的几个问题,而且具有以下优点:

  • CSS 大小是线性成长的,重复的规则都会用到同一个 class name,因此档案大小大幅降低

  • 很容易可以支援 RTL 跟 LTR

  • class name 变得与 context 无关,scope 也变小,因此更好维护也更好改动

其中我认为最重要的是第三点,这也是我支持 Atomic (原子) CSS 的原因。

在改样式的时候,你可以直接把 class name 删掉而不用怕影响到其他的元素,这是多么美好的一件事情,你再也不用担心改 A 坏 B,因为 class name 都跟 context 无关了。

Tailwind CSS 的作者以前有写过一篇文章,对于 Atomic (原子) CSS 如何解决传统 CSS 的问题有更多著墨跟范例,如果上面的理由无法说服你,可以看看这篇文章:CSS Utility Classes and “Separation of Concerns”。

总之呢,在原文中 TK 也预期到了仅管这个做法能解决问题,但读者一定会有一堆疑惑,所以准备来一一击破。

针对 Atomic (原子) CSS 的问题与反驳

关于底下的问题跟反驳,除了文章以外,我可能还会引用到这三处的资料:

  • ACSS FAQ

  • HTML5DevConf: Renato Iwashima, “ Atomic (原子) Cascading (级联) Style Sheets”

  • Thierry Koblentz 在 FED (美联储) London 2015 的简报

1. 你的 class name 没有语义,这样不行啊,规格不是这样写的

关于 semantic (语义) 的问题,在 2012 时也有一篇文章讨论过这件事情:About HTML semantics (语义) and front-end architecture,在 HTML spec (规格) 里面确实有这个段落:

There are no additional restrictions on the tokens (令牌) authors can use in the class attribute, but authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content.

如果这个元素是个 image,那你的 class name 应该取叫 image,而不是取叫它的样式例如说:display-block width-[150px] margin-3 之类的。

而上面引的那篇文章提到说其实在维护大型项目时,这样的命名策略反而会变成一种阻碍,我们根本没理由一定要照着这个做,因为:

  • 跟 content 有关的语义你看 HTML 就看得出来了

  • 除了一个叫做 Microformats 的标准以外,class name 对机器跟一般访客来说没什么太大的意义

  • 我们会用 class name,只是因为要跟 JS 或是 CSS 结合在一起。你想想,如果一个网站不需要 style 也不需要 JS,是不是就不会取 class name 了?那这样你的网站有比较不 semantic (语义) 吗?

  • 对开发者而言,class name 应该包含一些更有用的资讯

接著他举了一个例子:

<div class="news">
<h2>News</h2>
[news content]
</div>

你看内容就知道这个区块是来呈现 news 的,根本不需要 class name 也行。

这让我想到当初 JSX 的发展,也是直接破坏掉了以往 JavaScript 跟 HTML 应该要分开的 best practice。

如果大家都执着于前人订下的规范,当作信条一样遵从,而不去反思这个信条存在的理由,就不会有这么多革新的东西出现。

就如同 Challenging CSS Best Practices 一文中在最后提到的:

Tools, not rules. We all need to be open to new learnings, new approaches, new best practices and we need to be able to share them.

2. 你的 class name 太难懂了,看不懂,可读性很差

直接截一张 FED (美联储) London 2015 里的简报图,他们说 ACSS 的语法参考自 Emmet,可读性其实不会差:


不过这个解释我不是很买单就是了,因为对于一个没用过 Emmet 的人来说,看起来真的不太好懂,要花一段时间去熟悉那些缩写。

3. 你这跟 inline style 有什么不同?

其实本质上是一样的,都是把 style 限制在很小的 scope 里面,但 Atomic (原子) CSS 解决了 inline style 的几个坏处:

  • CSS 的优先顺序很高,很难盖过去

  • 很冗长

  • 不支援 pseudo-class 或是 pseudo-element

底下截一张官网的图:


Atomic CSS 保留了 inline style 的好处,也就是 scope 很小,同时也解决了上面提到的那些坏处。

4. 你说可以降低 CSS 大小,但 HTML 大小不是也会上升吗?那只是把成本转到别的地方而已

在原本 ACSS 的写法下,其实 class name 的长度不会比本来大多少。

举例来说,原本叫做 profile__image-background,改写之后可能是 D-ib Bgc(#ff0010) 之类的。根据他们自己做的统计,Yahoo! 自己的网站平均的 class name 长度是 22,而其他没有用 ACSS 写法的 Twitter 平均是 28,USA today 是 38,卫报网站是 36,只有特别对 class name 做了 uglify 的 Facebook 是 18,些微胜出而已。

而且,除了 class name 并没有明显变长以外,ACSS 还有一个好处是重复字元很多,所以 gzip 的压缩率会比较高。官网有给了一个数据是说他们自己经过测试后,semantic classes 可以降 35% 大小,而 ACSS 可以降 48%。

5. 那共用元件像是 button 该怎么办?难道我要每个地方都改样式?

在 Challenging CSS Best Practices 一文中有一个段落是在讲这个:

The technique I’m discussing here is not about banning “semantic” class names or rules that group many declarations. The idea is to reevaluate the benefits of the common approach, rather than adopting it as the de facto technique for styling Web pages. In other words, we are restricting the “component” approach to the few cases in which it makes the most sense.

Atomic CSS 的出现并没有要完全取代传统 semantic 的做法,正确的做法应该是哪个适合就用哪个。

而官网 FAQ 也有特别提到类似的事情:

If changing some styling requires you to edit multiple files, then you should use the classic CSS approach

举例来说,你的程序里面有个按钮会一直重复出现,这时候如果每次都复制贴上 HTML,要改 class 的时候就要每个文件都改,显然是不合理。

在这种状况下,用传统的做法当然会是更好的。

我对 Atomic CSS 的看法

前面看完了这么多经典教材,来讲一下我自己的想法。

首先,我认为 Atomic CSS 带来了两个独特的好处:

  • 降低 CSS 文件大小

  • 最大程度缩减 scope,让维护变得容易

第一点是显而易见的,这个应该就不用再多提了,CSS 档案大小会小很多,这一点是其他 CSS 的解决方案没办法做到的。

第二点就如同我之前所讲的,你改一个元素的 class name,保证只会改到这个元素本身,而不会动到其他地方,这是我认为 Atomic CSS 带来的最大的好处,让 style 变成 local scope。

有些人可能会疑惑说:“但你讲的这个,CSS-in-JS 或是 CSS modules 也都做得到啊”,没错,这两个解决方案也可以解决 scope 的问题。但 Atomic CSS 刚诞生的时候似乎这些解决方案都还不存在(或是还在非常早期),所以这里比较的对象是传统 CSS 的解决方案(像是 OOCSS、BEM、SMACSS 这些管理 CSS 的方法)。

除了优点以外,我也认为 Atomic CSS 有一些缺点以及不适合使用的地方:

  • class name 很长,直接看 HTML 的话不好阅读

  • 如果没办法做到 component 化,那就不适合使用 Atomic CSS

  • 需要花一段时间上手 Atomic CSS 的语法以及熟悉各种缩写

三大框架的流行导致了现在的前端工程师普遍都会以 component 的思考方式去开发,而非传统的 HTML 就管理内容、JavaScript 就管理程序、CSS 就管理样式。以组件的方式去思考后,三者会合在一起,变成一个独立封装的组件。

而组件化之后,前两项问题也就迎刃而解了。

第一点的话开发时我们都是去看 component 的文档,不会直接去看到 HTML,而看到 component 时根据它的命名我们也会知道它在干嘛,不需要 class name 做为辅助。

第二点的话因为都变成 component,所以可以保证要改动的时候只要改一个地方就好。

而第三点是我认为要导入 Atomic CSS 相关工具需要付出的成本,那就是熟悉新的语法跟各种缩写,这个是避免不掉的。但是对我来说,这样的成本不高,学习曲线也不高,顶多就是刚开始入门时要一直查表。比起它带来的效益,成本其实已经很小了。

最后提一下 CSS-in-JS 跟 CSS modules 这两个方案,一样都解决了 scope 的问题,但跟 Atomic CSS 比起来有两样是做不到的。

第一点是 CSS-in-JS 跟 CSS modules 这两个方案据我所知,应该都需要搭配一些前端的 library 或是框架来使用,例如说 React 或 Vue,但 Atomic CSS 不需要。举例来说,假设今天一个专案它的 component 是在后端用 template engine 达成的,而非在前端,那就没办法用这两套解决方案。

第二点是 CSS 大小,没办法跟 Atomic CSS 一样这么小。

不过关于这点,可以参考 Facebook 提出的 Atomic CSS-in-JS 方案,让你写起来像 CSS-in-JS,可以用传统 CSS 语法,但实际上产生时却帮你用 Atomic CSS 的方式来产生,巧妙地融合了两者的优点,是满值得关注的一项技术。

Tailwind CSS 改良了哪些部分?

上面谈到了这么多 Atomic CSS 的东西,最后我们简单来看一下 Tailwind CSS,比起一开始 Yahoo! 创造的 Atomizer,它有哪些优势?

其实在功能面上我觉得没有相差太多,最大的优势是我认为它的 DX(Developer Experience)更为突出,例如说它使用了更好看懂的 class name,文件也更加完整,很快就可以查到什么语法应该怎样写:


不过事实上,我认为这些基于 Atomic CSS 的框架最佳化的方向都是类似的,都是针对 DX 的方向去做改善。

例如说 Windi CSS 就带来很多语法上的改善以及新的用法,而 UnoCSS 以及 master CSS 也都有各自不同的做法,来增加开发者的体验或是加快编译的效率。

至于这些最佳化的细节我就不熟了,详情可以参考重新构想原子化 CSS 这篇文章。

我对 Tailwind CSS 也没有到很熟,这边补充一个需要注意的地方,那就是 Tailwind CSS 是去扫你的 source code 字串有哪些符合特定格式,所以如果你的 class name 是用动态产生的,就会抓不到,像这样:

// 这样写抓不到
<div class="text-
{{ error ? 'red' : 'green' }}-600"></div>

// 这样写才抓得到
<div class="
{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

不确定其他的 library 有没有把这问题解掉,但我自己是觉得没有太大的关系,因为这种动态产生的方式能避免就尽量避免会比较好。

为什么我会这样说呢?

分享一个小故事,以前我在维护一个用 Redux 的项目时有一系列操作长很像,例如说 post、user 跟 restaurant 的 CRUD 之类的,代码有很大一部分都重复,因此我就写了一个 utils 来处理共同逻辑,只要写 generateActions('user'),就自动帮你动态产生出 readUser 与 createUser 之类的 actions。

那时我想说这样很赞,但同事提醒我说如果你这样做,那全局搜索 readUser 的时候就搜不到东西,因为那是程序动态产生的,在原始代码里面找不到。

虽然我那时不觉得有什么,但过了两个月后我知道我错了。当你面对一个不熟悉的项目时,要去修一个 bug,最常做的就是拿你手上的信息去搜索原始代码,看看出现在哪边。如果你搜不到东西,那是满挫折的一件事情,会需要花更多时间去找问题到底在哪个范围。因此,可以被搜寻到是很重要的。

或是再举一个例子,假设今天设计师突然改变心意,说所有之前用 text-red-600 的地方应该要改成 text-red-500,但新的地方还是会用到 text-red-600,所以我们不能直接改设定档的色码,一定要去改 source code,把 text-red-600 都换成 text-red-500。此时你会怎么做?全局搜索然后取代,搞定。

此时,像上面那种动态产生 class name 的 case 除非你特别记得,否则就不会被改到。因为搜索不到,所以你也不知道那边其实会出现 text-red-600。如果真的要用动态产生,那至少加个注释标注一下会用到的东西的全名,让它能被搜寻到。

结语

“每样工具都有它适合的地方”这句话大家都知道,但重点是“那它到底适合什么地方?不适合什么地方?它解决了什么问题?又额外创造了哪些问题?”,基于这些问题去讨论一项技术,才能更深入地去了解它。

Atomic CSS 是在维护大型项目的时空背景之下诞生的,如果你没有碰到那种“牵一发而动全身,改一个地方要检查好多地方会不会坏掉”的状况,那你用了 Atomic CSS,可能确实感觉不到它的好处,因为它想解决的问题你根本没有碰到。

对于那些“它想解决的问题,你的项目没碰到”的状况下,导不导入的差异本来就不大,有些甚至还会增加不必要的复杂度。

若是在一个“相同的组件却四处分散,当你改 HTML 时需要同时改很多地方”的项目用上了 Atomic CSS,那确实是不适合,官方文件也不推荐这样做。如果硬要用,那碰到维护性的问题时并不是 Atomic CSS 的错,而是当时选择技术的人的错(就跟你说不适合了还要用)。

又或是你写了一个 UI library,而这个 library 又需要支持一些 UI 的定制化,如果你用 Atomic CSS 来做样式,那你要怎么完成这件事?难道要把每一个 HTML 元素都开放传 class name 进去吗?在这个状况下,像是 antd 那样使用传统的 CSS 解决方案说不定比较适合,因为可以直接改原本的 Less 档案,就能轻松客制化。

(daisyUI 是靠著把 HTML 一起开放出来,借此达成定制化,我上面指的案例比较像是写一个 React component,把实作细节包在里面的那种)

每个项目都有不同适合的技术与工具,在做选择时应该先了解每个项目的需求,以及每一项技术的优缺点,才能挑到相对合适的技术。

最后,从 Atomic CSS 的历史中,我觉得最值得学习的其实是“Tools, not rules”那一段。以前的最佳实践不一定适用于现在的状况,以前的 class name 不是这样用的,不代表现在就不行。我们不该墨守成规,不该执著在那些规则上面;如果别的做法有显而易见的好处,那为何不呢?

参考资料

  • Challenging CSS Best Practices

  • Let’s Define Exactly What Atomic CSS is

  • The Making of Atomic CSS: An Interview With Thierry Koblentz

  • Atomizer

  • ACSS FAQ

  • HTML5DevConf: Renato Iwashima, “Atomic Cascading Style Sheets”

  • Thierry Koblentz 在 FED London 2015 的简报

  • About HTML semantics and front-end architecture

  • Atomic CSS-in-JS

  • 浅谈 CSS 方法论与 Atomic CSS

  • 邪魔歪道还是苦口良药?Functional CSS 经验分享

  • 客观评价 TailwindCSS

  • Uno CSS - 一统天下的明日之星?

  • 重新构想原子化 CSS

  • 在 VUE SFC (vue-cli) 规划 Tailwind CSS 架构®

关于本文
作者:@huli
原文:https://blog.huli.tw/2022/05/23/atomic-css-and-tailwind-css/

CSS相关阅读,欢迎自荐投稿,前端早读课等你来。

【第2978期】CSS中@scope的简介
废物利用,拿自己的旧电脑搭建个服务器吧!
手把手教你搭建属于自己的服务器
【第2982期】使用示例介绍Web组件
CSS 高级选择器完全指南

文章有问题?点此查看未经处理的缓存