如果你問(wèn)我一個(gè)樣式指南首先要描述什么,我會(huì)告訴你:編寫(xiě)代碼的通用準(zhǔn)則。
當(dāng)幾個(gè)開(kāi)發(fā)者在同一項(xiàng)目中編寫(xiě) CSS 時(shí),遲早會(huì)陷入各自為政的境地。編碼樣式指南通過(guò)規(guī)范統(tǒng)一的樣式,不僅防止了這種混亂現(xiàn)象,也減輕了閱讀和維護(hù)代碼的難度。
概括地說(shuō),我們希望做到(受 CSS Guidelines 所啟發(fā)):
// Yep
.foo {
display: block;
overflow: hidden;
padding: 0 1em;
}
// Nope
.foo {
display: block; overflow: hidden;
padding: 0 1em;
}
無(wú)論你是否相信,字符串在 CSS 和 SCSS 中都占有重要地位。大多數(shù)的 CSS 值不是長(zhǎng)度就是標(biāo)識(shí)符,所以遵循固定的編程規(guī)范處理 Sass 中的字符串是非常重要的一項(xiàng)工作。
為了避免潛在的字符編碼問(wèn)題,強(qiáng)力建議在入口文件中通過(guò) @charset
指令使用 UTF-8 編碼格式。請(qǐng)確保該指令是文件的第一條語(yǔ)句,并排除其他字符編碼聲明。
@charset 'utf-8';
CSS 中不要求字符串必須用引號(hào)包裹,甚至是字符串中包含空格的。就拿font-family
屬性來(lái)說(shuō):無(wú)論你是否使用引號(hào)包裹,CSS 解析器都可以正確解析。
因此,Sass 也不強(qiáng)制要求字符串必須被引號(hào)包裹。更棒的是(你也會(huì)如此認(rèn)為),被引號(hào)包裹和沒(méi)被包裹的一對(duì)字符串完全等同(例如,'abc'
嚴(yán)格等同于 abc
)。
話(huà)雖如此,不強(qiáng)制要求字符串被引號(hào)包裹的畢竟是少數(shù),所以,在 Sass 中字符串應(yīng)該始終被單引號(hào)('
)所包裹(在 querty 鍵盤(pán)中單引號(hào)比雙引號(hào)更容易輸入)。即使不考慮與其他語(yǔ)言的一致性,單是考慮 CSS 的近親 JavaScript,也有數(shù)條理由這么做:
// Yep
$direction: 'left';
// Nope
$direction: left;
CSS 規(guī)范建議, 將 @charset
指令用雙引號(hào)包裹起來(lái) 才是有效的. 不過(guò),Sass 在編譯的時(shí)候已經(jīng)自動(dòng)修正了相關(guān)信息,所以無(wú)論何種方式都可以生成正確的代碼,即使是只有 @charset
。
CSS 中類(lèi)似 initial
或 sans-serif
的標(biāo)識(shí)符無(wú)須引用起來(lái)。事實(shí)上,font-family: 'sans-serif'
該聲明是錯(cuò)誤的,因?yàn)?CSS 希望獲得的是一個(gè)標(biāo)識(shí)符,而不是一個(gè)字符串。因此,我們無(wú)須引用這些值。
// Yep
$font-type: sans-serif;
// Nope
$font-type: 'sans-serif';
// Okay I guess
$font-type: unquote('sans-serif');
就像上例這樣,我們就可以區(qū)別用于 CSS 值的字符串(CSS 標(biāo)識(shí)符)和 Sass 的字符串類(lèi)型了(比如 map 的值)。
我們沒(méi)有引用前者,但卻使用單引號(hào)包裹了它。
如果字符串內(nèi)包含了一個(gè)或多個(gè)單引號(hào),一種解決方案就是使用雙引號(hào)包裹整個(gè)字符串,從而避免使用轉(zhuǎn)義字符。
// Okay
@warn 'You can\'t do that.';
// Okay
@warn "You can't do that.";
URL 最好也用引號(hào)包裹起來(lái),原因和上面所描述一樣:
// Yep
.foo {
background-image: url('/images/kittens.jpg');
}
// Nope
.foo {
background-image: url(/images/kittens.jpg);
}
在 Sass 中,數(shù)字類(lèi)型包括了長(zhǎng)度、持續(xù)時(shí)間、頻率、角度等等無(wú)單位數(shù)字類(lèi)型。Sass 允許在運(yùn)行中計(jì)算這些度量值。
當(dāng)數(shù)字小于 1
時(shí),應(yīng)該在小數(shù)點(diǎn)前寫(xiě)出 0.
永遠(yuǎn)不要顯示小數(shù)尾部的 0
。
// Yep
.foo {
padding: 2em;
opacity: 0.5;
}
// Nope
.foo {
padding: 2.0em;
opacity: .5;
}
當(dāng)定義長(zhǎng)度時(shí),0
后面不需要加單位。
// Yep
$length: 0;
// Nope
$length: 0em;
注意,該建議只是針對(duì)于長(zhǎng)度而言,對(duì)于類(lèi)似 transition-delay
的時(shí)間屬性就是不適合的。理論上,如果持續(xù)時(shí)間的屬性值為無(wú)單位的 0,那么該屬性值就會(huì)被認(rèn)為是無(wú)效的。雖然并不是所有的瀏覽器都這么嚴(yán)格檢查屬性值,但確實(shí)有一些瀏覽器會(huì)這么做。簡(jiǎn)而言之:只有長(zhǎng)度可以使用無(wú)單位的 0 作為屬性值。
在 Sass 中最常見(jiàn)的錯(cuò)誤,是簡(jiǎn)單地認(rèn)為單位只是字符串,認(rèn)為它會(huì)被安全的添加到數(shù)字后面。這雖然聽(tīng)起來(lái)不錯(cuò),但卻不是單位正確的解析方式。可以把單位認(rèn)為是代數(shù)符號(hào),例如,在現(xiàn)實(shí)世界中,5
英寸乘以 5
英寸得到 25
英寸。Sass 也適用這樣的邏輯。
將一個(gè)單位添加給數(shù)字的時(shí)候,實(shí)際上是讓該數(shù)值乘以1
個(gè)單位。
$value: 42;
// Yep
$length: $value * 1px;
// Nope
$length: $value + px;
需要注意的是加上一個(gè) 0unit
也可以正確解析,但是這種方式在某種程度上會(huì)造成一些混亂,所以我更愿意推薦上面的方式。事實(shí)上,將一個(gè)數(shù)字轉(zhuǎn)換為其他兼容單位時(shí),加 0
操作并不會(huì)造成錯(cuò)誤。更多信息請(qǐng)參考這篇文章.
$value: 42 + 0px;
// -> 42px
$value: 1in + 0px;
// -> 1in
$value: 0px + 1in;
// -> 96px
這一切最終取決于你想要達(dá)到怎樣的效果。只要記住,像添加一個(gè)字符串一樣添加一個(gè)單位并不是一種好的處理方式。
要?jiǎng)h除一個(gè)值的單位,你需要除以相同類(lèi)型的 1
單位。
$length: 42px;
// Yep
$value: $length / 1px;
// Nope
$value: str-slice($length + unquote(''), 1, 2);
給一個(gè)數(shù)值以字符串形式添加單位的結(jié)果是產(chǎn)生一個(gè)字符串,同時(shí)要防止對(duì)數(shù)據(jù)的額外操作。從一個(gè)帶有單位的數(shù)值中分離數(shù)字部分也會(huì)產(chǎn)生字符串,但這些都不是你想要的。更多信息請(qǐng)參考這篇文章:Use lengths, not strings。
最高級(jí)運(yùn)算應(yīng)該始終被包裹在括號(hào)中。這么做不僅是為了提高可讀性,也是為了防止一些 Sass 強(qiáng)制要求對(duì)括號(hào)內(nèi)內(nèi)容計(jì)算的極端情況。
// Yep
.foo {
width: (100% / 3);
}
// Nope
.foo {
width: 100% / 3;
}
"幻數(shù)",是古老的學(xué)校編程給未命名數(shù)值常數(shù)的命名?;旧希鼈冎皇?em>能工作?但沒(méi)有任何邏輯思維的隨機(jī)數(shù)。
相信不用多說(shuō)你也會(huì)理解,幻數(shù)是一場(chǎng)瘟疫,應(yīng)不惜一切代價(jià)以避免。當(dāng)你對(duì)數(shù)值的解析方式無(wú)法找到一個(gè)合理解釋時(shí),你可以對(duì)此提交一個(gè)內(nèi)容寬泛的評(píng)論,包括你是怎樣遇見(jiàn)這個(gè)問(wèn)題以及你認(rèn)為它應(yīng)該怎樣工作。承認(rèn)自己不清楚一些機(jī)制的解析方式,遠(yuǎn)比讓以后的開(kāi)發(fā)者從零開(kāi)始弄清它們更有幫助。
/**
* 1. Magic number. This value is the lowest I could find to align the top of
* `.foo` with its parent. Ideally, we should fix it properly.
*/
.foo {
top: 0.327em; /* 1 */
}
CSS-Tricks 上有一篇文章 討論了 CSS 中的 magic numbers,建議你閱讀一下。
顏色在 CSS 中占有重要地位。當(dāng)涉及到操縱色彩時(shí),Sass 通過(guò)提供少數(shù)的函數(shù)功能,最終成為了極具價(jià)值的助手。
Sass 非常善于操縱顏色,以下文章都討論了在 Sass 中對(duì)顏色的操作,建議閱讀:
為了盡可能簡(jiǎn)單地使用顏色,我建議顏色格式要按照以下順序排列:
除非是為了快速開(kāi)發(fā)出原型,否則不建議使用 CSS 顏色關(guān)鍵字。這是因?yàn)轭伾P(guān)鍵字都是英文單詞,對(duì)于非英語(yǔ)母語(yǔ)者會(huì)造成理解困難。此外,顏色關(guān)鍵字的語(yǔ)義化并不準(zhǔn)確,比如 grey
比 darkgrey
的顏色更深一些;grey
和 gray
之間的差別也會(huì)造成一致性的問(wèn)題。
HSL 表示法不僅僅是最易于理解的顏色表示方法,而且也便于開(kāi)發(fā)者通過(guò)調(diào)整色調(diào)、飽和度和亮度來(lái)驚喜地調(diào)整顏色。
相比于 HSL 表示法,RGB 表示法的優(yōu)勢(shì)在于表示近似紅綠藍(lán)的顏色時(shí)更加簡(jiǎn)潔明了,但是表示紅綠藍(lán)的混合色時(shí)就不如 HSL 表示法更易于理解了。
最后,十六進(jìn)制對(duì)于人類(lèi)的思維來(lái)說(shuō)是比較難以理解的,除非必要,否則請(qǐng)優(yōu)先考慮前幾種方式。
// Yep
.foo {
color: hsl(0, 100%, 50%);
}
// Also yep
.foo {
color: rgb(255, 0, 0);
}
// Meh
.foo {
color: #f00;
}
// Nope
.foo {
color: #FF0000;
}
// Nope
.foo {
color: red;
}
使用 HSL 值或者 RGB 值,通常在逗號(hào) (,
)后面追加一個(gè)空格,而不在前后括號(hào) ((
, )
) 和值之間添加空格。
// Yep
.foo {
color: rgba(0, 0, 0, 0.1);
background: hsl(300, 100%, 100%);
}
// Nope
.foo {
color: rgba(0,0,0,0.1);
background: hsl( 300, 100%, 100% );
}
當(dāng)一個(gè)顏色被多次調(diào)用時(shí),最好用一個(gè)有意義的變量名來(lái)保存它。
$sass-pink: hsl(330, 50%, 60%);
現(xiàn)在,你就可以在任何需要的地方隨意使用這個(gè)變量了。不過(guò),如果你是在一個(gè)主題中使用,我不建議固定的使用這個(gè)變量。相反,可以使用另一個(gè)標(biāo)識(shí)方式的變量來(lái)保存它。
$main-theme-color: $sass-pink;
這樣做可以防止一個(gè)主題變化而出現(xiàn)此類(lèi)結(jié)果 $sass-pink: blue
。這篇文章 介紹了為什么妥善處理顏色問(wèn)題如此重要。
lighten
和 darken
函數(shù)都是通過(guò)增加或者減小HSL中顏色的亮度來(lái)實(shí)現(xiàn)調(diào)節(jié)的?;旧?,它們就是 adjust-color
函數(shù)添加了 $lightness
參數(shù)的別名。
問(wèn)題是,這些函數(shù)經(jīng)常并不能實(shí)現(xiàn)預(yù)期的結(jié)果。另一方面,通過(guò)混合白色
或 黑色
實(shí)現(xiàn)變量或變暗的 mix
函數(shù),是一個(gè)不錯(cuò)的方法。
和上述兩個(gè)函數(shù)相比,使用 mix
的好處是,當(dāng)你降低顏色的比例時(shí),它會(huì)漸進(jìn)的接近黑色(或者白色),而 darken
和 lighten
立即變換顏色到黑色或白色。
http://wiki.jikexueyuan.com/project/sass-guidelines/images/1.1.png" alt="" />
有關(guān) lighten/darken 和 mix 之間差異的示例來(lái)源于KatieK
如果你不想每次都寫(xiě) mix
函數(shù),你可以創(chuàng)建兩個(gè)易用的 tint
和 shade
(Compass 的一部分)來(lái)處理相同的事:
/// Slightly lighten a color
/// @access public
/// @param {Color} $color - color to tint
/// @param {Number} $percentage - percentage of `$color` in returned color
/// @return {Color}
@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
/// Slightly darken a color
/// @access public
/// @param {Color} $color - color to shade
/// @param {Number} $percentage - percentage of `$color` in returned color
/// @return {Color}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}
scale-color
函數(shù)的設(shè)計(jì)初衷是為了更流暢地調(diào)試屬性——以實(shí)際的高低為調(diào)試基礎(chǔ)。它如同mix
一樣好用,并且提供了更清晰地調(diào)用約定。比例因子并不完全相同。
列表就是 Sass 的數(shù)組。列表是一個(gè)一維的數(shù)據(jù)結(jié)構(gòu)(不同于 maps),用于保存任意類(lèi)型的數(shù)值(包括列表,從而產(chǎn)生嵌套列表)。
列表需要遵守以下規(guī)范:
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif);
// Yep
$font-stack: (
'Helvetica',
'Arial',
sans-serif,
);
// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif;
// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif;
// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,);
當(dāng)需要給列表添加一個(gè)新列表項(xiàng)時(shí),請(qǐng)遵守其提供的 API,不要試圖手動(dòng)給列表添加列表項(xiàng)。
$shadows: (0 42px 13.37px hotpink);
// Yep
$shadows: append($shadows, $shadow, comma);
// Nope
$shadows: $shadows, $shadow;
在這篇文章中介紹了許多合理使用列表的技巧和注意事項(xiàng)。
在 Sass 中,樣式開(kāi)發(fā)者可以使用 map 這種數(shù)據(jù)結(jié)構(gòu) —— Sass 團(tuán)隊(duì)使 map 可以映射關(guān)聯(lián)數(shù)組、哈希表甚至是 Javascript 對(duì)象。map 是一種映射任何類(lèi)型的鍵值對(duì),包括內(nèi)嵌類(lèi)型的 map,但是我不建議使用 map 存儲(chǔ)復(fù)雜數(shù)據(jù)類(lèi)型。
map 的使用應(yīng)該遵循下述規(guī)范:
:
)之后添加空格;(
)要和冒號(hào) (:
)寫(xiě)在同一行;,
)結(jié)尾;,
),方便添加新鍵值對(duì)、刪除和重排已有鍵值對(duì);)
);)
)和分號(hào)(;
)之間不使用空格和換行。示例:
// Yep
$breakpoints: (
'small': 767px,
'medium': 992px,
'large': 1200px,
);
// Nope
$breakpoints: ( small: 767px, medium: 992px, large: 12
自從 Sass 支持 map 依賴(lài)具有很多關(guān)于它的文章,我建議你閱讀以下三篇:Using Sass Maps, Extra Map functions in Sass, Real Sass, Real Maps.
在這里,極有可能顛覆每個(gè)人對(duì)書(shū)寫(xiě) CSS 規(guī)則集的認(rèn)知(根據(jù)眾多規(guī)范整理而成,包括CSS Guidelines):
{
)中間書(shū)寫(xiě)一個(gè)空格;:
)后添加空格;;
);}
)單獨(dú)一行;}
)添加空行。示例:
// Yep
.foo, .foo-bar,
.baz {
display: block;
overflow: hidden;
margin: 0 auto;
}
// Nope
.foo,
.foo-bar, .baz {
display: block;
overflow: hidden;
margin: 0 auto }
添加與 CSS 相關(guān)的規(guī)范時(shí),我們需要注意:
@content
的混合宏在放在其他聲明之前;@content
的混合宏在嵌套選擇器后聲明;}
)上一行無(wú)需空行;示例:
.foo, .foo-bar,
.baz {
$length: 42em;
@include ellipsis;
@include size($length);
display: block;
overflow: hidden;
margin: 0 auto;
&:hover {
color: red;
}
@include respond-to('small') {
overflow: visible;
}
}
難以想象竟有這么多關(guān)于劃分 CSS 聲明順序的討論。具體而言,有如下兩派:
position
, display
, colors
, font
, miscellaneous...)順序排列;這兩種方式各有利弊。一方面,字母排序方式通俗易懂(至少對(duì)于語(yǔ)言中使用拉丁字母的人來(lái)說(shuō)),所以排序的過(guò)程完全沒(méi)有爭(zhēng)議。但是,這種排序的結(jié)果卻十分奇怪,如 bottom
和 top
竟然彼此不相鄰。為什么 animations
屬性出現(xiàn)在 display
屬性之前?字母排序方式有太多諸如此類(lèi)的怪相了。
.foo {
background: black;
bottom: 0;
color: white;
font-weight: bold;
font-size: 1.5em;
height: 100px;
overflow: hidden;
position: absolute;
right: 0;
width: 100px;
}
另一方面,按照類(lèi)型排序則讓屬性顯得更具有意義。每個(gè)和字體相關(guān)的屬性被聲明在一起,top
和 bottom
也結(jié)合在一起,最終審閱CSS規(guī)則集感覺(jué)就像是在讀故事。除非你堅(jiān)持諸如 Idiomatic CSS的規(guī)定,不然類(lèi)型聲明順序可以有更豐富充實(shí)的表現(xiàn)。white-space
應(yīng)該放在哪里:font還是dispaly? overflow
應(yīng)該歸屬何處?如何進(jìn)行組內(nèi)排序(如果是字母排序,這豈不成了個(gè)笑話(huà))?
.foo {
height: 100px;
width: 100px;
overflow: hidden;
position: absolute;
bottom: 0;
right: 0;
background: black;
color: white;
font-weight: bold;
font-size: 1.5em;
}
此外也有其他類(lèi)型排序的分支,比如Concentric CSS,他看起來(lái)相當(dāng)流行。Concentric CSS 的基礎(chǔ)是依賴(lài)盒模型定義順序:由外而內(nèi)。
.foo {
width: 100px;
height: 100px;
position: absolute;
right: 0;
bottom: 0;
background: black;
overflow: hidden;
color: white;
font-weight: bold;
font-size: 1.5em;
}
我必須說(shuō)我不能對(duì)此下任何判定。一份 CSS-Tricks 做的統(tǒng)計(jì)報(bào)告確認(rèn),超過(guò) 45% 的開(kāi)發(fā)者使用類(lèi)型順序聲明,而只有 14% 使用字母順序。此外還有 39% 的開(kāi)發(fā)者隨意而為,這其中就包括我。
http://wiki.jikexueyuan.com/project/sass-guidelines/images/1.2.png" alt="" />
圖表展示了開(kāi)發(fā)者排列 CSS 聲明順序的方式
因此,我不會(huì)在此強(qiáng)加規(guī)范選擇怎樣的聲明順序。只要你長(zhǎng)久的在自己的樣式表中保持一致的風(fēng)格,那么選擇喜歡的聲明順序就可以了(也就說(shuō)不要太隨便)。
最新研究 表明,使用CSS Comb (按照類(lèi)型排序) 對(duì) CSS 進(jìn)行排序,按類(lèi)型順序聲明,Gzip 壓縮文件大小平均達(dá)到 2.7%,而按字母順序排序壓縮的文件大小平均達(dá)到 1.3%。
Sass 中一個(gè)正在被眾多開(kāi)發(fā)者濫用的功能,就是選擇器嵌套。選擇器嵌套為樣式表作者提供了一個(gè)通過(guò)局部選擇器的相互嵌套實(shí)現(xiàn)全局選擇的方法。
比如下述Sass選擇器的嵌套:
.foo {
.bar {
&:hover {
color: red;
}
}
}
生成的 CSS:
.foo .bar:hover {
color: red;
}
從 Sass3.3 開(kāi)始,可以在同一行中使用最近選擇器引用(&
)來(lái)實(shí)現(xiàn)高級(jí)選擇器,比如:
.foo {
&-bar {
color: red;
}
}
生成的 CSS:
.foo-bar {
color: red;
}
這種方式通常被用來(lái)配合 BEM 全名方式使用,基于原選擇器(比如 .block
)生成 .block__element
and .block--modifier
選擇器。
傳說(shuō)中,使用 &
能在當(dāng)前選擇器下產(chǎn)生新的選擇器,這樣代碼庫(kù)中選擇器無(wú)法控制,因?yàn)樗麄儽旧聿淮嬖?/p>
選擇器嵌套最大的問(wèn)題是將使最終的代碼難以閱讀。開(kāi)發(fā)者需要花費(fèi)巨大精力計(jì)算不同縮進(jìn)級(jí)別下選擇器具體的表現(xiàn)效果。CSS 最終的表現(xiàn)效果往往不是淺顯易懂的。
選擇器越具體則聲明語(yǔ)句越冗長(zhǎng),而且對(duì)最近選擇器的引用(&
)也越頻繁。在某些時(shí)候,出現(xiàn)混淆選擇器路徑和探索下一級(jí)選擇器的錯(cuò)誤率很高,這非常不值得。
為了防止此類(lèi)情況,我們今年就 the Inception rule 討論了很多很多。它建議嵌套不要超過(guò)三層,我的一件比較激進(jìn),建議盡量避免使用嵌套。
雖然我們?cè)谙乱还?jié)看到這條規(guī)則有一些例外,但這一觀(guān)點(diǎn)還是很受歡迎的。更多信息請(qǐng)閱讀:《小心嵌套陷阱》 和 《避免選擇器的過(guò)渡嵌套》.
首先,在最外層選擇器中嵌套偽類(lèi)和偽元素是被允許,也是受推薦的。
.foo {
color: red;
&:hover {
color: green;
}
&::before {
content: 'pseudo-element';
}
}
使用選擇器嵌套選擇偽類(lèi)和偽元素不僅僅有道理的(因?yàn)樗奶幚砉δ芘c選擇器緊密相關(guān)),而且有助于保持總體的一致性。
當(dāng)然,如果使用類(lèi)似 .is-active
類(lèi)名來(lái)控制當(dāng)前選擇器狀態(tài),也可以這樣使用選擇器嵌套。
.foo {
// …
&.is-active {
font-weight: bold;
}
}
這并不是最重要的,當(dāng)一個(gè)元素的樣式在另一個(gè)容器中有其他指定的樣式時(shí),可以使用嵌套選擇器讓他們保持在同一個(gè)地方。
.foo {
// …
.no-opacity & {
display: none;
}
}
這所有的一切,有些是無(wú)關(guān)緊要的細(xì)節(jié),關(guān)鍵是要保持一致性。如果你覺(jué)得完全有信心搞定選擇器嵌套,然后你就使用了選擇器嵌套??赡氵€要確保你的整個(gè)團(tuán)隊(duì)也能搞定選擇器的嵌套。