在参与规模庞大、历时漫长且参与人数众多的项目时,全部开发者遵照以下规则极为重要:css
为了实现这一目标,咱们要采用诸多方法。html
本文档第一部分将探讨语法、格式以及分析 CSS 结构;第二部分将围绕方法论、思惟框架以及编写与规划 CSS 的见解。前端
不管编写什么文档,咱们都应当维持统一的风格,包括统一的注释、统一的语法与统一的命名规范。git
将行宽控制在 80 字节如下。渐变(gradient)相关的语法以及注释中的 URL 等能够算做例外,毕竟这部分咱们也无能为力。github
我倾向于用 4 个空格而非 Tab 缩进,而且将声明拆分红多行。web
有人喜欢在一份文件文件中编写全部的内容,而我在迁移至 Sass 以后开始将样式拆分红多个小文件。这都是很好的作法。不管你选择哪一种,下文的规则都将适用,并且若是你遵照这些规则的话你也不会遇到什么问题。这两种写法的区别仅仅在于目录以及区块标题:算法
在 CSS 的开头,我会维护一份目录,就像这样:设计模式
/*------------------------------------*\ $CONTENTS \*------------------------------------*/ /** * CONTENTS............You’re reading it! * RESET...............Set our reset defaults * FONT-FACE...........Import brand font files */
这份目录能够告诉其余开发者这个文件中具体含有哪些内容。这份目录中的每一项都与其对应的区块标题相同。浏览器
若是你在维护一份单文件 CSS,对应的区块将也在同一文件中。若是你是在编写一组小文件,那么目录中的每一项应当对应相应的 @include 语句。app
目录应当对应区块的标题。以下:
/*------------------------------------*\ $RESET \*------------------------------------*/
区块标题前缀 $ 可让咱们使用 [Cmd|Ctrl]+F 命令查找标题名时将搜索范围限制在区块标题中。
若是你在维护一份大文件,那么在区块之间空 5 行,以下:
/*------------------------------------*\ $RESET \*------------------------------------*/ [Our reset styles] /*------------------------------------*\ $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 来添加设计方法。
全部组件都不要声明宽度,而由其亲元素或格栅系统来决定。
坚定不要声明高度。高度应当仅仅用于尺寸已经固定的东西,例如图片和 CSS Sprite。在 p,ul,div 等元素上不该当声明高度。若是须要的话可使用更加灵活的 line-height。
格栅系统应当看成书架来理解。是它们容纳内容,而不是把它们自己当成内容装起来,正如你先搭起书架再把东西放进去。比起声明它们的尺寸,把格栅系统和元素的其它属性分来开处理更有助于布局,也使得咱们的前端工做更高效。
你在格栅系统上不该当添加任何样式,它们仅仅是为布局而用。在格栅系统内部再添加样式。在格栅系统中任何状况下都不要添加盒模型相关属性。
我用不少方法设定 UI 尺寸,包括百分比,px,em,rem 以及干脆什么都不用。
理想状况下,格栅系统应当用百分比设定。如上所述,由于我用格栅系统来固定栏宽和页宽,因此我能够不用理会元素的尺寸。
我用 rem 定义字号,而且辅以 px 以兼容旧浏览器。这能够兼具 em 和 px 的优点。下面是一个很是漂亮的 Sass Mixin,假设你在别处声明了基本字号(base-font-size)的话,用它就能够生成 rem 以及兼容旧浏览器的 px。
@mixin font-size($font-size){ font-size:$font-size +px; font-size:$font-size / $base-font-size +rem; }
我只在已经固定尺寸的元素上使用 px,包括图片以及尺寸已经用 px 固定的 CSS Sprite。
我会定义一些与格栅系统原理相似的 class 来声明字号。这些 class 能够用于双重标题分级,关于这点请阅读 Pragmatic, practical font-sizing in CSS。
CSS 简写应当谨慎使用。
编写像 background: red; 这样的属性的确很省事,可是你这么写的意思实际上是同时声明 background-image: none; background-position: top left; background-repeat: repeat; 。虽然大多数时候这样不会出什么问题,可是哪怕只出一次问题就值得考虑要不要放弃简写了。这里应当改成 background-color: red;。
相似的,像 margin: 0; 这样的声明的确简洁清爽,可是仍是应当尽可能写清楚。若是你只是想修改底边边距,就要具体一些,写成 margin-bottom: 0;。
与此同时你须要声明的属性也要写清楚,不要由于简写而波及其它属性。例如若是你只想改掉底部的 margin,那就不要用会把其它边距也清零的 margin: 0。
简写虽然好,但也很容易滥用。
在咱们开始处理选择器以前,牢记这句话:
在 CSS 里坚定不要用 ID。
在 HTML 里 ID 能够用于 JS 以及锚点定位,可是在 CSS 里只要用 class,一个 ID 也不要用。
Class 的优点在于复用性,并且私有度也并不高。在项目中私有度很是容易致使问题,因此将其下降就尤其重要。ID 的私有度是 class 的 255 倍,因此在 CSS 中坚定不要使用。
务必保持选择器简短高效。
经过页面元素位置而定位的选择器并不理想。例如 .sidebar h3 span{} 这样的选择器就是定位过于依赖相对位置,若是把 span 移到 h3 和 sidebar 外面时就很难保持其样式。
结构复杂的选择器将会影响性能。选择器结构越复杂(如 .sidebar h3 span 为三层,.content ul p a 是四层),浏览器的开销就越大。
尽可能使得样式不依赖于其定位,尽可能保持选择器简洁清晰。
做为一个总体,选择器应当尽可能简短(例如只有一层结构),可是 class 名则不该当过于简略,例如 .user-avatar 就远比 .usr-avt 好。
牢记:class 无所谓是否语义化;应当关注它们是否合理。不要强调 class 名要符合语义,而要注重使用合理且不会过期的名称。
由前文所述,过分修饰的选择器并不理想。
过分修饰的选择器是指像 div.promo 这样的。极可能你只用 .promo 也能获得相同的效果。固然你可能偶尔会须要用元素类型来修饰 class(例如你写了一个 .error 并且想让它在不一样的元素类型中显示效果不同,例如 .error { color: red; } div.error { padding: 14px; }),可是大多数时候仍是应当尽可能避免。
再举一个修饰过分的选择器例子,ul.nav li a{}。如前文所说,咱们立刻就能够删掉 ul 由于咱们知道 .nav 是个列表,而后咱们就能够发现 a 必定在 li 中,因此咱们就能将这个选择器改写成 .nav a{}。
虽然浏览器性能日渐提高,渲染 CSS 速度愈来愈快,可是你仍是应当关注效率。使用简短、没有嵌套的选择器,不使用全局选择器(* {})做为核心选择器,避免使用日渐复杂的 CSS3 新选择器能够避免这样的问题。
译注,核心选择器:浏览器解析选择器为从右向左的顺序,最右端的元素是样式生效的元素,是为核心选择器。
比起努力运用选择器定位到某元素,更好的办法是给你想要添加样式的元素直接添加一个 class。咱们以 .header ul {} 这样一个选择器为例。
假定这个 ul 就是这个网站的全站导航,它位于 header 中,并且目前为止是 header 中惟一的 ul 元素。.header ul{} 的确能够生效,可是这样并非好方法,它很容易过期,并且很是晦涩。若是咱们在 header 中再添加一个 ul 的话,它就会套用咱们给这个导航部分写的样式,哪怕咱们设想的不是这个效果。这意味着咱们要么要重构许多代码,要么给后面的 ul 新写许多样式来抵消以前的影响。
你的选择器必须符合你要给这个元素添加样式的缘由。思考一下,「我定位到这个元素,是由于它是 .header 下的 ul,仍是由于它是个人网站导航?」这将决定你应当如何使用选择器。
确保你的核心选择器不是类型选择器,也不是高级对象或抽象选择器。例如你在咱们的 CSS 中确定找不到诸如 .sidebar ul {} 或者 .footer .media {} 这样的选择器。
表达清晰:直接找到你要添加样式的元素,而非其亲元素。不要想固然地认为 HTML 不会改变。用 CSS 直接命中你须要的元素,而非投机取巧。
完整内容请参考个人文章 Shoot to kill; CSS selector intent
只在起辅助做用的 class 上用 !important。用 !important 提高优先级也能够,例如若是你要让某条规则一直生效的话,能够用 .error { color:red!important; }。
避免主动使用 !important。例如 CSS 写得很复杂时不要用它来取巧,要好好整理并重构以前的部分,保持选择器简短而且避免用 ID 将效果拔群。
魔数(Magic Number)是指那些「凑巧有效果」的数字,使用魔数很是很差,由于它们只是治标不治本并且缺少拓展性。
例如使用 .dropdown-nav li:hover ul { top: 37px; } 把下拉菜单移动下来远非良策,由于这里的 37px 就是个魔数。37px 会生效的缘由是由于这时 .dropbox-nav 碰巧高 37px 而已。
这时你应该用 .dropdown-nav li:hover ul { top: 100%; },也即不管 .dropbox-down 多高,这个下拉菜单都会往下移动 100%。
每当你要在代码中放入数字的时候,请三思而行。若是你能用一个关键字(例如 top: 100% 意即「从上面拉到最下面」)替换之,或者有更好的解决方法的话,就尽可能避免直接出现数字。
你在 CSS 中留下的每个数字,都是你许下而不肯遵照的承诺。
专门为 IE 写的样式基本上都是能够避免的,惟一须要为 IE 专门处理的是为了处理 IE 不支持的内容(例如 PNG)。
简而言之,若是你重构 CSS 的话,全部的布局和盒模型都不用额外兼容 IE。也就是说你基本上不用 <!--[if IE 7]> element{ margin-left:-9px; } < ![endif]--> 或者相似的兼容 IE 的写法。
若是你要解决 CSS 问题的话,先把旧代码拿掉再写新的。若是旧的 CSS 中有问题的话,写新代码是解决不了的。
把 CSS 代码和 HTML 部分删掉,直到没有 BUG 为止,而后你就知道问题出在哪里了。
有时候写上一个 overflow: hidden 或者其它能把问题藏起来的代码的确效果立竿见影,可是 overflow 方面可能根本就没问题。因此要治本,而不是单纯治标。
我用 Sass。使用时应当灵活运用。用 Sass 能够令你的 CSS 更强大,可是不要嵌套得太复杂。在 Vanilla CSS 中,只在必要的地方用嵌套便可,例如:
.header{} .header .site-nav{} .header .site-nav li{} .header .site-nav li a{}
这样的写法在普通 CSS 里彻底用不到。如下为很差的 Sass 写法:
.header{ .site-nav{ li{ a{} } } }
若是你用 Sass 的话,尽可能这么写:
.header{} .site-nav{ li{} a{} }