秦小

精心设计的 CSS 架构(译)

本文翻译自 Nathan Rambeck 的Thoughtfull CSS Architecture

你是否参与一个 CSS 项目,然后这个项目随着时间慢慢的变得混乱不堪?CSS 样式对 HTML 的影响是很难跟踪的,对 CSS 的小幅改动能修复一个问题,同时也可能引进很多其他的问题,还可能需要丑陋的补丁,甚至会使 Javascript 代码失效。我想我们都遇到过这些问题。但是,只要在项目开始是就就有一个良好的设计,是可以大幅的避免这个问题的。现在我们来谈谈 CSS 的架构吧。

精巧架构的好处

精巧 CSS 架构的主要好处就是可扩展性。对任何一个项目来说,不管是项目规模的增长还是团队成员的增加,都会使其可扩展性成为挑战。CSS 项目也不例外。CSS 的层叠(cascading)和全局(global)特征,使它功能强大,同时也很脆弱。不管你写 CSS 代码多久了,你都会遇到这样的情形:你看到自己只是改动了一行 CSS 代码,准备修复一个小 bug,却没想到会使项目的其它好几处都废掉了。这时你只想一头撞死在墙上😂。仔细的设计 CSS 可以为我们带来如下好处:

  • 更少的样式规则
  • 更少的样式冲突
  • 长期可维护性
  • 新团队成员可以快速融入
  • 团队成员更轻松地合作
  • 平滑的项目接力(handoffs)

CSS 规则的种类

Jonathan Snook 在他书中Scalable and Modular Architecture for CSS(SMACSS) 推广了对 CSS 规则进行分组的概念。将 CSS 规则按照这些良好定义的类别进行分组,可以让我们对每个样式的目的有更好的理解。基于 SMACSS 我将 CSS 规则分组为 7 个类别,确保每个样式刚好符合这些类别中的一个。

  • 基本样式
  • 对象
  • 组件
  • 状态
  • 主题(Themes)
  • 实用工具(Utilities)
  • Javascript 钩子(hooks)

理解这些类别和它们的目标能帮助我们更好的理解我们所写的样式。

基本样式

基本样式是为元素标签所写的样式。它们是整个网站全局的默认样式。通常,这些样式包括排版,盒子尺寸(box-sizing),以及为不同浏览器表现不一的元素统一格式。一个常见的错误就是,对基本元素添加过多的样式,创建了不是真正需要的默认样式。比如,你应该不会全局地删除无序列表的项目符号吧?

对象

对象是指只关注结构和布局的规则。装饰性的样式不能分组到对象类别。对象样式的概念是由 Nicole Sullivan 推广开的, 目的是为了重用结构和布局的通用模式。找出你设计中的结构模式,并创界对象规则,将它们用于多个组件或者网站的各个部分。通过将这些样式放到对象类中,我们可以避免重复,减少 CSS 的大小。网格系统(不管是你自己一手撸的还是引入的框架)就是一个对象类别的例子。

组件

组件是相互独立的自包含的 UI 片段。它们是原子设计的面包和黄油,并最终集成到你的样式中去。组件可大可小,可以小到只是一个按钮,也可以大到是一个轮播屏(carousel)。创建健壮的组件的关键在于,保持组件的独立性和自包含性,不能依赖页面的其它部分。这样的话,你就可以把组件扔到任何页面,它自己就能管理好它的结构和设计了。

状态

状态类用于帮助更新组件的状态。比如说,折叠组件的打开和折叠,链接激活和未激活,以及元素的显示和隐藏。通常使用 Javascript 来添加这些状态类。不要用 Javascript 来直接处理样式,而是应该更新状态类,让样式表自行决定怎样渲染页面。

主题(Themes)

主题类只是简单地变更组件的颜色,字体和其他的装饰性样式。主题类也可以用来改变整个页面的外观,不仅仅用于组件。虽然不是每个项目都用到主题,但你需要时它就变得很有用。

1
2
3
<blockquote class="c-pullquote t-light">
<p>A great quote from someone special.</p>
</blockquote>

使用工具(Utilities)

工具类是用于特定样式规则的单一目标的帮助类。它们用来调整 spacing, 增加字体尺寸,居中文本,清除浮动, 隐藏元素等等。工具类还可以用来小幅调整布局,如添加组件之间的间隙或者清除浮动。还能对现成的组件做小幅的修改,以避免创建一个新的组件变种。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.u-sp {
margin-bottom: 1em !important;
}
.u-clearfix:after {
content: " ";
display: block; clear: both; visibility: hidden;
height: 0; font-size: 0;
}
.u-txt-center {
text-align: center !important;
}
.u-txt-larger {
font-size: 130% !important;
}

1
2
3
<div class="promo u-sp"></div>
<div class="promo u-sp"></div>
<div class="promo"></div>

Javascript 钩子(hooks)

只要可能,最好分离 Javascript 与样式之间的依赖。如果一个 CSS 类名同时用于样式应用和 Javascript 选择,将可能引起问题。比如在未来重构了 CSS 样式而 Javascript 没有相应的更新。所以,Javascript 钩子需要它们专用的 CSS 类。

1
<button class="btn btn--buy js-buy-now">

类名

为类命名时,一定要保证名字长到能轻易辨别(不要用 .pq,要用 .pullquote),但也不要超过需要的长度(要 .nav,不要 .navgation)。使用可读的类名,对团队成员在现在和将来都能理解你代码所表达的逻辑非常重要。

设计描述性的有意义的类名是写 CSS 是最难得问题,但是指要做好了,就会变得很有帮助。限于篇幅,这里不展开讨论怎样命名,建议看看 Ethan Muller 的这篇文章 Naming CSS Stuff is Really Hard, by

BEM 命名约定

