模块化 CSS( Modular CSS)是指把页面分割成不同的组成部分,这些组成部分可以在多种上下文中重复使用,并且互相之间没有依赖关系。最终目的是,当我们修改其中一部分 CSS 时,不会对其他部分产生意料之外的影响。
之前的样式表可以使用选择器在页面上随意修改,模块化的样式则允许开发人员添加一些限制。我们把样式表的每个组成部分称为模块( module),每个模块独立负责自己的样式,不会影响其他模块内的样式。也就是说,在 CSS 里引入了软件封装的原则。
封装( encapsulation) ——相关的函数和数据集合在一起组成对象,通常用来隐藏结构化对象内部的状态或值,从而使外部因素不能操作对象内部。
CSS 中没有数据和传统函数的概念,但是有选择器及其命中的页面元素。为了达到封装的目的,这些会成为模块的组成部分,并且每个模块都只负责少量的 DOM 元素的样式。
有了封装的思想,我们就可以为页面上那些彼此分立的组件定义模块了,像导航菜单、对话框、进度条、缩略图,等等。可以通过为 DOM 元素设置一个独一无二的的类名来识别每个模块。同时,每个模块包含一系列子元素,构建成页面上的组件。模块内部可以嵌套其他模块,最终构成完整的页面。
1. 基础样式:打好基础
开始写模块化样式之前,需要先配置好环境。每个样式表的开头都要写一些给整个页面使用的通用规则,模块化 CSS 也不例外。这些规则通常被称为基础样式,其他的样式是构建在这些基础样式之上的。基础样式本身并不是模块化的,但它会为后面编写模块化样式打好基础。
新建一个网页和一个样式表,把代码清单1中的基础样式粘贴到 CSS 中。这里只是列举了你可能用到的一些基础样式。
/*重置盒模型大小*/
: root {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
body {
font-family: 'Times New Roman', Times, serif;
}
其他常用的基础样式还包括链接的颜色、标题的样式、外边距等。 标签默认的外边距很小,你可能会考虑将它的外边距去掉。根据项目的实际情况,你也可能想为表单字段、表格和列表等添加一些样式。
基础样式应该是通用的,只添加那些影响页面上大部分或者全部内容的样式。选择器不应该使用类名或者 ID 来匹配元素,应只用标签类型或者偶尔用用伪类选择器。核心思想是这些基础样式提供了一些默认的渲染效果,但是之后可以很方便地根据需要覆盖基础样式。基础样式配置完成以后,很少会再修改。我们会在基础样式的稳定表现之上,构建模块化CSS。在样式表中,基础样式后面的内容将主要由各种模块组成。
2. 一个简单的模块
下面来创建一个短消息通知的模块。每个模块都需要一个独一无二的名称,我们把这个模块叫作“message”。为了吸引用户的注意,可以加上一些颜色和边框效果(如下图所示)。
这个模块使用一个类名为 message 的 div 作为标记。将代码清单2添加到网页中。
<div class="message">
Save successful
</div>
模块的 CSS 是一个规则集,通过类名指向模块。 CSS 中设置了内边距、边框、边框圆角和颜色。把代码清单3添加到样式表中,放在基础样式后面,就可以把这些样式应用到消息模块了。
.message {
padding: 0.8em 1.2em;
border-radius: 0.2em;
border: 1px solid #265559;
color: #265559;
background-color: #e0f0f2;
}
模块的选择器由单个类名构成,这非常重要。选择器里没有其他规则来约束这些样式仅作用在页面上的某个地方。对比一下,如果使用一个类似于#sidebar .message 的选择器,就意味着这个模块只能用在#sidebar 元素内部。没有这些约束,模块就可以在任意上下文中重复使用。
通过给元素添加类名,就可以把这些样式复用到很多场景,比如针对表单输入给用户反馈,提供醒目的帮助文字,或者提醒用户注意免责声明条款等。使用相同的组件,就产生了一套风格一致的 UI。所有用到组件的地方将看上去一样,不会出现有的地方蓝绿色有色差、有的地方内边距偏大等问题。
2.1. 模块的变体
保持一致性确实不错,但有时候需要特意避免一致。上面的消息模块很好用,但某些情况下我们需要它看起来有些不同。比如,我们需要显示一条报错的消息,这时候应该使用红色而不是之前的蓝绿色。再比如,我们可能想要区分传递信息的消息和表示操作成功的通知(比如保存成功)。这可以通过定义修饰符( modifiers)来实现。
通过定义一个以模块名称开头的新类名来创建一个修饰符。例如,消息模块的 error 修饰符应该叫作 message-error。通过包含模块名称,可以清楚地表明这个类属于消息模块。
说明: 常用的写法是使用两个连字符来表示修饰符,比如 message—error。
下面我们为模块创建三个修饰符:成功、警告和错误。将代码清单4添加到样式表中。
.message {
padding: 0.8em 1.2em;
border-radius: 0.2em;
border: 1px solid #265559;
color: #265559;
background-color: #e0f0f2;
}
.message--success {
color: #2f5926;
border-color: #2f5926;
background-color: #cfe8c9;
}
.message--warning {
color: #594826;
border-color: #594826;
background-color: #e8dec9;
}
.message--error {
color: #59262f;
border-color: #59262f;
background-color: #e8c9cf;
}
修饰符的样式不需要重新定义整个模块,只需覆盖要改变的部分。在本例中,这意味着只需要修改文本、边框和背景的颜色。
如代码清单5所示,把主模块类名和修饰符类名同时添加到元素上,就可以使用修饰符了。这样既应用了模块的默认样式,又可以在有需要的时候利用修饰符重写部分样式。
<div class="message message--error">
Invalid password
</div>
同样,有需要时也可以使用成功或警告修饰符。这些修饰符只是改变了模块的颜色,但其他的修饰符可能会改变模块的大小甚至布局。
1. 按钮模块的变体
下面创建另一个带有一些变体的模块。我们将实现一个按钮模块,其中包含大小和颜色选项的变体(如下图所示)。我们可以用不同的颜色为按钮添加视觉意义。绿色代表积极的行为,比如保存和提交表单;红色意味着警告,有利于防止用户不小心点击取消按钮。
代码清单6给出了这些按钮的样式,包括基础按钮模块和四个修饰符类:两个尺寸修饰符和两个颜色修饰符。将这些代码添加到样式表中。
.button {
padding: 0.5em 0.8em;
border: 1px solid #265559;
border-radius: 0.2em;
background-color: transparent;
font-size: 1rem;
}
.button--success {
border-color: #cfe8c9;
color: #fff;
background-color: #2f5926;
}
.button--danger {
border-color: #e8c9c9;
color: #fff;
background-color: #a92323;
}
.button--small {
font-size: 0.8rem;
}
.button--large {
font-size: 1.2rem;
}
尺寸修饰符能够设置字体的大小。通过更改字号来调整元素相对单位 em 的大小,进而改变内边距和边框圆角的大小,而不需要重写已经定义好的值。
提示: 要把一个模块所有的代码集中放在同一个地方,这样一个接一个的模块就会组成我们最终的样式表。
有了这些修饰符,写 HTML 的时候就有了多种选择。我们可以根据按钮的重要程度来添加修饰符类,修改按钮的大小,也可以选择用不同的颜色来为用户提供语境意义。
代码清单7里的 HTML 组合使用修饰符来创建多个按钮。将这些代码添加到页面,并查看实际效果。
<button class="button button--large">Read more</button>
<button class="button button--success">Save</button>
<button class="button button--danger button—small">Cancel</button>
双连字符的写法可能看起来有点儿多余,但当我们开始创建名称很长的模块的时候,比如导航菜单或者文章摘要,好处就显现出来了。为这些模块添加修饰符后,类名将如 nav-menu—horizontal 或者 pull-quote—dark。
双连字符的写法很容易区分哪部分是模块名称,哪部分是修饰符。 nav-menu—horizontal和 nav—menu-horizontal 分别代表了不同的含义。这样一来,即使项目里有很多名称相似的模块,也很容易分辨它们。
2. 不要使用依赖语境的选择器
假设我们正在维护一个网站,里面有浅色调的下拉菜单。有一天老板说,网页头部的下拉菜单需要改成带白色文本的深色调。
如果没有模块化 CSS,我们可能会使用类似于.page-header .dropdown 的选择器,先选中要修改的下拉菜单,然后通过选择器写一些样式,覆盖 dropdown 类提供的默认颜色。现在要写模块化 CSS,这样的选择器是严格禁用的。虽然使用后代选择器可以满足当下的需要,但接下来可能会带来很多问题。下面我们来分析一下。
第一,我们必须考虑把这段代码放在哪里,是和网页头部的样式放在一起,还是跟下拉菜单的样式放在一起?如果我们添加太多类似的单一目的的规则,样式之间毫无关联,到最后样式表会变得杂乱无章。并且,如果后面需要修改样式,你还能想起来它们放在哪里吗?
第二,这种做法提升了选择器优先级。当下次需要修改代码的时候,我们需要满足或者继续提升优先级。
第三,后面我们可能需要在其他场景用到深色的下拉列表。刚才创建的这个下拉列表是限定在网页头部使用的。如果侧边栏也需要同样的下拉列表,我们就得为该规则集添加新的选择器来匹配两个场景,或者完整地复制一遍样式。
第四,重复使用这种写法会产生越来越长的选择器,将 CSS 跟特定的 HTML 结构绑定在一起。例如,如果有个#products-page .sidebar .social-media div:first-child h3这样的选择器,样式集就会和指定页面的指定位置紧紧耦合。这些问题是开发人员处理 CSS 的时候遭受挫折的根源。使用和维护的样式表越长,情况越糟。新样式需要覆盖旧样式时,选择器优先级会持续提升。到后面不知不觉地就会发现,我们写了一个选择器,其中包含两个 ID 和五个类名,只是为了匹配一个复选框。
在样式表中,元素被各种彼此不相关的选择器匹配,这样很难找到它使用的样式。理解整个样式表的组织方式变得越来越困难,你搞不明白它是怎样把页面渲染成这样的。搞不懂代码就意味着 bug 变得常见,可能很小的改动就会弄乱大片的样式。删除旧代码也不安全,因为你不了解这段代码是干什么的,是否还在用。样式表越长,问题就愈发严重。模块化 CSS 就是要尝试解决这些问题。
当模块需要有不同的外观或者表现的时候,就创建一个可以直接应用到指定元素的修饰符类。比如,写.dropdown—dark,而不是写成.page-header .dropdown。通过这种方式,模块本身,并且只能是它本身,可以决定自己的样式表现。其他模块不能进入别的模块内部去修改它。这样一来,深色下拉列表并没有绑定到深层嵌套的 HTML 结构上,也就可以在页面上需要的地方随意使用。
千万不要使用基于页面位置的后代选择器来修改模块。坚决遵守这个原则,就可以有效防止样式表变成一堆难以维护的代码。
2.2. 多元素模块
我们已经创建了消息和按钮两个模块,简单又好用,它们都由单个元素组成,但是有很多模块需要多个元素。我们不可能只靠一个元素就实现下拉菜单或者模态框。
下面来创建一个更复杂的模块。这是一个媒体对象(如下图所示)。
这个模块由四个元素组成: div 容器、容器包含的一张图片和正文、正文里的标题。跟其他模块一样,我们会给主容器添加 media 类名来匹配模块名称。对于图片和正文,可以使用类名mediaimage 和 mediabody。这些类名以模块名称开头,后跟双下划线,然后是子元素的名称。(这是 BEM 命名规范里的另一种约定。)就跟双连字符代表的修饰符一样,这样的类名可以清楚地告诉我们这个元素扮演了什么角色、属于哪个模块。
媒体模块的样式如代码清单8所示,将其添加到样式表中。
/*主容器*/
.media {
padding: 1.5em;
background-color: #eee;
border-radius: 0.5em;
}
/*清除浮动*/
.media::after {
content: "";
display: block;
clear: both;
}
/*图片和正文子元素*/
.media__image {
float: left;
margin-right: 1.5em;
}
.media__body {
overflow: auto;
margin-top: 0;
}
/*正文里的标题*/
.media__body > h4 {
margin-top: 0;
}
你会发现并不需要使用很多后代选择器。图片是媒体模块的一个子元素,所以可以使用选择器.media > .mediaimage,但这不是必要的。因为 mediaimage 类名包含了模块的名称,所以已经确保模块名称是独一无二的了。
正文标题确实直接使用了后代选择器。其实也可以用 mediatitle 类(或者 mediabody__title,这样可以完整地表示出在整个层级中的位置),但是大部分时候没必要。在本例中,
标签已经足够语义化,能够表明这是媒体模块的标题。不过这样一来,标题就不能使用其他的 HTML 标签(
或者
)了。如果你不太喜欢这么严格的限制,可以改成使用类名来匹配元素。
将代码清单9中的模块标记添加到页面中。
<div class="media">
<img src="./image/runner.png" alt="" class="media__image">
<div class="media__body">
<h4>Strength</h4>
<p>
Strength training is an important part of
injury prevention. Focus on your core—
especially your abs and glutes.
</p>
</div>
</div>
这是个多功能的模块,可以工作在各种尺寸的容器内部,随着容器宽度自适应调整。正文可以包含多个段落,也可以使用不同尺寸的图片(可以考虑为图片添加 max-width 属性,防止图片挤出正文区域)。
1. 同时使用变体和子元素
我们也可以创建模块的变体。现在可以很轻松地把图片从左浮动改成右浮动(如下图所示)。
变体 media—right 可以实现这样的效果。我们把变体的类名添加到模块的主 div 上(
将修饰符类名添加到 HTML 里的元素上,然后把代码清单10添加到样式表里查看效果。
.media--right > .media__image {
float: right;
}
这条规则覆盖了媒体图片之前的 float: left。由于浮动的工作原理,我们不需要改变 HTML中元素的排列顺序。
2. 避免在模块选择器中使用通用标签名
我们在媒体模块中使用了选择器.mediabody > h4 来匹配标题元素。这么做是允许的,因为
标签就是用来标识一个次要标题的。同样的方式也可以用在带列表的模块上。相比为列表里的每个项目都添加 menuitem 类名,使用.menu > li 匹配菜单项简单多了,尽管这种写法有些争议。
我们应该避免使用基于通用标签类型的匹配,比如 div 和 span。类似于.page-header >span 的选择器太宽泛了。最初建立模块的时候,可能只是用 span 标签做一件事,但谁也说不准以后会不会出于其他目的再添加第二个 span。后面再为 span 追加类名就比较麻烦了,因为我们需要在 HTML 标记中找到所有用到模块的地方,全部改一遍。
3. 把模块组合成更大的结构
每个模块应该只做一件事情。消息模块的职责是使消息提示醒目;媒体模块的职责是在一段文本中配置一张图片。我们可以简洁明了地概括出它们的目标。有的模块是为了版面布局,有的是为了编写体例。当模块想要完成不止一件事的时候,我们应该考虑把它拆分成更小的模块。
创建模块之前应该先自问一下:“从更高的层面上看,这个模块的职责是什么?”对于本例,你的回答可能是这样的:“用按钮触发下拉菜单并展示上下堆叠排列的菜单项。”
就这个场景来说,这还算是个比较恰当的描述。但是我有一条经验:“如果你不得不使用并(或者和)这个词来表述模块的职责,那你可能正在描述多项职责。”因此,模块究竟是要触发菜单,还是展示堆叠菜单项呢?
当我们需要使用并(或者和)来描述模块职责的时候,思考一下是不是在描述两种(甚至更多的)职责。有可能不是。如果是的话,我们就需要为每个职责分别定义模块。这是模块封装的一个非常重要的原则,我们把它叫作单一职责原则( Single Responsibility Principle)。尽可能把多种功能分散到不同的模块中,这样每个模块就可以保持精炼、聚焦,并且容易理解。
3.1. 拆分不同模块的职责
下面我们用两个不同的模块来创建下拉菜单。第一个模块可以叫作下拉( dropdown),其中包含一个控制容器可见性的按钮。换句话说,这个模块负责展示和隐藏容器。我们也可以描述按钮的外观和代表行为的小三角。阐述模块的细节虽然需要用到并(或者和),但是这些细节都是从属于首要职责的, 因此这么做没问题。
第二个模块叫作菜单,是放置链接的列表。把菜单模块的一个实例放入下拉模块的容器内,就可以构成完整的界面了。
把代码清单11中的代码加入到页面中。这段代码主体是一个下拉模块,下拉模块内部包含了菜单模块。代码中还有一小段 JavaScript,当触发器被点击时用来实现开关的功能。
<div class="dropdown">
<!--下拉的触发按钮-->
<button class="dropdown__toggle">Main Menu</button>
<!--用做菜单容器的抽屉子元素-->
<div class="dropdown__drawer">
<!--放在抽屉内部的菜单模块-->
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="/coffees">Coffees</a></li>
<li><a href="/brewers">Brewers</a></li>
<li><a href="/specials">Specials</a></li>
<li><a href="/about">About us</a></li>
</ul>
</div>
</div>
<script type="text/javascript">
(function() {
var toggle = document.querySelector('.dropdown__toggle');
toggle.addEventListener('click', function(event) {
event.preventDefault();
var dropdown = event.target.parentNode;
dropdown.classList.toggle('is-open')
})
})();
</script>
这里使用了双下划线标记,表示触发器和抽屉是下拉模块的子元素。点击触发器可以显示或者隐藏抽屉元素。 JavaScript 代码为下拉模块的主元素添加或者移除 is-open 类,以此来实现这个功能。
下拉模块的样式如代码清单12所示,将其添加到样式表中。这样就实现了下拉的功能,不过里面的菜单目前还没有样式。
/*为绝对定位的抽屉元素建立一个包含块*/
.dropdown {
display: inline-block;
position: relative;
}
.dropdown__toggle {
padding: 0.5em 2em 0.5em 1.5em;
border: 1px solid #ccc;
font-size: 1rem;
background-color: #eee;
}
.dropdown__toggle::after {
content: "";
position: absolute;
right: 1em;
top: 1em;
border: 0.3em solid;
border-color: black transparent transparent;
}
.dropdown__drawer {
display: none;
position: absolute;
left: 0;
top: 2.1em;
min-width: 100%;
background-color: #eee;
}
.dropdown.is-open .dropdown__toggle::after {
top: 0.7em;
border-color: transparent transparent black;
}
.dropdown.is-open .dropdown__drawer {
display: block;
}
在代码清单12里,主元素使用了相对定位,这样就创建了一个包含块,抽屉元素在包含块内使用绝对定位。代码也为触发按钮提供了一些样式,包括::after 伪元素里的三角形。在添加了 is-open 类之后,它会显示抽屉元素并翻转三角形。
接下来,当我们需要回过头修改某个模块时,就会发现模块越小越好,这有助于迅速理解。
1. 在模块里使用定位
这是我们第一个使用定位的模块,其中创建了模块自己的包含块(主元素的 position: relative)。绝对定位的元素(抽屉元素和::after 伪元素)就是基于同一个模块内的位置来定位的。
应该尽量让需要定位的元素关联到同一个模块内的其他元素。只有这样,我们把模块放在另一个有定位的容器里的时候,才不会弄乱样式。
2. 状态类
is-open 类在下拉模块中有特定的用途。我们在模块里使用 JavaScript 动态地添加或移除它。它也是状态类( state class)的一个示例,因为它代表着模块在当前状态下的表现。
按照惯例,状态类一般以 is-或者 has-开头。这样状态类的目的就会比较明显,它们表示模块当前状态下的一些特征或者即将发生的变化。再举一些状态类的示例,比如 is-expanded、is-loading 或者 has-error 等。这些状态类具体会表现成什么样子取决于使用它们的模块。
重点: 状态类的代码要和模块的其他代码放在一起。使用 JavaScript 动态更改模块表现的时候,要使用状态类去触发改变。
3. 菜单模块
下拉模块已经搞定了,下面开始实现菜单模块。我们不需要关心下拉动作的开和关,这已经在下拉模块里实现了。菜单模块只需要实现链接列表的观感。
样式如代码清单13所示,将其添加到样式表中。
.menu {
margin: 0;
padding-left: 0;
list-style-type: none;
border: 1px solid #999;
}
.menu > li + li {
border-top: 1px solid #999;
}
.menu > li > a {
display: block;
padding: 0.5em 1.5em;
background-color: #eee;
color: #369;
text-decoration: none;
}
.menu > li > a:hover {
background-color: #fff;
}
每个
菜单模块是完全独立的,并不依赖于下拉模块。这使得代码更简单,因为我们不需要理解在这个模块之前先搞懂另一个,也有助于更加灵活地复用模块。
我们可以根据不同需要创建其他样式的菜单(变体或者完全不同的模块都可以),用在下拉模块的内部。也可以把菜单模块用在下拉模块以外的任意地方。我们无法预知后面的页面需要什么,但有了可复用的模块,可以一定程度上确保提供前后一致的观感。
3.2. 模块命名
为模块命名是个很伤脑筋的事情。开发模块的时候我们可以用临时的名称,但是最终完成之前,一定要注意命名。这可能算是模块化 CSS 开发里最难的部分了。
回想一下前面的媒体模块,如下图所示,我们用它来展示一张跑步者的图片和跑步小提示。
假设我们还没有为该模块命名,现在有个页面需要用到它,我们可能会叫它跑步提示模块。这个名称很贴切,看上去也比较合适,但是我们可能会用这个模块里的样式去做其他事情。如果使用同样的 UI 元素做别的事情,该怎么办呢?比如延续跑步主题网站的主题,我们可能会使用一连串的模块列出即将举办的赛事信息,这时候还以跑步提示来命名模块就不合适了。
模块的命名应该有意义,无论使用场景是什么。同时也要避免使用简单地描述视觉效果的名称。把这个模块叫作“带图片的灰盒子”看上去比较通用一些,但是如果之后要改成浅蓝色背景呢?或者重新设计网站呢?这样的名称就不能用了,你还得重新命名,再替换掉 HTML 里所有用到它的地方。
我们应该换一种思路,思考模块代表什么含义。这一般并不容易。“媒体模块”这个名称就很恰当,它代表了一种图文混排的版式。它给人以强烈的印象,并没有将模块局限于任何特定用法或者视觉实现。
模块要适用于各种不同场景,而其名称应该简单易记。当网站有很多页面的时候,我们可能会多次用到某个模块。到时候你和团队里其他成员沟通,可能会进行这种对话:“这里用个‘媒体’ ”“这 些‘板块’ 太挤了”。
目前,我们已经实现了消息模块、媒体模块、下拉模块和菜单模块。一些比较好的模块名称包括面板( panel)、警告( alert)、可折叠的部分( collapsible-section)、表单控制项( form-control)等。如果你从一开始就对网站的整体设计有全面的了解,会有助于命名。例如,你可能觉得有两个 UI 元素都可以叫作板块( tile),然而它们毫不相关,这时候就应该更明确地命名它们(比如媒体板块和标题板块)。
有些人强制使用两个词来命名每个模块,这样就可以避免模块指代不明确,因为你也不知道什么时候会需要另一个新的板块模块。如果现有的板块模块命名比较明确,新的板块模块出现的时候,再取名就会比较容易,不至于跟前一个混淆。
为模块的变体类命名的时候,应该遵守同样的原则。例如,如果已经有按钮模块了,就不应该使用 button—red 和 button—blue 命名红色和蓝色变体子类。网站设计在将来有可能会改变,你不知道这些按钮的颜色会不会也跟着变化。应该使用一些更有意义的名称,比如button—danger 和 button—success。
使用大或小这样具有相对意义的词语来命名修饰符不是最佳方式,但也可以接受。没人说过网站重构的时候不能更改 button—large 的尺寸,只要它还是比标准按钮大一些就可以。一定要牢记,不要使用像 button—20px 这样特别精确的修饰符。
4. 工具类
有时候,我们需要用一个类来对元素做一件简单明确的事,比如让文字居中、让元素左浮动,或者清除浮动。这样的类被称为工具类( utility class)。
从某种意义上讲,工具类有点像小号的模块。工具类应该专注于某种功能,一般只声明一次。我通常把这些工具类放在样式表的底部,模块代码的下面。
代码清单14展示了四个工具类,它们分别实现了特定的功能:文字居中、左浮动、清除浮动(包裹浮动)、隐藏元素。
.text-center {
text-align: center !important;
}
.float-left {
float: left;
}
/*清除浮动*/
.clearfix::before, .clearfix::after {
content: "";
display: table;
}
.clearfix::after {
clear: both;
}
.hidden {
display: none !important;
}
这里用到了两次!important。工具类是唯一应该使用 important 注释的地方。事实上,工具类应该优先使用它。这样的话,不管在哪里用到工具类,都可以生效。我敢肯定,任何时候为元素添加 text-center 类,都是想让文本居中,不想让其他样式覆盖它。用了 important 注释就可以确保这一点。
可以把这些类添加到页面元素里看看实际效果。
5. 总结
- 把 CSS 拆解成可复用的模块。
- 不要书写可能影响其他模块或者改变其他模块外观的样式。
- 使用变体类,提供同一模块的不同版本。
- 把较大的结构拆解成较小的模块,然后把多个模块组合在一起构建页面。
- 在样式表中,把所有用于同一个模块的样式放在一起。
- 使用一种命名约定,比如双连字符和双下划线,以便一眼就可以看清楚模块的结构。