# CSS / Sass 規范
*用更合理的方式寫 CSS 和 Sass*
參考自 [Airbnb CSS / Sass Styleguide](https://github.com/airbnb/css)
## 目錄
1. [術語](#terminology)
- [規則聲明](#rule-declaration)
- [選擇器](#selectors)
- [屬性](#properties)
1. [CSS](#css)
- [格式](#formatting)
- [注釋](#comments)
- [SMACSS 和 BEM](#oocss-and-bem)
- [ID 選擇器](#id-selectors)
- [JavaScript 鉤子](#javascript-hooks)
- [邊框](#border)
1. [Sass](#sass)
- [語法](#syntax)
- [排序](#ordering-of-property-declarations)
- [變量](#variables)
- [Mixins](#mixins)
- [擴展指令](#extend-directive)
- [嵌套選擇器](#nested-selectors)
<a name="terminology"></a>
## 術語
<a name="rule-declaration"></a>
### 規則聲明
我們把一個(或一組)選擇器和一組屬性稱之為 “規則聲明”。舉個例子:
```css
.listing {
font-size: 18px;
line-height: 1.2;
}
```
<a name="selectors"></a>
### 選擇器
在規則聲明中,“選擇器” 負責選取 DOM 樹中的元素,這些元素將被定義的屬性所修飾。選擇器可以匹配 HTML 元素,也可以匹配一個元素的類名、ID, 或者元素擁有的屬性。以下是選擇器的例子:
```css
.my-element-class {
/* ... */
}
[aria-hidden] {
/* ... */
}
```
<a name="properties"></a>
### 屬性
最后,屬性決定了規則聲明里被選擇的元素將得到何種樣式。屬性以鍵值對形式存在,一個規則聲明可以包含一或多個屬性定義。以下是屬性定義的例子:
```css
/* some selector */ {
background: #f1f1f1;
color: #333;
}
```
屬性位置循序
1. 位置屬性(position, top, right, z-index, display, float等)
2. 大小(width, height, padding, margin)
3. 文字系列(font, line-height, letter-spacing, color- text-align等)
4. 背景(background, border等)
5. 其他(animation, transition等)
```css
.example{
z-index: -1;
display: block;
font-size: 1.5em;
color: #333;
background: #f1f1f1;
}
```
<a name="css"></a>
## CSS
<a name="formatting"></a>
### 格式
* 使用 4 個空格作為縮進。
* 類名建議使用破折號代替駝峰法。如果你使用 BEM,也可以使用下劃線。
* 不要使用 ID 選擇器。
* 在一個規則聲明中應用了多個選擇器時,每個選擇器獨占一行。
* 在規則聲明的左大括號 `{` 前加上一個空格。
* 在屬性的冒號 `:` 后面加上一個空格,前面不加空格。
* 規則聲明的右大括號 `}` 獨占一行。
* 規則聲明之間用空行分隔開。
* 以上所有規則在一般編譯器中都可以通過格式化配置文件實現
**Bad**
```css
.avatar{
border-radius:50%;
border:2px solid white; }
.no, .nope, .not_good {
// ...
}
#lol-no {
// ...
}
```
**Good**
```css
.avatar {
border-radius: 50%;
border: 2px solid white;
}
.one,
.selector,
.per-line {
// ...
}
```
<a name="comments"></a>
### 注釋
* 建議使用行注釋 (在 Sass 中是 `//`) 代替塊注釋。
* 建議注釋獨占一行。避免行末注釋。
* 給沒有自注釋的代碼寫上詳細說明,比如:
- 為什么用到了 z-index
- 兼容性處理或者針對特定瀏覽器的 hack
* 瀏覽器兼容性前綴使用自動化自動生成
<a name="oocss-and-bem"></a>
### SMACSS 和 BEM
出于以下原因,我們鼓勵使用 SMACSS 和 BEM 的組合:
* 可以幫助我們理清 CSS 和 HTML 之間清晰且嚴謹的關系。
* 可以幫助我們創建出可重用、易裝配的組件。
* 可以減少嵌套,降低特定性。
* 可以幫助我們創建出可擴展的樣式表。
**示例**
```html
<header class="l-header">
<nav class="m-nav">
<ul class="m-nav_compent">
<li class="m-nav_children">
<span class="m-nav_list--red">
</span>
</li>
</ul>
</nav>
</header>
```
```scss
.l {
&-header {
}
}
.m {
&-nav{
&_compent{
}
&_children{
}
&_list{
&--red{
}
}
}
}
```
<a name="id-selectors"></a>
### ID 選擇器
在 CSS 中,雖然可以通過 ID 選擇元素,但大家通常都會把這種方式列為反面教材。ID 選擇器給你的規則聲明帶來了不必要的高[優先級](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity),而且 ID 選擇器是不可重用的。
想要了解關于這個主題的更多內容,參見 [CSS Wizardry 的文章](http://csswizardry.com/2014/07/hacks-for-dealing-with-specificity/),文章中有關于如何處理優先級的內容。
<a name="javascript-hooks"></a>
### JavaScript 鉤子
避免在 CSS 和 JavaScript 中綁定相同的類。否則開發者在重構時通常會出現以下情況:輕則浪費時間在對照查找每個要改變的類,重則因為害怕破壞功能而不敢作出更改。
我們推薦在創建用于特定 JavaScript 的類名時,添加 `.js-` 前綴:,在spa項目中,幾乎用不到直接操作dom的情況
```html
<button class="btn btn-primary js-request-to-book">Request to Book</button>
```
<a name="border"></a>
### 邊框
在定義無邊框樣式時,使用 `0` 代替 `none`。
**Bad**
```css
.foo {
border: none;
}
```
**Good**
```css
.foo {
border: 0;
}
```
<a name="sass"></a>
## Sass
<a name="syntax"></a>
### 語法
* 使用 `.scss` 的語法,不使用 `.sass` 原本的語法。
* CSS 和 `@include` 聲明按照以下邏輯排序(參見下文)
<a name="ordering-of-property-declarations"></a>
### 屬性聲明的排序
1. 屬性聲明
首先列出除去 `@include` 和嵌套選擇器之外的所有屬性聲明。
```scss
.btn-green {
background: green;
font-weight: bold;
// ...
}
```
2. `@include` 聲明
緊隨后面的是 `@include`,這樣可以使得整個選擇器的可讀性更高。
```scss
.btn-green {
background: green;
font-weight: bold;
@include transition(background 0.5s ease);
// ...
}
```
3. 嵌套選擇器
_如果有必要_用到嵌套選擇器,把它們放到最后,在規則聲明和嵌套選擇器之間要加上空白,相鄰嵌套選擇器之間也要加上空白。嵌套選擇器中的內容也要遵循上述指引。
```scss
.btn {
background: green;
font-weight: bold;
@include transition(background 0.5s ease);
.icon {
margin-right: 10px;
}
}
```
<a name="variables"></a>
### 變量
變量名應使用破折號(例如 `$my-variable`)代替 camelCased 和 snake_cased 風格。對于僅用在當前文件的變量,可以在變量名之前添加下劃線前綴(例如 `$_my-variable`)。
<a name="mixins"></a>
### Mixins
為了讓代碼遵循 DRY 原則(Don't Repeat Yourself)、增強清晰性或抽象化復雜性,應該使用 mixin,這與那些命名良好的函數的作用是異曲同工的。雖然 mixin 可以不接收參數,但要注意,假如你不壓縮負載(比如通過 gzip),這樣會導致最終的樣式包含不必要的代碼重復。
<a name="extend-directive"></a>
### 擴展指令
應避免使用 `@extend` 指令,因為它并不直觀,而且具有潛在風險,特別是用在嵌套選擇器的時候。即便是在頂層占位符選擇器使用擴展,如果選擇器的順序最終會改變,也可能會導致問題。(比如,如果它們存在于其他文件,而加載順序發生了變化)。其實,使用 @extend 所獲得的大部分優化效果,gzip 壓縮已經幫助你做到了,因此你只需要通過 mixin 讓樣式表更符合 DRY 原則就足夠了。
<a name="nested-selectors"></a>
### 嵌套選擇器
**請不要讓嵌套選擇器的深度超過 3 層!**
```scss
.page-container {
.content {
.profile {
// STOP!
}
}
}
```
當遇到以上情況的時候,你也許是這樣寫 CSS 的:
* 與 HTML 強耦合的(也是脆弱的)*—或者—*
* 過于具體(強大)*—或者—*
* 沒有重用
**永遠不要嵌套 ID 選擇器!**
如果你始終堅持要使用 ID 選擇器(勸你三思),那也不應該嵌套它們。如果你正打算這么做,你需要先重新檢查你的標簽,或者指明原因。如果你想要寫出風格良好的 HTML 和 CSS,你是**不**應該這樣做的。