程式碼準則 by @mdo

開發出靈活、穩定、可持續發展的 HTML 與 CSS 標準。

目錄

HTML

CSS

不二法門

不管是下面的準則或您自己的準則,只要用同樣的準則即可。如有任何不對之處,請不吝指教。若想新增或貢獻內容,請至 GitHub 上提 Issue

不管有多少個貢獻者,每行程式碼都應該像是同一人所寫。

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,來啟用標準模式。每個瀏覽器將會有更加一致的 render 結果。

<!DOCTYPE html>
<html>
  <head>
  </head>
</html>

字元編碼

明確宣告字元編碼來快速簡單地確保內容算繪出來正確無誤。

<head>
  <meta charset="UTF-8">
</head>

引用 CSS 與 JavaScript

根據 HTML5 規範,引用 JavaScript 與 CSS 檔案時,無需指定 CSS 與 JavaScript 的 type。因為預設值便是 text/csstext/javascript

HTML5 規範出處

<!-- External CSS -->
<link rel="stylesheet" href="code-guide.css">

<!-- In-document CSS -->
<style>
  /* ... */
</style>

<!-- JavaScript -->
<script src="code-guide.js"></script>

實用性勝過純粹性

在不犧牲實用性的前提下,盡量保持 HTML 具有語義並合乎標準。盡量使用簡潔、簡單的 Markup。

屬性順序

HTML 屬性應按照特定順序撰寫,確保程式碼的易讀性。

Class 是為了重用的元素而生,應該排第一位。ID 具體得多,應盡量少用(可用場景像是頁內書籤),所以排第二位。

<a class="..." id="..." data-modal="toggle" href="#">
  Example link
</a>

<input class="form-control" type="text">

<img src="..." alt="...">

布林屬性

布林屬性不需要有值。XHTML 要求宣告數值,但 HTML5 不需要。

深入閱讀請查閱 WhatWG 關於布林屬性一節

元素有布林屬性存在即代表值為 true,反之不存在則代表值為 false。

如果一定要附上屬性的數值的話,則無需遵守 WhatWG 的這條規則:

如果有寫屬性的話,其數值必須是空字串或是屬性的標準名稱(需符合 ASCII 不分大小寫的規則),且前後不可有空白。

長話短說,不用寫數值。

<input type="text" disabled>

<input type="checkbox" value="1" checked>

<select>
  <option value="1" selected>1</option>
</select>

簡化 Markup

撰寫 HTML 時,盡量避免多餘的父元素。但這需要反覆重寫與重構,才能寫出更少的 HTML。看看右邊這個例子:

<!-- Not so great -->
<span class="avatar">
  <img src="...">
</span>

<!-- Better -->
<img class="avatar" src="...">

JavaScript 產生的 Markup

Markup 寫在 JavaScript 裡,不僅難找、也更難編輯,性能更是差。盡量避免在 JavaScript 裡撰寫 Markup。

CSS

語法

對於這裡使用的術語有任何問題嗎?請參考維基百科上關於層疊樣式表條目裡的語法小節

/* Bad CSS */
.selector, .selector-secondary, .selector[type=text] {
  padding:15px;
  margin:0px 0px 15px;
  background-color:rgba(0, 0, 0, 0.5);
  box-shadow:0 1px 2px #CCC,inset 0 1px 0 #FFFFFF
}

/* Good CSS */
.selector,
.selector-secondary,
.selector[type="text"] {
  padding: 15px;
  margin: 0 0 15px;
  background-color: rgba(0,0,0,.5);
  box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}

宣告順序

相關的屬性宣告應以下列順序分組:

  1. Positioning
  2. Box Model
  3. Typographic
  4. Visual

Positioning,與位置有關的宣告放最前面,因為這可以從正常文件順序裡移除元素,或是覆寫牽扯到 Box Model 的樣式。Box Model 放下一個。因為它決定元件的位置與尺寸。

其它只在元件內部起作用的屬性放最後,因為這不會影響前兩者。

關於屬性順序的完整列表,請參考 Recess

