CSS 编码规约
本规约涉及 CSS 及其预编译语言(Sass、Less)的编码风格和最佳实践,部分规则可通过 stylelint 工具落地。
1 CSS
1.1 编码风格
上图是一张符合规约要求编码风格的速览图,详细规则如下:
-
1.1.1
强制所有声明都应该以分号结尾,不能省略。stylelint: declaration-block-trailing-semicolon虽然 CSS 语法中最后一条声明的分号是可选的,但是使用分号可以增加代码的一致性和易用性。
/* bad */.selector {margin-top: 10px;padding-left: 15px}/* good */.selector {margin-top: 10px;padding-left: 15px;} -
1.1.2
推荐使用 2 个空格缩进,不要使用 4 个空格或 tab 缩进。stylelint: indentation/* bad */.selector {padding-left: 15px;}/* good */.selector {padding-left: 15px;} -
1.1.3
推荐选择器和{之间保留一个空格。stylelint: block-opening-brace-space-before/* bad */.selector{padding-left: 15px;}/* good */.selector {padding-left: 15px;} -
1.1.4
推荐属性名和:之前无空格,:和属性值之间保留一个空格。stylelint: declaration-colon-space-after declaration-colon-space-before/* bad */.selector {margin-top : 10px;padding-left:15px;}/* good */.selector {margin-top: 10px;padding-left: 15px;} -
1.1.5
推荐>、+、~、||等组合器前后各保留一个空格。stylelint: selector-combinator-space-before selector-combinator-space-after/* bad */.selector>.children {padding-left: 15px;}.selector+.brother {padding-left: 15px;}/* good */.selector > .children {padding-left: 15px;}.selector + .brother {padding-left: 15px;} -
1.1.6
推荐在使用,分隔的属性值中,,之后保留一个空格。stylelint: value-list-comma-space-after/* bad */.selector {background-color: rgba(0,0,0,0.5);box-shadow: 0px 1px 2px rgba(0,0,0,0.5),inset 0 1px 0 rgba(255,255,255,0.5);}/* good */.selector {background-color: rgba(0, 0, 0, 0.5);box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.5);} -
1.1.7
推荐注释内容和注释符之间留有一个空格。stylelint: comment-whitespace-inside/* bad */.selector {/*comment*//* comment *//***comment*/padding-left: 15px;}/* good */.selector {/* comment *//*** comment*/padding-left: 15px;} -
1.1.8
推荐声明块的右大括号}应单独成行。/* bad */.selector {padding-left: 15px;}/* good */.selector {padding-left: 15px;} -
1.1.9
推荐属性声明应单独成行。stylelint: declaration-block-single-line-max-declarations/* bad */.selector {padding-left: 15px; margin-left: 10px;}/* good */.selector {padding-left: 15px;margin-left: 10px;} -
1.1.10
推荐单行代码最多不要超过 100 个字符。 stylelint: max-line-length 除了以下两种情况:- 使用
url()函数时 - CSS 属性值本身无法换行时,即属性值内无空格或逗号时
/* bad */background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.04, rgb(88, 94, 124)), color-stop(0.52, rgb(115, 123, 162)));/* good */background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0.04, rgb(88, 94, 124)),color-stop(0.52, rgb(115, 123, 162))); - 使用
-
1.1.11
参考使用多个选择器时,每个选择器应该单独成行。stylelint: selector-list-comma-newline-after/* bad */.selector, .selector-secondary, .selector-third {padding: 15px;margin-bottom: 15px;}/* good */.selector,.selector-secondary,.selector-third {padding: 15px;margin-bottom: 15px;} -
1.1.12
参考声明块内只有一条语句时,也应该写成多行。/* bad */.selector { padding-left: 15px; }/* good */.selector {padding-left: 15px;} -
1.1.13
参考注释行上方需留有一行空行,除非上一行是注释或块的顶部。stylelint: comment-empty-line-before/* bad */.selector {/* comment */padding-left: 15px;/* comment */padding-right: 15px;}/* good */.selector {/* comment */padding-left: 15px;/* comment */padding-right: 15px;}
1.2 选择器
-
1.2.1
参考不要使用 id 选择器。stylelint: selector-max-idid 会带来过高的选择器优先级,使得后续很难进行样式覆盖(继而引发使用
!important覆盖样式的恶性循环)。/* bad */.normal {padding: 10px;}#special {padding: 15px;}/* good */.normal {padding: 10px;}.normal.special {padding: 15px;} -
1.2.2
参考属性选择器的值始终用双引号包裹。stylelint: selector-attribute-quotes属性选择器的值的引号只有在某些情况下可以省略。
/* bad */input[type=text] {height: 20px;}/* good */input[type="text"] {height: 20px;} -
1.2.3
参考使用 CSS 选择器时,应注意以下性能问题:- 使用 class 而不是原生元素标签
- 减少在经常出现的组件中使用个别属性选择器(如
[class^="..."]) - 控制选择器的长度,每个组合选择器内的条目尽量不超过 3 个
只从效率的角度来看,CSS 选择器从高(高效率)到低(低效率)的顺序是:
- ID 选择器, 比如
#header - 类选择器,比如
.header - 标签(元素)选择器,比如
div - 相邻兄弟选择器,比如
h2 + p - 子选择器,比如
ul > li - 后代选择器,比如
ul a - 通配符选择器,比如
* - 属性选择器,比如
[class^="grid-"] - 伪类(伪元素)选择器,比如
a:hover、a::before
1.3 属性和属性值
-
1.3.1
推荐使用尽可能短的十六进制值。stylelint: color-hex-length/* bad */.selector {color: #ffffff;}/* good */.selector {color: #fff;} -
1.3.2
推荐不要使用 !important 重写样式。 -
1.3.3
推荐十六进制值统一使用小写字母(小写字母更容易分辨)。stylelint: color-hex-case/* bad */.selector {color: #FEFEFE;}/* good */.selector {color: #fefefe;} -
1.3.4
推荐长度值为 0 时,省略掉长度单位。stylelint: length-zero-no-unit在 CSS 中,长度值为 0 时,它的单位是可选的(长度单位包括:em, ex, ch, vw, vh, cm, mm, in, pt, pc, px, rem, vmin, and vmax)。省略长度单位可以使代码更简洁:
/* bad */.selector {margin-top: 0px;font-size: 0em;}/* good */.selector {margin-top: 0;font-size: 0;} -
1.3.5
参考保留小数点前的 0。stylelint: number-leading-zero在 CSS 中,大于 -1 小于 1 的小数,小数点前的 0 可以省略:
/* bad */.selector {opacity: .5;left: -.5px;}/* good */.selector {opacity: 0.5;left: -0.5px;}对于是否省略小数点前的 0,业界存在争议:
- 省略 0 的好处是:代码更简洁,可以减少一个字符
- 不省略的好处是:代码可读性更好、一致性更强
你可选择自己倾向的风格,在代码中风格统一即可,要么都省略,要么都保留。
我们推荐保留 0,因为当今很多 CSS 压缩工具会在压缩时帮我们去掉 0,不存在多占用一个字符的问题。保留 0 能增强代码的可读性和一致性。
-
1.3.6
参考属性声明的顺序。相关联的属性声明最好写成一组,并按如下顺序排序:
- 定位:如 position、left、right、top、bottom、z-index
- 盒模型:如 display、float、width、height、margin、padding、border
- 文字排版:如 font、color、line-height、text-align
- 外观:如 background
- 其他属性
「定位」和「盒模型」放在最前面,是因为它们决定了元素的布局、位置和尺寸。「定位」排在「盒模型」之前,是由于「定位」属性可以让元素脱离正常文本流,从而使「盒模型」属性失效。
除了「定位」和「盒模型」,其他属性都只在元素内部起作用,不会对前两类属性的结果产生影响,因此放在后面。
.declaration-order {/* 定位 */position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 100;/* 盒模型 */display: block;float: right;width: 100px;height: 100px;border: 1px solid #e5e5e5;/* 排版 */font: normal 13px "Helvetica Neue", sans-serif;line-height: 1.5;color: #333;text-align: center;/* 外观 */background-color: #f5f5f5;/* 其他 */opacity: 1;}更多 CSS 属性顺序参考如下列表:
第一组 第二组 第三组 第四组 第五组 第六组 第七组 content box-sizing background* font* opacity unicode-bidi transition* position *width color src visibility direction transform* top *height box-decoration-break line-height filter columns animation* right margin* box-shadow letter-spacing resize column-* bottom padding* outline* quotes cursor break-* left border* table-layout counter-* pointer-events page-break-* z-index caption-side -ms-writing-mode user-select orphans display empty-cells text-* widows vertical-align list-style* white-space *zoom flex* word-* orientation grid* overflow-wrap fill *gap tab-size stroke align-* hyphens justify-* interpolation-mode order float clear object-fit overflow* clip -
1.3.7
参考适时使用简写属性。stylelint: declaration-block-no-shorthand-property-overrides declaration-block-no-redundant-longhand-properties常见的简写属性包括:
fontbackgroundpaddingmarginborderborder-radius
使用简写属性时,需要显式地设置所有值。我们应该在真正需要设置所有值或大多数值时才使用简写属性。
如果只是想设置某一个属性,则不应该使用简写属性:
/* bad */.selector {margin: 0 0 10px;}/* good */.selector {margin-bottom: 10px;}
1.4 其他
-
1.4.1
推荐不要使用 CSS 的 @import。与
<link>相比,@import会在关键渲染路径上增加更多的往返(即关键路径的深度变长),这样会导致浏览器处理 CSS 文件速度变慢,因此我们应该避免使用@import。<!-- bad --><style>@import url("more.css");</style><!-- good --><link rel="stylesheet" href="more.css">
2 Sass 和 Less
对于 CSS 而言,可以在新项目中尝试放弃使用 Sass、Less 这样的处理器语言,因为:
- 这些处理器语言是在一定历史条件下的产物,虽然这些产物在一定程度上提高开发者的开发效率,但不同的处理器语言也同时增加了项目的维护成本(特别是多人协作,多团队协作的时候)。
- 更建议使用 PostCSS 处理器,它类似于 CSS 中的 Babel,不但具备 Sass 和 Less 的功能,而且社区繁荣,同时还可以根据自己的需求扩展相关的插件。
- 随着 CSS 的一些新特性出现,Sass 和 Less 以往的优势也会慢慢消失。
-
2.1
推荐四则运算符两侧各保留一个空格:/* bad */.selector {width: $default-width/2;}/* good */.selector {width: $default-width / 2;} -
2.2
推荐Mixin 名称和括号()间无空格,在拥有多个参数的表达式中,,之前无空格,,之后保留一个空格:/* bad */.selector {.size(30px,20px);.clearfix ();}/* good */.selector {.size(30px, 20px);.clearfix();} -
2.3
推荐按如下顺序组织 Sass / Less 代码:@import语句- 全局变量声明
- 样式声明
@import 'common/theme.scss';$color-red: #f0f0f0;.selector {color: $color-red;} -
2.4
推荐对于 Sass 和 Less,块内的属性声明按如下顺序排序:- 标准属性声明:除了 mixin 调用、extend 子级选择器的声明,其他属性声明的顺序与「属性声明的顺序」章节的规则一致
- mixin 调用:Sass 的
@include声明、Less 的 mixin 调用 - 嵌套的子级选择器:将嵌套的选择器放到块的末尾,并且在其上方保留一行空行
.btn {background: #ccc;font-weight: bold;@include transition(background 0.5s ease);.icon {margin-right: 10px;}} -
2.5
推荐嵌套选择器的深度不要超过 3 层,否则可能带来一些副作用:- 与 HTML 结构强耦合,难以复用
- 过高的选择器优先级
.container {.header {.user-name {// STOP!不要再嵌套更深选择器}}} -
2.6
推荐可以使用双斜杠注释。但需要注意的是,编译为 CSS 后,代码中的双斜杠注释会被删除,而/* */会被保留。// 单行注释.selector-a {padding-left: 15px;}/** 多行注释* 多行注释*/.selector-b {margin-left: 15px;}编译为 CSS 后,双斜杠注释会被删除:
.selector-a {padding-left: 15px;}/** 多行注释* 多行注释*/.selector-b {margin-left: 15px;} -
2.7
推荐使用 Mixin (@mixin 和 @include 指令) 来让代码遵循 DRY 原则(Don't Repeat Yourself)、增加抽象性和降低复杂度。应避免使用 @extend 指令,它不够直观且具有潜在风险,尤其是在嵌套选择器中。即使继承的是顶层选择器,如果选择器的顺序发生变化,也可能引起问题(比如,如果它们存在于其他文件,而加载顺序发生了变化)。
Extend 相比 Mixin 的好处是,如果无参数的 mixin 被多处使用,编译后会输出多段重复的代码。这时如果使用 @extend,可以避免这个问题。但是 gzip 等压缩工具就可以解决重复代码的问题,因此大多数情况下,你只需要使用 mixin 来让代码符合 DRY 原则。
配套工具
- stylelint-config-ali:本规约配套的 stylelint 规则包