內容目錄
黃金原則
確實要求每個人,或自己,時時刻刻都一致認同規範。不管大還是小,有錯誤的地方請大聲說出來。想要對這份程式碼編寫指南進行新增或貢獻,請在 Github 開 Issue,中文翻譯相關 Issue 請點此。
每行程式碼,不管有多少人參與,應該要看起來是一個人寫出來的。
HTML
語法
- 不以大寫使用標籤,包含 Doctype。
- 使用 2 個空白的 Soft Tab—這是可以保證程式碼在任何環境,都長得一樣的唯一方法。
- 巢狀裡面元素應該要縮排一次(2 個空白)。
- 屬性一定要用雙引號,絕不使用單引號。
- 在自帶關閉符號的元素不在尾端加上斜線-HTML5 規格提到:這不是必需的。
- 不能省略非必要的關閉標籤(例如:
</li>
或</body>
)。
<!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.
<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/css
和 text/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 屬性應該要按照以下的順序列出。
class
id
,name
data-*
src
,for
,type
,href
,value
title
,alt
role
,aria-*
tabindex
style
先放最常用來辨識元素的屬性-class
、id
、name
然後是 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="...">
編輯器偏好設定
為了避免常見的程式碼不一致,以及亂成一鍋粥的差異比對,編輯器要做以下設定:
- 使用設定為 2 個空白的 Soft Tab。
- 儲存時,移除尾端的空白字元。
- 編碼設定為 UTF-8。
- 在檔案的尾端新增一行。
請考慮把這些偏好設定在專案的 .editorconfig
檔案裡記錄、設定起來。請看 Bootstrap 做的實際案例。深入閱讀 EditorConfig。
CSS
語法
- 使用 2 個空白的 Soft Tab—這是可以保證程式碼在任何環境,都長得一樣的唯一方法。
- 為選擇器 ,每個選擇器都維持一行。
- 為了易讀性考量,宣告的起頭大括號前面,要有空格。
- 宣告的關閉大括號要換行。
- 每個宣告的
:
後面要有空白。 - 每個宣告應該都是單獨一行,錯誤回報才會更精準。
- 所有宣告的結尾都有分號。最後一個宣告可以省略,然而,沒有的話,程式碼更容易出錯。
- 以逗號分隔的屬性值,應該要在逗號之後加個空白(例如:
box-shadow
)。 - 以空白分隔的方式使用顏色屬性(例如:
color: rgb(0 0 0 / .5)
),詳見顏色段落。 - 屬性值或顏色參數不需要前綴 0(例如:
.5
取代0.5
,以及-.5px
取代-0.5px
)。 - 16 進位值全部是小寫,例如:
#fff
。小寫字母因為通常有比較多獨特的形狀,在掃視文件的時候,辨識容易許多。 - 盡可能使用捷徑來表示16 進位值,例如:
#fff
取代#ffffff
。 - 選擇器裡的屬性值要用引號,例如:
input[type="text"]
。只有一些特定情況才不必這樣做,這是保持一致的好方法。 - 數值為 0 的時候,避免指定單位。例如:
margin: 0;
,而不是margin: 0px;
。
對於這裡的用語有問題?請看 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;
}
宣告順序
屬性宣告應根據以下順序群組起來:
- 設定位置
- Box 模型
- 文字排版
- 視覺
- 其它
因為位置設定,可以把元素從一般文件佈局流動移除,並且覆蓋 Box 模型,因此要放在最前面。接著是 Box 模型,不管是 Flex、Float、Grid 或 Table-因為掌控了元件的尺寸、擺放位置與靠齊。其它東西,因為是在元件的裡面發生,或者,不會影響前面兩段,而放在後面。
雖然 border
是 Box 模型的一部分,大多數的系統都一律把 box-sizing
設定為 border-box
,不讓 border-width
影響整體尺寸。加上讓 border
跟 border-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;
}
邏輯屬性
邏輯屬性,是以 block 和 inline 等抽象名詞替代方向與尺寸屬性。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 4,rgba()
和 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; }
捷徑語法標記
只有在強調得設定所有可用的值時,才會使用捷徑宣告。常見濫用的捷徑屬性包含:
padding
margin
font
background
border
border-radius
通常,沒有必要設定捷徑屬性所提供的全部值。舉例來說: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 名稱
- Class 要保持小寫,並且使用半型橫槓(而不是底線或 camelCase)。半型橫槓在相關的 Class 就像天然的分隔(例如:
.btn
和.btn-danger
)。 - 避免過度和隨意的捷徑標記。按鈕取為
.btn
很有用,但.s
就沒有意義了。 - Class 要盡量保持簡短與簡潔。
- 使用有意義的名稱;使用結構或有意義的名稱,而不是根據外表。
- 根據最近的上層或基礎 Class 來取前綴詞。
- 使用 Class 名稱
.js-*
來標注行為(而不是樣式)。但是,這些 Class 不會出現在 CSS 檔案裡。
在編寫 Custom Properties 和預處理器變數名稱時,這些規則也很好用。
// 不良範例
.t { ... }
.red { ... }
.header { ... }
// 優良範例
.tweet { ... }
.important { ... }
.tweet-header { ... }
選擇器
- 使用 Class 取代一般元素標籤,樣式設定就不是依靠標記,而更明顯且可靠。
- 經常重複使用的元件,要避免使用多重屬性選擇器(例如:
[class^="..."]
)。已知瀏覽器的效能會受到影響。 - 選擇器要簡短,並且盡力把每個選擇器的元素限制在 3 個。
- 只有在有必要的時候,根據最近的上層,限制 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 { ... }