BEM (Block, Element, Modifier) 是一个很著名的超好用的 CSS 组件类名命名约定,由俄罗斯最大的搜索引擎 Yandex 开发。BEM 非常简单:

[BLOCK]__[ELEMENT]-[MODIFIER]

你可能会吐槽这个命名法是不是太啰嗦了,但是这种担忧很快就会被 BEM 带来的价值所取代。下面是一个使用 BEM 的组件的例子。

1
2
3
4
5
6
7
<div class="alert alert--warning">
<h1 class="alert__title">
<span class="alert__icon"></span>
Alert Title
</h1>
<p class="alert__description">The password you entered is invalid.</p>
</div>

BEM 有如下三个好处:

  • Readability: 清晰描述性的类名使 HTML 和 CSS 文件更好读。
  • Self-description: 清楚地指出了哪个元素属于哪个组件。
  • Specificity: 为每一个元素都设置一个类看起来有些过分了。但是,这样做可以保持每个选择操作的低特殊性,使得重载变得更直接。

命名空间

另一个命名类的最佳实践是根据我们前面所描述的分类为类名加一个命名空间前缀。这些前缀在类名前加了几个字符,这些字符对理解这个类的使用目的是极有价值的。下面是我所用的命名空间:

  • 对象: .o-
  • 组件: .c-
  • 状态: .is-.has-
  • 主题: .t-
  • 实用工具: .u-
  • Javascript 钩子: .js-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<footer class="c-footer">
<div class="o-container-wide">
<a class="c-footer__logo" href="/">The Assist</a>
<div class="c-social c-social--follow">
<div class="c-social__label u-txt-center">Join the conversation</div>
<ul class="c-social__list">
<li class="c-social__item"></li>
<li class="c-social__item is-active"></li>
<li class="c-social__item"></li>
</ul>
</div>
<p class="c-footer__credit"></p>
</div>
</footer>

关于命名空间价值的更多信息,可以查看 Harry Roberts 的 More Transparent UI Code With Namespaces

代码风格

像其他任何代码一样,保持代码风格的一致性很重要。包括空白字符,缩进,命名约定,注释等规范。你可以找到从网上找到一些合理代码规范,比如: Google, GithubNicolas Gallagher。你可以使用它们,也可以制定自己的规范。

代码组织

为了最优化代码的组织你应该使用预处理工具( SassLessStylus)和后处理工具(PostCSS)来编译代码。好处很多,包括变量,函数,mixin, import 和 嵌套 (nesting) 等特性。这些特性让你比只是用 CSS 时能写出更具良好组织的架构。

使用 import, 可以将样式分解成多个意义明确的文件。

1
2
3
4
5
6
7
8
@import "variables";
@import "mixins";
@import "normalize";
@import "typography";
@import "headings";
@import "headings";
@import "layout";
@import "carousel";

为所有使用超过一次的值创建变量。为变量名加一个前缀,让变量的目的明确,同时也可以配合编辑器的补全功能。

1
2
3
4
// Colors
$c-warning: Red;
$c-primary: Blue;
$c-background: White;

全局变量应该定义在专门的文件中,只用于单个组件的局部变量应定义在当前文件中。对于 Sass,变量可以定义在嵌套的样式规则里面。

1
2
3
4
5
6
.alert {
$background-color: Red;
$foreground-color: Cream;
background-color: $background-color;
color: $foreground-color;
}

源码顺序

由于 CSS 层叠的本性,样式的顺序很是重要的。如果你没有特意的安排样式导入的顺序,你会发现你总要不停的与 CSS 的层叠规则进行战斗。

最近, Harry Roberts 发表了一个排序样式的明智方法/a sensible method for ordering your styles,他把这个方法称作 ITCSS (Inverted Triangle CSS)。其目标是避免名字空间冲突,特异性(specificity)问题,样式泄漏(leaky styles),和无意的回归(inadvertent regressions) (见他的 in-depth slides)。其概念非常简单:将那些应用到最大范围的具有最小特殊性的设定和规则放在最前面,将那些应用到最小范围的具有最大特殊性的置于最后。这就是说,要将变量定义,标签样式规则放在开头,而工具类和 IE 补丁置于结尾。

Harry 定义了 7 组样式文件类型:

  • settings: 变量和其他设置
  • Tools: 自定义函数和 Mixin
  • Generic: Font-face, box-sizing, normalize 等
  • Elements: 标签默认样式,如标题和链接
  • Objects: 布局和结构类
  • Components: 独立的组件
  • Trumps: 实用工具及终极王牌(Utilities and other rules meant to be a final trump over everything else)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@import "settings.global";
@import "settings.colors";
@import "tools.functions";
@import "tools.mixins";
@import "generic.box-sizing";
@import "generic.normalize";
@import "elements.headings";
@import "elements.links";
@import "objects.wrappers";
@import "objects.grid";
@import "components.nav";
@import "components.buttons";
@import "components.promos";
@import "trumps.utilities";
@import "trumps.ie8";

深度挖掘

这篇文章只简单介绍了正在变得越来越深入和广泛的巨大主题,算是抛砖引玉。希望能激发你的灵感,去创造更加精巧严密的 CSS 结构和设计。如果你想跟深入地了解这方面的主题,可以查看本文中引用的诸多链接,查看下面列出的此领域的领军人物的想法和资源。

  • Harry Roberts - 目前该领域最多产的思想领军人物之一。关注他的 Twitter,订阅这个博客。阅读他的有关CSS Guidelines的文档。
  • Jonathan Snook - 在他的论文和在线书籍中推广了 CSS 架构的概念, Scalable and Modular Architecture for CSS
  • Nicole Sullivan - 引入面向对象的 CSS (Object Oriented CSS)的概念。记录在一个 Github wiki中。