程式碼編寫指南的 Logo

程式碼編寫指南

一致、有彈性且能長期維護的 HTML、CSS 開發標準。

@mdo 撰寫 · v4.0.0 · GitHub 網址

內容目錄

HTML

CSS

黃金原則

確實要求每個人,或自己,時時刻刻都一致認同規範。不管大還是小,有錯誤的地方請大聲說出來。想要對這份程式碼編寫指南進行新增或貢獻,請在 Github 開 Issue,中文翻譯相關 Issue 請點此

每行程式碼,不管有多少人參與,應該要看起來是一個人寫出來的。

HTML

語法

<!doctype html>
<html>
  <head>
    <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 html>
<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

語言屬性

根據 HTML5 規格:

Authors are encouraged to specify a lang attribute on the root html element, giving the document’s language. This aids speech synthesis tools to determine what pronunciations to use, translation tools to determine what rules to use, and so forth.

規格,深入閱讀 lang 屬性。前往 IANA 查看語言代碼清單

<html lang="en">
  <!-- ... -->
</html>

IE 相容模式

現今,除非得要支援 IE10 或更舊版,已經不需要加上 Internet Explorer 文件的相容 <meta> 標籤。這組標籤已經在 IE11 棄用,Microsoft Edge不管是舊版或其它版本,也都不會用到。

詳細資訊請看這篇很讚的 Stack Overflow 文章

<!-- 適用於 IE10 以前 -->
<meta http-equiv="x-ua-compatible" content="ie=edge">

字元編碼

明確宣告字元編碼,就可以確保輸出的內容是正確的。這樣也可以使用一般文字,而不是 HTML Entity。例如:只要編碼與文件是相符的,就可以用 取代 &emdash;。有些 XML 的保留字元,像是與符號 (&)、不換行空格、少於/多於與引號,可能還是得要用 HTML 字元 Entity。

建議使用 UTF-8 編碼。

<head>
  <meta charset="utf-8">
</head>
<body>
  <p>破折號就這樣使用—毋需得用上 HTML Entity。</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 的標準與情境,但不應犧牲實用性。只要有可能,盡量以最低的複雜度,使用最少的標記。

<!-- 很好 -->
<button>...</button>

<!-- 不好 -->
<div class="btn" onClick="...">...</div>

屬性順序

為了可以更容易閱讀程式碼,HTML 屬性應該要按照以下的順序列出。

先放最常用來辨識元素的屬性-classidname 然後是 data。接下來是元素專用的其它屬性;最後是無障礙設計和樣式相關的屬性。

<a class="..." id="..." data-toggle="modal" href="#">
  範例連結
</a>

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

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

二元屬性

二元屬性是不需要宣告值的屬性。XHTML 要求得宣告值,但 HTML 沒這樣需求。

想要深入理解,可以查閱 WhatWG 這段,對二元屬性的意見

元素出現二元屬性,代表是 true 值,而沒有該屬性,就是 false 值。

如果屬性一定得要有值,雖然不需要這樣做,可以按照 WhatWG 的規範:

如果有設定屬性,它的值不是空白字串,就是 […] 屬性的唯一名稱,且前方和尾端沒有空白。

簡單來說,就是不要加上值

<input type="text" disabled>

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

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

精簡標記使用

撰寫 HTML 的時候,盡可能避免多餘的上層元素。很多時候,這得要不斷修改與重構,但會產生比較少 HTML。

<!-- 不怎麼好 -->
<span class="avatar">
  <img src="...">
</span>

<!-- 好多了 -->
<img class="avatar" src="...">

編輯器偏好設定

為了避免常見的程式碼不一致,以及亂成一鍋粥的差異比對,編輯器要做以下設定:

請考慮把這些偏好設定在專案的 .editorconfig 檔案裡記錄、設定起來。請看 Bootstrap 做的實際案例。深入閱讀 EditorConfig

CSS

語法

對於這裡的用語有問題?請看 Wikipedia 上,階層式樣式表的語法段落

// 壞 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. Box 模型
  3. 文字排版
  4. 視覺
  5. 其它

因為位置設定,可以把元素從一般文件佈局流動移除,並且覆蓋 Box 模型,因此要放在最前面。接著是 Box 模型,不管是 Flex、Float、Grid 或 Table-因為掌控了元件的尺寸、擺放位置與靠齊。其它東西,因為是在元件的裡面發生,或者,不會影響前面兩段,而放在後面。

雖然 border 是 Box 模型的一部分,大多數的系統都一律把 box-sizing 設定為 border-box,不讓 border-width 影響整體尺寸。加上讓 borderborder-radius 更靠近,這就是改放在視覺段落的原因。

預處理器的 Mixin 和函式要擺在最合適的地方。例如:border-top-radius() 這個 Mixin 要放在 border-radius 屬性的位置;而 responsive-font-size() 會在 font-size 屬性的位置。

Bootstrap 使用的 Stylelint 屬性順序,可以看到所有屬性列表與其順序。

.declaration-order {
  // 設定位置
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;

  // Box 模型
  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 使用這些值。

為什麼要使用邏輯屬性?不是每個語言都跟英文一樣,是從左往右看。因此,Writing Mode 得能靈活使用。有了邏輯屬性,就可以輕鬆地支援水平或垂直書寫的與言(像是中文、日文與韓文)。另外,通常寫起來比較簡短、簡易。

延伸閱讀

// 未使用邏輯屬性
.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 4rgba()hsla() 現在跟 rgb()hsl() 是相同的,也就是說,能夠在 rgb()hsl() 修改 Alpha 值。同時,顏色值也支援以空白分隔的新語法。為了能跟未來的 CSS 顏色函式相容,就用新語法吧。

除了顏色值和語法,挑選顏色的時候也要確保符合 WCAG 的最小對比度(16px 以下是 4.5:1;以上是3:1)。

延伸閱讀:

.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>

Media Query 放置方法

把要用到的 Media Query,盡可能放在相關規則附近。不要打包成另一組獨立的樣式表,或放在文件的尾端。這樣做,之後會讓其他人不小心就錯過。請參考常見的設置方法。

.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 的標題只設定了上和下的 margin,因此,有必要的時候,僅需要覆蓋這兩個值。0 值代表覆蓋了瀏覽器預設值,或先前指定的值。

過度使用捷徑屬性,會導致沒必要的覆蓋和預料之外的副作用,讓程式碼更雜亂。

Mozilla Developer Network 有一篇針對捷徑屬性的優良文章,寫給不熟標記和行為的人。

// 不良範例
.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 {  }
}

在預處理器使用運算子

所有數學運算都用括號包起來,值、變數與運算子之間都有一個空白,可讀性就會大幅改善。

// 不良範例
.element {
  margin: 10px 0 @variable*2 10px;
}

// 優良範例
.element {
  margin: 10px 0 (@variable * 2) 10px;
}

註解

程式碼是由人所撰寫和維護。確保程式碼對其他人來說,是充分描述的、有良好註解,以及友善的。好的程式碼註解傳達情境或目標。請勿僅僅重複描述元件或 Class 名稱。在預處理器撰寫 CSS 時,使用 // 語法。發布到正式環境時,要移除所有註解。

比較龐大的註解記得要寫成完整的句子;一般記錄使用簡潔的詞彙。

// 不良範例
// 彈出視窗的標題
.modal-header {
  ...
}

// 優良範例
// 包覆 .modal-title 和 .modal-close 的元素
.modal-header {
  ...
}

Class 名稱

在編寫 Custom Properties 和預處理器變數名稱時,這些規則也很好用。

// 不良範例
.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 { ... }