Code Guide logo

编码指南

遵守统一的编码标准,编写一致、灵活、可长期维护的 HTML 和 CSS 代码。

@mdo 撰写 · v4.0.0 · GitHub repo

目录

HTML

CSS

黄金法则

坚持遵守以下这些(或者你自己定制的)编码准则。无论错误大小,请告诉我们。如果您想对本【编码指南】进行补充或贡献,请 在 GitHub 上新开一个 issue

无论有多少参与者,每一行代码都应该像是由一个人所写。

HTML

HTML 语法

<!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 属性,用于设置当前文档的语言。这有助于语音合成工具确定使用何种发音,也有助于翻译工具确定使用何种规则,等等。

请参考 此规范 以便了解更多关于 lang 属性的信息。请打开 IANA 网站获取 语言代码列表

<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/csstext/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 属性应按照以下顺序排列,以便于阅读代码。

最常用于识别 HTML 元素的属性应当排在第一位,比如 classidnamedata 属性。特定 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="...">

编辑器偏好设置

按照以下参数设置编辑器,以避免常见的代码不一致和混乱的差异比对:

建议在项目的 .editorconfig 配置文件中记录并使用上述设置。有关示例,请参考 Bootstrap 项目的配置文件。了解有关 EditorConfig 的更多信息。

CSS

CSS 语法

如果对此处使用的术语有疑问,请参阅 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;
}

属性声明的顺序

属性声明应按照以下顺序分组书写:

  1. 定位
  2. 盒模型
  3. 排版
  4. 视觉效果
  5. 其它

定位排在第一位,因为它可以将元素从正常的文档流中移除并覆盖盒模型的相关样式。盒模型(无论是 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;
}

逻辑属性

逻辑属性是基于抽象术语(如 blockinline)的属性,是方向和尺寸属性的替代属性。默认情况下,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; }

属性的简写形式

将属性的简写形式限制在必须明确填充所有属性值的情况下。以下属性的简写形式经常被过度使用:

通常,我们不需要为属性的简写形式填充所有属性值。例如,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 的命名

当创建自定义属性和预处理器变量时,遵守上述规则也很有用。

// 不好的示例
.t { ... }
.red { ... }
.header { ... }

// 好的示例
.tweet { ... }
.important { ... }
.tweet-header { ... }

选择器

延申阅读:

// 不好的示例
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 {
  /* ... */
}

代码的组织结构

//
// 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 { ... }