目录
黄金法则
坚持遵守以下这些(或者你自己定制的)编码准则。无论错误大小,请告诉我们。如果您想对本【编码指南】进行补充或贡献,请 在 GitHub 上新开一个 issue。
无论有多少参与者,每一行代码都应该像是由一个人所写。
HTML
HTML 语法
- HTML 标签不要大写,doctype 声明不能少。
- 使用软制表符(也就是两个空格)缩进代码,因为只有这样才能保证代码在任何环境下的排版效果是一致的。
- 嵌套的 HTML 标签应当缩进一次(也就是两个空格)。
- 属性要使用双引号,而不是单引号。
- 不要在自闭合的 HTML 标签结尾处添加斜线,HTML5 技术规范 规定这种斜线不是必须的。
- 不要省略可选的结束标签(例如
</li>
或</body>
)。
<!doctype html>
<html>
<head>
<title>Page title</title>
</head>
<body>
<img src="images/company-logo.png" alt="Company">
<h1 class="hello-world">Hello, world!</h1>
</body>
</html>
HTML5 doctype
在每个 HTML 页面的开头添加类似下面这样的 doctype 声明,就能让每个浏览器执行 标准模式(standards mode) 并得到一致的渲染效果。别忘了,按照前面的语法建议,HTML 标签要小写。
<!doctype html>
<html>
<head>
<!-- ... -->
</head>
<body>
<!-- ... -->
</body>
</html>
lang
属性
以下摘自 HTML5 技术规范:
我们建议为 html 根元素指定一个
lang
属性,用于设置当前文档的语言。这有助于语音合成工具确定使用何种发音,也有助于翻译工具确定使用何种规则,等等。
<html lang="en">
<!-- ... -->
</html>
IE 兼容模式
如今,除非你必须要支持 IE10 或更老版本的 IE,否则没有必要包含用于兼容 IE 的特定的 <meta>
标签了。 IE11 也已经淘汰了这些标签,并且 Microsoft Edge 浏览器(的遗留或其它版本)也不再使用这些标签了。
如需了解更多信息,请阅读 Stack Overflow 上的这篇精彩的文章。
<!-- 只针对 IE10 及更老版本 -->
<meta http-equiv="x-ua-compatible" content="ie=edge">
字符编码
通过声明明确的字符编码,从而确保正确地呈现内容。这样还可以使用常规字符代替 HTML 实体(HTML entities),例如用 —
字符代替 &emdash;
实体,前提是它们的编码与文档的编码相匹配。对于 XML 保留的字符,例如 ampersand、non-breaking spaces、less/greater than 以及 quotes 字符,你仍然需要使用 HTML 实体。
推荐使用 UTF-8 编码。
<head>
<meta charset="utf-8">
</head>
<body>
<p>Use an em dash like so—no HTML entity required.</p>
</body>
加载 CSS 和 JavaScript 文件
根据 HTML5 技术规范,在加载 CSS 和 JavaScript 文件时通常无需通过 type
属性标注文件类型,因为 text/css
和 text/javascript
是它们各自的默认类型。
HTML5 技术规范相关链接
<!-- 外部 CSS 文件 -->
<link rel="stylesheet" href="code-guide.css">
<!-- 文档内联 CSS 代码 -->
<style>
/* ... */
</style>
<!-- JavaScript -->
<script src="code-guide.js"></script>
实用性高于纯粹性
力求合乎 HTML 标准和语义,但不能以牺牲实用性为代价。尽可能使用最少量的、最直接的 HTML 标签。
<!-- 好 -->
<button>...</button>
<!-- 不好 -->
<div class="btn" onClick="...">...</div>
属性的顺序
HTML 属性应按照以下顺序排列,以便于阅读代码。
class
id
、name
data-*
src
、for
、type
、href
、value
title
、alt
role
、aria-*
tabindex
style
最常用于识别 HTML 元素的属性应当排在第一位,比如 class
、id
、name
和 data
属性。特定 HTML 元素所独有的属性排在第二位,其次是与可访问性(accessibility)和样式相关的属性。
<a class="..." id="..." data-toggle="modal" href="#">
Example link
</a>
<input class="form-control" type="text">
<img src="..." alt="...">
布尔(Boolean)类型的属性
布尔类型的属性是不需要赋值的。XHTML 要求赋值,但 HTML5 没有这样的要求。
如需了解更多信息,请查阅 WhatWG 中关于布尔(boolean)类型属性的部分
如果布尔(boolean)类型的属性存在于 HTML 元素上则表示其值为 true,否则其值为 false。
如果 必须 为属性赋值,但 实际上不需要明确赋值,请遵循 WhatWG 的指导原则:
如果属性存在,其值必须是空字符串或属性的规范名称,并且前后不能有空白。
总而言之,不要为其赋值。
<input type="text" disabled>
<input type="checkbox" value="1" checked>
<select>
<option value="1" selected>1</option>
</select>
减少 HTML 元素
在编写 HTML 代码时,尽量避免多余的父元素。很多时候,这需要不断地迭代和重构,但能减少输出的 HTML 代码。
<!-- 不太好的示例 -->
<span class="avatar">
<img src="...">
</span>
<!-- 更好的示例 -->
<img class="avatar" src="...">
编辑器偏好设置
按照以下参数设置编辑器,以避免常见的代码不一致和混乱的差异比对:
- 使用软制表符(也就是两个空格)。
- 保存时去除尾部的空白符。
- 设置为 UTF-8 编码。
- 在文件末尾添加新行。
建议在项目的 .editorconfig
配置文件中记录并使用上述设置。有关示例,请参考 Bootstrap 项目的配置文件。了解有关 EditorConfig 的更多信息。
CSS
CSS 语法
- 使用软制表符(也就是两个空格),因为只有这样才能保证代码在任何环境下的渲染效果是一致的。
- 当多个选择器成组出现时,请让每个选择器独自成行。
- 为清晰起见,在属性声明块的起始花括号前保留一个空格。
- 属性声明块结尾处的花括号应独自成行。
- 在每条属性声明的
:
后添加一个空格符。 - 每条属性声明都应独自成行,以便获得更精准的错误报告。
- 所有属性声明都以分号结束。最后一条属性声明后面的分号可有可无,但是没有的话,代码会更容易出错。
- 以逗号分隔的属性值应在每个逗号后面添加一个空格符(例如: ,
box-shadow
)。 - 颜色属性的各个分量值之间用空格符分隔(例如:
color: rgb(0 0 0 / .5)
)。请参考“颜色”章节 - 不要在属性值或颜色的参数前加零(例如,用
.5
替代0.5
,用-.5px
替代-0.5px
)。 - 小写所有十六进制的值,例如
#fff
。 小写字母在快速浏览代码时更容易辨认,因为它们往往具有更独特的形状。 - 尽可能使用简写的十六进制值,例如,用
#fff
代替#ffffff
。 - 给选择器中的属性值加引号,例如
input[type="text"]
。虽然在某行情况下不是必须的,但这是保持一致性的好习惯。 - 避免为零值添加单位。例如,用
margin: 0;
替代margin: 0px;
。
如果对此处使用的术语有疑问,请参阅 Wikipedia 上的 Cascading Style Sheets 文章 中的语法部分。
// 不好的 CSS 代码
.selector, .selector-secondary, .selector[type=text] {
padding:15px;
margin:0px 0px 15px;
background-color:rgba(0, 0, 0, 0.5);
box-shadow:0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF
}
// 好的 CSS 代码
.selector,
.selector-secondary,
.selector[type="text"] {
padding: 15px;
margin-bottom: 15px;
background-color: rgb(0 0 0 / .5);
box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}
属性声明的顺序
属性声明应按照以下顺序分组书写:
- 定位
- 盒模型
- 排版
- 视觉效果
- 其它
定位排在第一位,因为它可以将元素从正常的文档流中移除并覆盖盒模型的相关样式。盒模型(无论是 flex、float、grid 或 table)紧随其后,因为它决定了组件的尺寸、位置和对齐方式。所有其它部分影响的都是组件 内部,或者根本不影响前两个部分,因此排在最后。
虽然 border
属性是盒模型的一部分,但大多数系统中会在全局将 box-sizing
属性重置为 border-box
值,因此 border-width
属性就不会影响整体的尺寸了。这(将 border
属性放在 border-radius
属性附近)就是为什么将其放在“视觉”分组中的原因了。
预处理器的 mixin 和函数应使用在最合适的地方。例如,border-top-radius()
这个 mixin 应当取代 border-radius
属性,而 responsive-font-size()
函数应取代 font-size
属性。
关于属性及其顺序的完整列表,请参阅 Bootstrap 所使用的 property order for Stylelint 。
.declaration-order {
// 定位
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
// 盒模型
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100px;
height: 100px;
// 排版
font: normal 14px "Helvetica Neue", sans-serif;
line-height: 1.5;
color: #333;
text-align: center;
text-decoration: underline;
// 视觉效果
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
border-radius: 3px;
// 其它
opacity: 1;
}
逻辑属性
逻辑属性是基于抽象术语(如 block 和 inline)的属性,是方向和尺寸属性的替代属性。默认情况下,block 指的是垂直方向(顶部和底部),而 inline 指的是水平方向(右侧和左侧)。你可以在所有现代浏览器中使用这些 CSS 属性。
为什么要使用逻辑属性? 并不是每种语言都能像英语一样从左到右书写,因此书写模式要有灵活性。有了逻辑属性,你就可以轻松支持横向书写或竖向书写的语言(例如中文、日文和韩文)了。此外,这些语言通常篇幅较短,书写简单。
延申阅读:
// 不使用逻辑属性
.element {
margin-right: auto;
margin-left: auto;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
// 使用逻辑属性
.element {
margin-inline: auto;
border-block: 1px solid #eee;
}
颜色
随着 所有主流浏览器 都提供了对 CSS Color Levels 4 的支持,rgba()
和 hsla()
已经分别成为 rgb()
的 hsl()
的别名了,也就是说你可以在 rgb()
和 hsl()
中修改 alpha 值了。同时,表示颜色值的新语法(以空格分割各个分量)也已经被支持了。为了与未来的 CSS 颜色函数兼容,请使用这种新语法。
无论您使用何种颜色值和语法,请务必确保您选择的颜色符合 WCAG 最低对比度 (4.5:1 for 16px and smaller, 3:1 for larger)。
延申阅读:
.element {
color: rgb(255 255 255 / .65);
background-color: rgb(0 0 0 / .95);
}
避免使用 @import
与 <link>
元素相比,@import
速度较慢,并且会增加额外的页面请求,还可能导致其它不可预见的问题。因此,请避免使用或选择其他方式:
更多信息请参阅 Steve Souders 的这篇文章。
<!-- 使用 <link> 元素 -->
<link rel="stylesheet" href="core.css">
<!-- 避免使用 @import -->
<style>
@import url("more.css");
</style>
媒体查询的放置位置
尽可能将媒体查询与相关的规则集放在一起。不要将媒体查询集中放在独立的样式表中或者文件的末尾。分开放置只会让同事将来更容易忽略它们。以下是一段典型的代码示例。
.element { ... }
.element-avatar { ... }
.element-selected { ... }
@media (min-width: 480px) {
.element { ... }
.element-avatar { ... }
.element-selected { ... }
}
每条属性声明应独自成行
如果规则集中 只包含一条属性声明,可以考虑删除换行符,以提高代码的可读性和编辑速度。任何包含多条属性声明的规则集中,每条属性声明都应独自成行。
这里的关键考虑是错误检测。例如,CSS 验证器会指出你在第 183 行出现了语法错误。如果是每条属性声明独自成行,就很容找到。如果多条属性声明都挤在一行的话,将其分成多行是明智的选择。
// 一条属性声明占一行
.span1 { width: 60px; }
.span2 { width: 140px; }
.span3 { width: 220px; }
// 多条属性声明时,每行一条
.sprite {
display: inline-block;
width: 16px;
height: 15px;
background-image: url("../img/sprite.png");
}
.icon { background-position: 0 0; }
.icon-home { background-position: 0 -20px; }
.icon-account { background-position: 0 -40px; }
属性的简写形式
将属性的简写形式限制在必须明确填充所有属性值的情况下。以下属性的简写形式经常被过度使用:
padding
margin
font
background
border
border-radius
通常,我们不需要为属性的简写形式填充所有属性值。例如,HTML 的标题元素通常只需要设置上边距和下边距(top and bottom margin)即可,因此,必要时只需覆盖这两个值即可。0
值则意味着不仅覆盖浏览器的默认值,还覆盖了先前的代码所赋予的值。
过度使用属性的简写形式会导致代码的表达不流畅,产生属性值被不必要的覆盖以及意想不到的副作用。
Mozilla Developer Network 上有一篇关于 shorthand properties 的精彩文章供大家参考。
// 不好的示例
.element {
margin: 0 0 10px;
background: red;
background: url("image.jpg");
border-radius: 3px 3px 0 0;
}
// 好的示例
.element {
margin-bottom: 10px;
background-color: red;
background-image: url("image.jpg");
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
利用预处理器实现嵌套
尽可能避免利用预处理器进行不必要的嵌套,尽量保持简单并避免反向嵌套。只有在必须要将样式的作用域限定在父元素内,并且有多个元素需要嵌套时,才考虑使用嵌套。
延伸阅读:
// 没有嵌套
.table > thead > tr > th { … }
.table > thead > tr > td { … }
// 有嵌套
.table > thead > tr {
> th { … }
> td { … }
}
使用预处理器提供的运算符
为了提高代码的可读性,请将所有数学运算用括号括起来,并在赋值(values)、变量(variables)和运算符(operators)之间留一个空格。
// 不好的示例
.element {
margin: 10px 0 @variable*2 10px;
}
// 好的示例
.element {
margin: 10px 0 (@variable * 2) 10px;
}
注释
代码是由人来编写和维护的。请确保你的代码描述清晰、注释良好,并且让别人容易理解。良好的代码注释能够传达上下文或目的。不要简单地复制一遍组件或类的名字。使用预处理器编写 CSS 时使用 //
添加注释。当 CSS 被交付到生产环境时,删除所有注释。
对于较长的注释,请务必书写完整的语句;对于一般的注释,请使用简洁的短语。
// 不好的示例
// Modal header
.modal-header {
...
}
// 好的示例
// Wrapping element for .modal-title and .modal-close
.modal-header {
...
}
class 的命名
- 保持 class 名称小写并使用破折号(不是下划线或 camelCase 驼峰命名方式)。破折号是相关 class(例如
.btn
和.btn-danger
相关)的自然分隔符。 - 避免过多且随意地使用缩写形式。例如,用
.btn
代表 button,而.s
则无法推测出任何含义。 - 尽量让 class 名称保持简短且言简意赅。
- 使用有意义的名称;使用结构化或代表目的的名称,不要使用所呈现的样式来命名。
- 根据最近的父元素的 class 名称或基础 class 的名称添加前缀。
- 使用
.js-*
形式的 class 名称来表示行为(而不是样式),并且不要在 CSS 中使用这些 class。
当创建自定义属性和预处理器变量时,遵守上述规则也很有用。
// 不好的示例
.t { ... }
.red { ... }
.header { ... }
// 好的示例
.tweet { ... }
.important { ... }
.tweet-header { ... }
选择器
- 使用 class 而不是普通的 HTML 标签,可以获得更明确、更可靠的样式,而不受 HTML 标签变化的影响。
- 避免在常出现的组件上使用多个属性选择器(例如
[class^="..."]
),因为浏览器的性能会受这些选择器的影响。 - 选择器要简短,并尽量将选择器所涉及的元素数量限制在三个以内。
只有
在必要时(例如,不使用带前缀的 class 时)才将 class 的作用域限制在最近的父元素内。
延申阅读:
// 不好的示例
span { ... }
.page-container #stream .stream-item .tweet .tweet-header .username { ... }
.avatar { ... }
// 好的示例
.avatar { ... }
.tweet-header .username { ... }
.tweet .avatar { ... }
子代和后代选择器
必要时,使用 子代选择器 (>
) 来限制某些样式的作用域是很有用的,例如 <table>
元素在使用时经常需要递归嵌套。使用子代选择器能够将样式限制在父元素的直接子元素范围内,以避免将来产生不必要的样式覆盖。
.custom-table > tbody > tr > td,
.custom-table > tbody > tr > th {
/* ... */
}
代码的组织结构
- 按组件将代码分块。
- 为注释建立一致的层次结构。
- 使用一致的空白来分隔代码块,当代码量很大时,这样做有利于快速浏览。
- 当使用多个 CSS 文件时,按组件而不是按页面来划分。因为页面可能被重新排列,而组件就会被挪动。
//
// Component section heading
//
.element { ... }
//
// Component section heading
//
// Sometimes you need to include optional context for the entire component. Do that up here if it's important enough.
//
.element { ... }
// Contextual sub-component or modifer
.element-heading { ... }