# CSS 命名那些事儿

OOCSS SMACSS METACSS BEMCSS

TIP

TODO 待重构

# OOCSS 模式

面向对象的 CSS

OOCSS(Object Oriented CSS)

OOCSS 的创始人 Nicole Sllivan 提了两个主要原则

结构与样式分离:

你应该在对象中定义结构和位置,而对于样式特性应该使用类名分离出来,比如说 background 或 border。这样一来你就不需要去覆盖一些特征性样式

容器与内容分离:

不要在你的 HTML 结构中插入样式。换句话说,你的样式中尽量不要使用 html 标签或者 id 标识符。相反,应该定义一些类名来定义样式,而且选择器的嵌套的层级应该尽量的少。


/* 不好的方式 */
.box-1 {
  border: 1px solid #ccc;
  width: 200px;
  height: 200px;
  border-radius: 10px;
}
.box-2 {
  border: 1px solid #ccc;
  width: 120px;
  height: 120px;
  border-radius: 10px;
}
/* 好的方式 */
.box-1 {
  width: 200px;
  height: 200px;
}
.box-2 {
  width: 120px;
  height: 120px;
}
.box-border {
  border: 1px solid #ccc;
  border-radius: 10px;
}

一句话解释: 使用class来编写样式,每个class都有其各自的用途。有点类似原子类 CSS 的思想。

# SMACSS 模式

可伸缩和模块化

TIP

TODO 待补充

SMACSS (Scalable & Modular Architecture CSS) 像 OOCSS 一样以减少重复样式为基础,使用一套五个层次来划分 CSS 给项目带来更结构化的方法

  • Base (基本)
  • Layout (布局)
  • Module (模块)
  • State (状态)
  • Theme (皮肤)

如果全部用上可能会显得有点繁琐,一般来说只用上 Base Layout State 就够了,如果不够用再进一步拓展。

最佳实践:Bootstarp (opens new window)

# METACSS 模式

METACSS 一些写在全局的通用方法,是 SMACSS 中通用方法思想的分支。又叫做原子类 CSS

一般以 css 属性、Emmet css 缩写或功能来命名,通常以一个 css 属性为一个单位

表示属性的:每个属性单独成列

.display-flex {
  display: flex;
}

表示功能的: 一组属性形成一个特定功能

.text-ellipsis {
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}

最佳实践:Tailwind CSS (opens new window)

# BEM 模式

BEM 可能是目前最流行的 CSS 命名方式,很多主流的组件库都是使用这种命名方式,因此我们的重点放在它身上。

BEM 分别代表着:Block(块)、Element(元素/子块/组成部分)、Modifier(修饰符),是一种组件化的 CSS 命名方法和规范,由俄罗斯 Yandex 团队所提出。

# 特点

组件化/模块化的开发思路,书写方式解耦化,不会造成命名空间的污染,如:.xxx ul li 写法带来的潜在嵌套风险。

命名方式化扁平,避免样式层级过多而导致的解析效率降低,渲染开销变大。

组件结构独立化,减少样式冲突,可以将已开完成的组件快速应用到新项目中。有着较好的维护性、易读性、灵活性。规则

BEM 的命名模式在社区中有着不同方式,以下为 Yandex 团队所提出的命名规则为(本文中的代码使用该规则):

.[Block 块]__[Element 元素]_[Modifier 修饰符] 不同的命名模式,区别在于 BEM 之间的连接符号不同,依个人而定:

.[Block 块]__[Element 元素]_[Modifier 修饰符] 任何一种规范,都是基于实际需求而定,便于团队开发和维护扩展,每个规范都是经过合理评估后所得出的一种“思路”和“建议”。

# Block

Block 是一个独立的实体,即通常所说的模块或组件。例:header、menu、search

规则

块名需能清晰的表达出,其用途、功能或意义,具有唯一性。

块名称之间用-连接。每个块名前应增加一个前缀, 这前缀在 CSS 中有命名空间(如:m-、u-、分别代表:mod 模块、ui 元件)。 每个块在逻辑上和功能上都相互独立。

