在参与规模庞大、历时漫长且参与人数众多的项目时,所有开发者遵守如下规则极为重要:
为了实现这一目标,我们要采用诸多方法。
本文档第一部分将探讨语法、格式以及分析 CSS 结构;第二部分将围绕方法论、思维框架以及编写与规划 CSS 的看法。
无论编写什么文档,我们都应当维持统一的风格,包括统一的注释、统一的语法与统一的命名规范。
将行宽控制在 80 字节以下。渐变(gradient)相关的语法以及注释中的 URL 等可以算作例外,毕竟这部分我们也无能为力。
我倾向于用 4 个空格而非 Tab 缩进,并且将声明拆分成多行。
有人喜欢在一份文件文件中编写所有的内容,而我在迁移至 Sass 之后开始将样式拆分成多个小文件。这都是很好的做法。无论你选择哪种,下文的规则都将适用,而且如果你遵守这些规则的话你也不会遇到什么问题。这两种写法的区别仅仅在于目录以及区块标题:
在 CSS 的开头,我会维护一份目录,就像这样:
/*------------------------------------*/ $CONTENTS/*------------------------------------*//** * CONTENTS............You’re reading it! * RESET...............Set our reset defaults * FONT-FACE...........Import brand font files */
这份目录可以告诉其他开发者这个文件中具体含有哪些内容。这份目录中的每一项都与其对应的区块标题相同。
如果你在维护一份单文件 CSS,对应的区块将也在同一文件中。如果你是在编写一组小文件,那么目录中的每一项应当对应相应的 @include 语句。
目录应当对应区块的标题。如下:
/*------------------------------------*/ $RESET/*------------------------------------*/
区块标题前缀 $ 可以让我们使用 [Cmd|Ctrl]+F 命令查找标题名时将搜索范围限制在区块标题中。
如果你在维护一份大文件,那么在区块之间空 5 行,如下:
/*------------------------------------*/ $RESET/*------------------------------------*/[Ourresetstyles]/*------------------------------------*/ $FONT-FACE/*------------------------------------*/
在大文件中快速翻动时这些大块的空档有助于区分区块。
如果你在维护多份以 include 连接的 CSS 的话,在每份文件头加上标题即可,不必这样空行。
尽量按照特定顺序编写规则,这将确保你充分发挥 CSS 中第一个 C 的意义:cascade,层叠。
一份规划良好的 CSS 应当按照如下排列:
如此一来,当你依次编写 CSS 时,每个区块都可以自动继承在它之前区块的属性。这样就可以减少代码相互抵消的部分,减少某些特殊的问题,组成更理想的 CSS 结构。
关于这方面的更多信息,强烈推荐 Jonathan Snook 的SMACSS。
[selector]{ [PRoperty]:[value]; [<- Declaration ->]}[选择器]{ [属性]:[值]; [<- 声明 ->]}
编写 CSS 样式时,我习惯遵守这些规则:
例如:
.widget{ padding:10px; border:1px solid #BADA55; background-color:#C0FFEE; -webkit-border-radius:4px; -moz-border-radius:4px; border-radius:4px;} .widget-heading{ font-size:1.5rem; line-height:1; font-weight:bold; color:#BADA55; margin-right:-10px; margin-left: -10px; padding:0.25em; }
我们可以发现,.widget-heading 是 .widget 的子元素,因为前者的样式集比后者多缩进了一级。这样通过缩进就可以让开发者在阅读代码时快速获取这样的重要信息。
我们还可以发现 .widget-heading 的声明是根据其相关性排列的:.widget-heading 是行间元素,所以我们先添加字体相关的样式声明,接下来是其它的。
以下是一个没有拆分成多行的例子:
.t10 { width:10% }.t20 { width:20% }.t25 { width:25% } /* 1/4 */.t30 { width:30% }.t33 { width:33.333% } /* 1/3 */.t40 { width:40% }.t50 { width:50% } /* 1/2 */.t60 { width:60% }.t66 { width:66.666% } /* 2/3 */.t70 { width:70% }.t75 { width:75% } /* 3/4*/.t80 { width:80% }.t90 { width:90% }
在这个例子(来自inuit.css’s table grid system)中,将 CSS 放在一行内可以使得代码更紧凑。
一般情况下我都是以连字符(-)连接 class 的名字(例如 .foo-bar 而非 .foo_bar 或 .fooBar),不过在某些特定的时候我会用 BEM(Block, Element, Modifier)命名法。
BEM 命名法可以使得选择器更规范,更清晰,更具语义。
该命名法按照如下格式:
.block{}.block__element{}.block--modifier{}
其中:
打个比方:
.person{}.person--woman{} .person__hand{} .person__hand--left{} .person__hand--right{}
这个例子中我们描述的基本元素是一个人,然后这个人可能是一个女人。我们还知道人拥有手,这些是人体的一部分,而手也有不同的状态,如同左手与右手。
这样我们就可以根据亲元素来划定选择器的命名空间并传达该选择器的职能,例如根据这个选择器是一个子元素(__)还是其亲元素的不同状态(--)。
由此,.page-wrapper 是一个独立的选择器。这是一个符合规范的命名,因为它不是其它元素的子元素或其它状态;然而 .widget-heading 则与其它对象有关联,它应当是 .widget 的子元素,所以我们应当将其重命名为 .widget__heading。
BEM 命名法虽然不太好看,而且相当冗长,但是它使得我们可以通过名称快速获知元素的功能和元素之间的关系。与此同时,BEM 语法中的重复部分非常有利于 gzip 的压缩算法。
无论你是否使用 BEM 命名法,你都应当确保 class 命名得当,力保一字不多、一字不少;将元素命名抽象化以提高复用性(例如 .ui-list,.media)。子元素的命名则要尽量精准(例如 .user-avatar-link)。不用担心 class 名的数量或长度,因为写得好的代码 gzip 也能有效压缩。
为了确保易读性,在 HTML 标记中用两个空格隔开 class 名,例如:
<div class="foo--bar bar__baz">
增加的空格应当可以使得在使用多个 class 时更易阅读与定位。
切勿将标记 CSS 样式的 class 用作 Javascript 钩子。把 JS 行为与样式混在一起将无法对其分别处理。
如果你要把 JS 和某些标记绑定起来的话,写一个 JS 专用的 class。简单地说就是划定一个前缀 .js- 的命名空间,例如 .js-toggle,.js-drag-and-drop。这意味着我们可以通过 class 同时绑定 JS 和 CSS 而不会因为冲突而引发麻烦。
<th class="is-sortable js-is-sortable"></th>
上面的这个标记有两个 class,你可以用其中一个来给这个可排序的表格栏添加样式,用另一个添加排序功能。
虽然我(该 CSS Guideline 文档原作者 Harry Roberts)是个英国人,而且我一向拼写 colour 而非 color,但是为了追求统一,我认为在 CSS 中用美式拼法更佳。CSS 以及其它多数语言都是以美式拼法编写,所以如果在 .colour-picker{} 中写 color:red 就缺乏统一性。我以前主张同时用两种拼法,例如:
.color-picker,.colour-picker{}
但是我最近参与了一份规模庞大的 Sass 项目,这个项目中有许多的颜色变量(例如 $brand-color,$highlight-color 等等),每个变量要维护两种拼法实在辛苦,要查找并替换时也需要两倍的工作量。
所以为了统一,把所有的 class 与变量都以你参与的项目的惯用拼法命名即可。
我使用行宽不超过 80 字节的文档块风格注释:
/** * This is a docBlock style comment * * This is a longer description of the comment, describing the code in more * detail. We limit these lines to a maximum of 80 characters in length. * * We can have markup in the comments, and are encouraged to do so: * <div class="foo"> <p>Lorem</p> </div> * * We do not prefix lines of code with an asterisk as to do so would inhibit * copy and paste. *//** * 这是一个文档块(DocBlock)风格的注释。 * * 这里开始是描述更详细、篇幅更长的注释正文。当然,我们要把行宽控制在 80 字节以内。 * * 我们可以在注释中嵌入 HTML 标记,而且这也是个不错的办法: * <div class="foo"> <p>Lorem</p> </div> * * 如果是注释内嵌的标记的话,在它前面不加星号,以免被复制进去。 */
在注释中应当尽量详细描述代码,因为对你来说清晰易懂的内容对其他人可能并非如此。每写一部分代码就要专门写注释以详解。
注释有许多很高级的用法,例如:
你应当避免过分修饰选择器,例如如果你能写 .nav{} 就尽量不要写 ul.nav{}。过分修饰选择器将影响性能,影响 class 复用性,增加选择器私有度。这些都是你应当竭力避免的。
不过有时你可能希望告诉其他开发者 class 的使用范围。以 .product-page 为例,这个 class 看起来像是一个根容器,可能是 html 或者 body 元素,但是仅凭 .product-page 则无法判断。
我们可以在选择器前加上准修饰(即将前面的类型选择器注释掉)来描述我们规划的 class 作用范围:
/*html*/.product-page{}
这样我们就能准确获知该 class 的作用范围而不会影响复用性。
其它例子如:
/*ol*/.breadcrumb{}/*p*/.intro{}/*ul*/.image-thumbs{}
这样我们就能在不影响代码私有度的前提下获知 class 作用范围。
如果你写了一组新样式的话,可以在它上面加上标签,例如:
/** * ^navigation ^lists */.nav{}/** * ^grids ^lists ^tables */.matrix{}
这些标签可以使得其他开发者快速找到相关代码。如果一个开发者需要查找和列表相关的部分,他只要搜索 ^lists 就能快速定位到 .nav,.matrix 以及其它相关部分。
将面向对象的思路用于 CSS 编写的话,你经常能找到两部分 CSS 密切相关(其一为基础,其一为拓展)却分列两处。我们可以用继承标记来在原元素和继承元素之间建立紧密联系。这些在注释中的写法如下:
在元素的基本样式中:
/** * Extend `.foo` in theme.css */ .foo{}
在元素的拓展样式中:
/** * Extends `.foo` in base.css */ .bar{}
这样一来我们就能在两块相隔很远的代码间建立紧密联系。
之前的章节主要探讨如何规划 CSS,这些都是易于量化的规则。本章将探讨更理论化的东西,也将探讨我们的态度与方法。
编写新组件时,要在着手处理 CSS 之前写好 HTML 部分。这可以令你准确判断哪些 CSS 属性可以继承,避免重复浪费。
先写标记的话,你就可以关注数据、内容与语义,在这之后再添加需要的 class 和 CSS 样式。
我以面向对象 CSS 的方式写代码。我把组件分成结构(对象)与外观(拓展)。正如以下分析(注意此处并非示例):
.room{}.room--kitchen{}.room--bedroom{}.room--bathroom{}
我们在屋子里有许多房间,它们都有共同的部分:地板、天花板、墙壁和门。这些共享的部分我们可以放到一个抽象的 .room{} class 中。不过我们还有其它与众不同的房间:一个厨房可能有地砖,卧室可能有地毯,洗手间可能没有窗户但是卧室会有,每个房间的墙壁颜色也许也会不一样。面向对 象 CSS 的思路使得我们把相同部分抽象出来组成结构部分,然后用更具体的 class 来拓展这些特征并添加特殊的处理方法。
所以比起编写大量各自不同的模块,应当努力找出这些模块中重复的设计模式并将其抽象出来,写成一个可以复用的 class,将其用作基础然后编写其它拓展模块的特殊情形。
当你要编写一个新组件时,将其拆分成结构和外观。编写结构部分时用最通用 class 以保证复用性,编写外观时用更具体的 class 来添加设计方法。
所有组件都不要声明宽度,而由其亲元素或格栅系统来决定。
坚
新闻热点
疑难解答