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