由于块是独立的,可以在应用开发中进行复用,从而降低代码重复并提高开发效率。

块可以放置在页面上的任何位置,也可以互相嵌套。同类型的块,在显示上可能会有一定的差异,所以不要定义过多的外观显示样式,主要负责结构的呈现。这样就能确保块在不同地方复用和嵌套时,增加其扩展性。

综上所述,最终我们可以把 BEM 规则最终定义成:

.[命名空间]-[组件名/块]__[元素名/元素]--[修饰符]

情景

需要构建一个 search 组件。

写法

.m-search {
}

结构

如果打算开发一套框架,可以使用具有代表性的缩写,用来表示命名空间:

Element UI(el-)、Ant Design(ant-)、iView(ivu-)。

# Element

是块中的组成部分,对应块中的子元素/子节点。例:header title、menu item、list item

规则

元素名需能简单的描述出,其结构、布局或意义,并且在语义上与块相关联。块与元素之间用__连接。不能与块分开单独使用。

块的内部元素,都被认为是块的子元素。一个块中元素的类名必须用父级块的名称作为前缀,因此不能写成:blockelem1elem2。

情景

search 组件中包含 input 和 button,是列表中的一个子元素。

写法

.m-search {
}
.m-search__input {
}
.m-search__button {
}

结构

<form class="m-search">
  <input class="m-search__input" />
  <button class="m-search__button">Search</button>
</form>

原则上书写时不会出现两层以上的嵌套,所有样式都为平级,嵌套只出现在比如 .m-block_active ,状态激活时的情况。

# Modifier

定义块和元素的外观、状态或类型。

例:color、disabled、size

规则 修饰符需能直观易懂表达出,其外观、状态或行为。修饰符用_连接块与元素。修饰符不能单独使用。在必要时可进行扩展,书写成:block__elem_modifier_modifier,第一个 modifier 表示其命名空间。情景 假定 search 组件有多种外观,我们选择其中一种。并且在用户未输入内容时,button 显示为禁用样式。

写法

.m-search {
}
.m-search_dark {
}
.m-search__input {
}
.m-search__button {
}
.m-search__button_disabled {
}

结构

<!-- dark 表明 search 组件的外观 -->
<form class="m-search m-search-form_dark">
  <input class="m-search__input" />
  <!-- disabled 表明 search__button 的状态 -->
  <button class="m-search__button m-search__button_disabled">Search</button>
</form>

很多人觉得 BEM 写法难看,刚刚接触可能是会觉得有点奇怪,但所有东西都有一个适应过程。如果仅仅为了好看,规避其优点,我认为得不偿失。个人建议可以尝试使用 BEM 规范来书写代码。

BEM 命名会使得 Class 类名变长,但经过 GZIP 等压缩后,文件的体积其实并无太大影响。

就和早年提出 CSS 语义化 一样,不要为了语义而去语义,语义化本身的作用就是帮助大家更好的识别代码,所有的规范都是基于项目的发展和团队的协作,团队可以根据成员的意愿选择最合适的方式。

# 我的 CSS 模式

  • 全局样式遵循类名控制一个属性或一组功能的样式,遵循 BEM 命名
  • 组件内的样式,使用 Less/Sass 合理嵌套,遵循 BEM 命名
  • 元素的状态,使用 .active.current.disable,嵌套在 BEM 之后

/* 全局 */
.display-flex {
  display: flex;
}
.flex-1 {
  flex: 1 1 0;
}
/* 组件 */
.el-input {
  height: 28px;
}
.el-input_inner {
  color: #ccc;
}
/* 状态 */
.el-input_inner.active {
  color: #777;
}

# 参考资料

博客 CSS 的模組化方法 (opens new window)
Sass 使用 Sass 来写 OOCSS (opens new window)
知乎 前端使用原子类的优劣? (opens new window)
知乎 如何看待 CSS 中 BEM 的命名方式? (opens new window)
掘金 如何写出一套可维护的 CSS 库? (opens new window)