原文:BEM by Example, by Nathan Rambeck.
BEM 一个流行的类名约定标准,它的基本概念很简单,但对于新手而言,刚接触时也会碰到一些误区,本文将通过一系列例子来解释常见的使用误区。
BEM(是 Block-Element-Modifier 的简写形式)是一个 CSS 类名的命名标准。现在已被广泛使用,用来编写易于阅读、理解和扩展的 CSS 代码。
为什么是 BEM
使用 BEM 命名方式有如下三点好处:
- 命名本身能够传达功能或目的
- 命名本身能够传达组件结构
- 它是由一系列低权重选择器组成的
BEM 的工作原理
使用 BEM 方式组织的类名由以下三部分组成:
- 块(Block):组件的最外层的祖先元素定义为块。
- 元素(Element):组件内部中的后代元素,可能是一个可能是多个。
- 修饰符(Modifier):表示块或元素的一个变体。
如果上面三部分全部使用,BEM 的书写形式看起来是这样的:
[block]__[element]--[modifier]
经过简短的介绍之后,我们下面来看一些具体的例子。
例子
组件
有些组件简单到只用一个元素就能表达出来,这个单独的类就是块。
<button class="btn"></button>
<style>
.btn {}
</style>
带修饰符的组件
组件可能会有变体。组件变体使用修饰符来实现。
<!-- 这样写 -->
<button class="btn btn--secondary"></button>
<style>
.btn {
display: inline-block;
color: blue;
}
.btn--secondary {
color: green;
}
</style>
修饰符是配合基础类使用的,不能单独使用,修饰符中也不会包含基础样式。
<!-- 不要这样写 -->
<button class="btn--secondary"></button>
<style>
.btn--secondary {
display: inline-block;
color: green;
}
</style>
包含元素的组件
较复杂的组件包含后代元素,每一个需要赋予样式的元素都应该有一个名称。
使用 BEM 的目的之一是为了低权重和一致性。不要忽略后代元素的类名,否则你只能通过增加权重的方式为这些裸露的元素赋样式(下面代码里的 img
和 figcap
就是例子)。不使用类名可能结构看上去更简洁,但却增加了未来出现级联问题的风险。BEM 的一个目标是让大多数选择器只使用一个类名。
<!-- 这样写 -->
<figure class="photo">
<img class="photo__img" src="me.jpg">
<figcaption class="photo__caption">Look at me!</figcaption>
</figure>
<style>
.photo { } /* 权重 10 */
.photo__img { } /* 权重 10 */
.photo__caption { } /* 权重 10 */
</style>
<!-- 不要这样写 -->
<figure class="photo">
<img src="me.jpg">
<figcaption>Look at me!</figcaption>
</figure>
<style>
.photo { } /* 权重 10 */
.photo img { } /* 权重 11 */
.photo figcaption { } /* 权重 11 */
</style>
如果组件中包含多个层次的后代元素,不要试图在类名中表现出每一层。BEM 不是为了传达结构深度。BEM 表达的是组件中的一个后代元素,类名是由块和元素组成。下面的例子里,.photo__caption__quote
不是正确的 BEM 写法,.photo__quote
才是。
<!-- 这样写 -->
<figure class="photo">
<img class="photo__img" src="me.jpg">
<figcaption class="photo__caption">
<blockquote class="photo__quote">
Look at me!
</blockquote>
</figcaption>
</figure>
<style>
.photo { }
.photo__img { }
.photo__caption { }
.photo__quote { }
</style>
<!-- 不要这样写 -->
<figure class="photo">
<img class="photo__img" src="me.jpg">
<figcaption class="photo__caption">
<blockquote class="photo__caption__quote"> <!-- 类名中出现的后代元素不能多于一个 -->
Look at me!
</blockquote>
</figcaption>
</figure>
<style>
.photo { }
.photo__img { }
.photo__caption { }
.photo__caption__quote { }
</style>
带修饰符的元素
在某些情况下,你可能希望修改组件中的某个元素的样式。这时候,应该给元素而不是组件添加修饰符。当然,相比于修改整个组件,修改组件中某个元素的场景不太常见,用处也小。
<figure class="photo">
<img class="photo__img photo__img--framed" src="me.jpg">
<figcaption class="photo__caption photo__caption--large">Look at me!</figcaption>
</figure>
<style>
.photo__img--framed {
/* 增量样式修改 */
}
.photo__caption--large {
/* 增量样式修改 */
}
</style>
基于组件修饰符书写样式
如果你发现自己总是以相同的方式修改某个组件里的多个元素,那么可以考虑将修饰符添加到组件的基类名上,并根据这个修饰符的含义,调整每个后代元素的样式。这种方式会增加选择器权重,但让修改组件样式变得更简单了。
<!-- 这样写 -->
<figure class="photo photo--highlighted">
<img class="photo__img" src="me.jpg">
<figcaption class="photo__caption">Look at me!</figcaption>
</figure>
<style>
.photo--highlighted .photo__img { }
.photo--highlighted .photo__caption { }
</style>
<!-- 不要这样写 -->
<figure class="photo">
<img class="photo__img photo__img--highlighted" src="me.jpg">
<figcaption class="photo__caption photo__caption--highlighted">Look at me!</figcaption>
</figure>
<style>
.photo__img--highlighted { }
.photo__caption--highlighted { }
</style>
多词名称
BEM 名称故意使用双下划线和双连字符,来分隔块-元素-修饰符。如果使用的名称中包含多个单词,那么就可以使用一个连字符连接这多个单词。这样的类名更加易读,同时,应该尽量避免使用缩写,除非这个缩写是大家普遍知道的。
<!-- 这样写 -->
<div class="some-thesis some-thesis--fast-read">
<div class="some-thesis__some-element"></div>
</div>
<style>
.some-thesis { }
.some-thesis--fast-read { }
.some-thesis__some-element { }
</style>
<!-- 不要这样写
名称读起来很费劲
-->
<div class="somethesis somethesis--fastread">
<div class="somethesis__someelement"></div>
</div>
<style>
.somethesis { }
.somethesis--fastread { }
.somethesis__someelement { }
</style>
让人欢喜的 BEM
如果你现在还没有开始使用 BEM,那我强烈建议你在下一个项目中使用它。它可能与你已有的书写习惯不同,但我相信你很快就能看到使用它为项目带来的好处。
希望上面的这些例子能帮助你避免,大多数人在第一次使用这种古怪的命名约定时所犯的一些常见错误。
(完)