.declaration-order {
  /* Positioning */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;

  /* Box-model */
  display: block;
  float: right;
  width: 100px;
  height: 100px;

  /* Typography */
  font: normal 13px "Helvetica Neue", sans-serif;
  line-height: 1.5;
  color: #333;
  text-align: center;

  /* Visual */
  background-color: #f5f5f5;
  border: 1px solid #e5e5e5;
  border-radius: 3px;

  /* Misc */
  opacity: 1;
}

Media Query 擺放位置

將 Media Query 與其最相關的規則放在一起。別把他們放在 CSS 檔案最後面,或是獨立成另外的樣式表。這樣只會讓之後接手的人錯過他們。右邊是個經典範例。

.element { ... }
.element-avatar { ... }
.element-selected { ... }

@media (min-width: 480px) {
  .element { ...}
  .element-avatar { ... }
  .element-selected { ... }
}

具前綴的屬性

使用帶有各家廠商前綴的屬性時,縮排每個屬性、垂直對齊,以便多行編輯。

Textmate 裡,使用 Text → Edit Each Line in Selection (⌃⌘A)。Sublime Text 2 則用 Selection → Add Previous Line (⌃⇧↑) 和 Selection → Add Next Line (⌃⇧↓)。

/* Prefixed properties */
.selector {
  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);
          box-shadow: 0 1px 2px rgba(0,0,0,.15);
}

單行宣告

當一組規則只包含單條宣告的的情況裡,考慮移除換行;寫成單行的可讀性更高、更容易編輯。任何包含多條宣告的一組規則應該要分為多行。

這麼分的關鍵因素是錯誤偵測─比如:CSS Validator 表示 183 行有語法錯誤。如果是單行宣告便就是那行,多條宣告沒分行的話則會找到抓狂。

/* Single declarations on one line */
.span1 { width: 60px; }
.span2 { width: 140px; }
.span3 { width: 220px; }

/* Multiple declarations, one per line */
.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 與 bottom margin,所以只要更改這兩個值即可。濫用特性縮寫只會寫出更差的程式碼,也會有無謂的覆寫與不預期的副作用。

Mozilla Developer Network 有篇很好的專文,給不熟悉記法與行為的開發者:特性簡寫

/* Bad example */
.element {
  margin: 0 0 10px;
  background: red;
  background: url("image.jpg");
  border-radius: 3px 3px 0 0;
}

/* Good example */
.element {
  margin-bottom: 10px;
  background-color: red;
  background-image: url("image.jpg");
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
}

Less 和 Sass 裡的巢狀

避免無謂的巢狀。你能巢狀不表示你都要用巢狀。只在多元素需要巢狀,或是需要在父元素下增加樣式的場景下使用巢狀。

// Without nesting
.table > thead > tr > th {  }
.table > thead > tr > td {  }

// With nesting
.table > thead > tr {
  > th {  }
  > td {  }
}

註解

程式碼是由人來撰寫與維護的。確保程式碼精準描述、有良好的註解,讓別人看起來是很親切。好的註解傳遞意圖、意境。註解不要只是重複元件或是 Class 的名稱。

長註解記得使用完整的句子,一般的筆記用簡潔的用語。

/* Bad example */
/* Modal header */
.modal-header {
  ...
}

/* Good example */
/* Wrapping element for .modal-title and .modal-close */
.modal-header {
  ...
}

Class 名稱

/* Bad example */
.t { ... }
.red { ... }
.header { ... }

/* Good example */
.tweet { ... }
.important { ... }
.tweet-header { ... }

選擇器

延伸閱讀:

/* Bad example */
span { ... }
.page-container #stream .stream-item .tweet .tweet-header .username { ... }
.avatar { ... }

/* Good example */
.avatar { ... }
.tweet-header .username { ... }
.tweet .avatar { ... }

組織

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

編輯喜好

使用下列設定來設定編輯器,避免掉常見的程式碼不一致和醜陋的 diffs:

考慮將以上偏好應用到專案的 .editorconfig 檔案並撰寫文件說明。舉個例子,參考 Bootstrap 的 .editorconfig。瞭解更多內容,請參考 editorconfig.org