CSS(Cascading Style Sheet,可译为“层叠样式表”或“级联样式表”)是一组格式设置规则,用于控制Web页面的外观。
作为每个前端工程师入门第一门学习的语言来说,它简单易学的特点让许多初学者能够很快通过它来构建出一套富含色彩的网页,并且随着Css3的普遍流行与支持,新的特性无疑让这门简单的样式语言变得越来越强大,简单的写Css并不是一件多难的事情,而要写出一套可维护的Css却是一件相当困难的事情。
于是引入下面介绍的几种Css设计模式
OOCSS,字面意思是面向对象的CSS,是由Nicole Sullivan提出的css理论,虽说是理论,实则更像一种程序员间约定的规范:
* Separate structure and skin(分离结构和主题)减少对 HTML 结构的依赖
* Separate container and content(分离容器和内容)增加样式的复用性
一般我们在书写列表的时候结构大概会如上面的结构所示,这种时候我们如果要对a
修改样式可能经常会用.container-list ul li a
这种方式来选择,一方面这种写法在效率上比较拙劣,另一方面一旦我们在项目后期过程中对列表中的html结构进行了重构,这意味着我们同时也需要对css进行重构,使html与css的耦合性变得十分强,造成维护上的困难,也要避免没必要的嵌套地狱
而OOCSS推荐的写法是在a
标签内加上list-item
样式,此时便能通过.container-list .list-item
的方式来控制解耦
在 OOCSS 的观念中,强调重复使用 class,而应该避免使用 id 作为 CSS 的选择器。
以BootStrap为例(BootStrap便是根据OOCSS规范写的),以下为LESS文件:
.label {
display: inline;
padding: .2em .6em .3em;
font-size: 75%;
font-weight: bold;
line-height: 1;
color: @label-color;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
//省略
}
// Colors
// Contextual variations (linked labels get darker on :hover)
.label-default {
.label-variant(@label-default-bg);
}
.label-primary {
.label-variant(@label-primary-bg);
}
.label-success {
.label-variant(@label-success-bg);
}
.label-info {
.label-variant(@label-info-bg);
}
.label-warning {
.label-variant(@label-warning-bg);
}
.label-danger {
.label-variant(@label-danger-bg);
}
像这里的label
样式的重复部分先用一个样式.label
抽离出来,这样我们在使用不同样式的label时便可以这样使用:
<a class='label label-danger'>labela>
OOCSS追求元件的复用,其class命名更为抽象,一般不体现具体事物,而注重表现层的抽取.
这样的写法能有效提高页面的可维护性,结合LESS和SASS等预编译语言更是有无穷的力量,这是如果我们需要改变整个页面label的大小,我们也许只需要改变一下基础类.label
上的样式便可以了。
(What’s Smacss)[https://smacss.com/]
SMACSS is a way to examine your design process and as a way to fit those rigid frameworks into a flexible thought process. (smacss通过一个灵活的思维过程来检查你的设计过程和方式是否符合你的架构,更像一种规范~)
设计的主要规范有三点:
* Categorizing CSS Rules(为css分类)
* Naming Rules(命名规范)
* Minimizing the Depth of Applicability(最小化适配深度)
这一点是SMACSS的核心。SMACSS认为css有5个类别,分别是:
1 Base
2 Layout
3 Module
4 State
5 Theme or Skin
Base Rules, 基础规范,描述的是任何场合下,页面元素的默认外观。它的定义不会用到class和ID。css reset也属于此类。常见的如normalize.css,CSS Tools
如果使用Sass的Compass的话,还可以使用它的global-reset的mixin版本
@import 'compass';
@include global-reset;
关于两种css reset的选择可以参考Modular CSS typography (同时也讲到了css模块化的讨论,推荐看一看)
这里样式只会对标签元素本身做设定,不会出现任何 class 或id,但是可以有属性选择器或是伪类.
Layout Rules, 布局规范,元素是有层次级别之分的,Layout Rules属于较高的一层,它可以作为层级较低的Module Rules元素的容器。左右分栏、栅格系统等都属于布局规范。布局是一个网站的基本,无论是左右还是居中,甚至其他什么布局,要实现页面的基本浏览功能,布局必不可少。SMACSS还约定了一个前缀l-/layout-来标识布局的class。举个最普遍的例子。
.layout-header {}
.layout-container {}
.layout-sidebar {}
.layout-content {}
.layout-footer {}
Module Rules, 模块规范,模块是SMACSS最基本的思想,同时也是大部分CSS理论的基本,将样式模块化就能达到复用和可维护的目的,但是SMACSS提出了更具体的模块化方案。SMACSS中的模块具有自己的一个命名,隶属于模块下的类皆以该模块为前缀,例子如下:
.todolist{}
.todolist-title{}
.todolist-image{}
.todolist-article{}
可以看到todolist作为一个模块,包含了title,image,article等组件,同时还可以加上如.todolist-background-danger
等修饰类,在模块内可以使用其名称做前缀任意组织模块结构,但目的是让其变得更易用,提高可扩展性和灵活度,如果只是为了修饰而修饰,写出大量没有任何复用性的类,便是一种弄巧成拙的做法。
State Rules, 状态规范,这个应该很多前端开发者都很好理解,描述的是任一元素在特定状态下的外观。例如,一个消息框可能有success和error等状态。与OOCSS抽取修饰类的方式的不同,SMACSS是抽取更高级别的样式类,得到更强的复用性,如隐藏某个元素的写法:
//html
"is-hidden">
...
//css
.is-hidden{
display: none;
}
Theme Rules,主题规范,描述了页面主题外观,一般是指颜色、背景图。Theme Rules可以修改前面4个类别的样式,且应和前面4个类别分离开来(便于切换,也就是“换肤”)。SMACSS的Theme Rules不要求使用单独的class命名,也就是说,你可以在Module Rules中定义.header{ }然后在Theme Rules中也用.header { }来定义需要修改的部分(后加载覆盖前加载样式内容)
按照前面5种的划分:
Base Rules(Pass)
Layout Rules用l-或layout-这样的前缀,例如:.l-header、.l-sidebar。
Module Rules用模块本身的命名,例如图文排列的.media、.media-image。
State Rules用is-前缀,例如:.is-active、.is-hidden。
Theme Rules如果作为单独class,用theme-前缀,例如.theme-a-background、.theme-a-shadow。
最小适配深度原则,简单的例子:
/* depth 1 */
.sidebar ul h3 { }
/* depth 2 */
.sub-title { }
两段css的区别在于html和css的耦合度(这一点上和OOCSS的分离容器和内容的原则不谋而合)。可以想到,由于上面的样式规则使用了继承选择符,因此对于html的结构实际是有一定依赖的。如果html发生重构,就有可能不再具有这些样式。对应的,下面的样式规则只有一个选择符,因此不依赖于特定html结构,只要为元素添加class,就可以获得对应样式。
当然,继承选择符是有用的,它可以减少因相同命名引发的样式冲突(常发生于多人协作开发)。但是,我们不应过度使用,在不造成样式冲突的允许范围之内,尽可能使用短的、不限定html结构的选择符。这就是SMACSS的最小化适配深度的意义。
BEM,即Block,Element,Modifier,是由Yandex(俄罗斯最著名的互联网企业)的开发团队提出的前端开发理论。BEM通过Block、Element、Modifier来描述页面(关键就是为了解决多人协作的命名问题).
Block是页面中独立存在的区块,可以在不同场合下被重用。每个页面都可以看做是多个Block组成。
Element是构成Block的元素,只有在对应Block内部才具有意义,是依赖于Block的存在。
Modifier是描述Block或Element的属性或状态。同一Block或Element可以有多个Modifier。
Block作为起始开头不同 Block 和 Element 用 __ 两个底线区隔开来,不同的 Modifier 则用 – 两个 dash 区隔。至于 - 一个 dash 则表示这个 class 不依赖任何 Block 或 Element,是个独立的存在,例如:.page-container 或 .article-wrapper。如:
.sidebar {
.sidebar--left__section {
.sidebar--left__section--header {}
.sidebar--left__section--footer {}
}
}
在用 jQuery 可以常常看到这样的写法:$(‘.nav–main a’),一旦css发生重构,javascript代码也将变得难以维护,分不清那些代码是否会受到影响.
所以用 HTML 的属性去选取 DOM 节点会更好,如果非要用 CSS 的 class 那也可以多写一个 js- 的 prefix,以表示这个节点有被 Javascript 使用,例如:
"nav--main__item js-nav--main__item">...
"nav--main__item js-nav--main__item">...
"nav--main__item js-nav--main__item">...
三种设计模式都有共同的特点,那便是让html与css更好的解耦,抽取样式中可复用的部分,使你的html代码更具语义。了解这些设计模式无疑会使你的css代码更具模块化,多人协作的时候也能有效避免那些命名问题带来的困扰又或者说提供一些解决的思路。
同时现在的我们可能很少直接去编写css,而是采取LESS或者SASS的方式编写,在通过这些高效的预编译器编写代码的时候,也同样需要合理的代码编写和组织方式,我将会在下一篇 Css设计模式-实践篇之LESS与SASS 与大家分享。