你不要覺得CSS沒什麼重要可言?最近幾年他成為一個熱門的話題,很多人都在討論他。CSS並不是一個簡單的事情,前端開發者可以使用他將頁面製作的更佳漂亮。看得更遠一些,我們更關心的是網站的效能以及如何製作出更好的網站。在本文中,我想分享我最近幾個月的學到的有關於CSS編碼的知識。作為一個程式設計師,我真正感興趣的事情是框架(Architectural)部分。我覺得寫CSS應該需要去改變,為此我深挖了很多知識。我搜尋了好的程式、工作流和原則。這篇文章將帶領大家和CSS一起旅行,很多人都說寫CSS並不是程式設計,我就不同意,我說寫CSS同樣是有趣的,富有挑戰性的。
CSS前處理器
讓我們一起面對吧,在世界上寫CSS並不是一件可笑的事情。CSS前處理器看起來就像CSS,但他更像一個魔術師一樣,使用一些魔法會產生有效的CSS程式碼。這讓你的樣多和瀏覽器之間新增加了一層,這樣不是更加的糟糕嗎?看上去是這樣,但事實不是這樣的,因為CSS前處理器提供了一些真正有用的特性。
連線(Concatenation)
我認為最有價值的東西是連線你的檔案。我相信,你知道使用@import來引用你的.css檔案,告訴瀏覽器獲取這個檔案。這樣做,瀏覽器需要增加一個請求,這樣有點麻煩,因為你可能有很多個這樣的檔案。增加額外的請求,使你的程式效能變得更低。如果您使用CSS前處理器語言,這個問題將不會存在。他們只會編譯你樣式檔案中單個.css檔案。
擴充套件(Extending)
LESS和Sass是最主要的兩個CSS前處理器。我們都支援擴充套件。雖然他們的工作方式略有不同,但他們的想法是一樣的。你可以做一個基本的類(通常稱為mixin)和一群屬性,然後在另一個選擇器匯入這些屬性,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// less .bordered(@color: #000) { border: dotted 2px @color; } .header { .bordered; } .footer { .bordered(#BADA55); } // 編譯出來的CSS .header { border: dotted 2px #000000; } .footer { border: dotted 2px #bada55; } |
這裡的問題是,如果你沒有定義一個引數的mixin,例如剛才的示例:
1 2 3 |
.bordered { border: dotted 2px #000; } |
這是最後編譯的CSS檔案,無論你是否使用都沒有任何關係。因為他是一個有效的選擇器。在Sass中我們可以做得更靈活一些。他有混合(mixins)、擴充套件(extends)和佔位選擇器(placeholders)(如果您想看到他們之間的區別,我強烈建議您閱讀這篇文章)。接下來我們簡單看看Sass是如何工作和編譯的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// sass @mixin bordered($color: #000) { border: dotted 2px $color; } .header { @include bordered; } .footer { @include bordered(#BADA55); } //編譯的CSS .header { border: dotted 2px black; } .footer { border: dotted 2px #bada55; } |
它看起來和前面幾乎相同,但如果我們秋看第二個placeholder的用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// sass %bordered { border: dotted 2px #000; } .header { @extend %bordered; } .footer { @extend %bordered; } // 編譯的CSS .header, .footer { border: dotted 2px #000; } |
這個有兩個優勢,首先不會編譯出.bordered類名,其次會使用組合選擇器,合併相同的樣式,使程式碼變得更加簡潔。
配置(Configuration)
LESS和Sass都支援變數,你可以呼叫這些變數,將將他們作為屬性的值:
1 2 3 4 5 6 |
// sass $brand-color: #009f0A; ... h1 { color: $brand-color; } |
這是一個很好的特性,因為你可以儲存一些重要的東西,比如說顏色或者網格的寬度,將他們存放在同一個地方,如果你需要修改一些不經常改動的程式碼,會變得非常的簡便。
另一個好處理,可以使用變數的插值,如下面演示的方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
// sass @mixin border($side) { border-#{$side}: solid 1px #000; } .header { @include border("left"); } // 編譯的CSS .header { border-left: solid 1px #000; } |
針對前處理器(Against the preprocessors)
- 前處理器是一種工具,您要使用得先要有一個適合的環境。如果你思念將其整合到你的應用程式中,也需要額外的編碼。
- 如果你不想讓你的程式碼變得混亂,你需要有一個觀察機制,用來監測你的檔案。如果這樣,你每個專案開始時就需要執行這個監測機制。
- 通常開發人員只看到.less或.sass檔案,但是輸出是什麼非常重要。你可能有寫得很好的Sass程式碼,但這並不意味著,你最終會有很好的CSS程式碼。可能會有一些特異性的問題,所以需要定期檢測編譯的版本。
擴充套件閱讀
- CSS Preprocessor
- Sass教程
- How to Choose the Right CSS Preprocessor
- CSS Preprocessors: Focused Decisions
- Sass And LESS: An Introduction To CSS Preprocessors
- Poll Results: Popularity of CSS Preprocessors
- On CSS preprocessors
- Understand the Power of Sass and Why You should use CSS Preprocessors
- OOCSS & CSS Preprocessors (pt.1)
- OOCSS & CSS Preprocessors (pt.2
- 8 CSS preprocessors to speed up development time
BEM
好的,我找到了一個新的好玩工具。這個前處理器可以節省大量的時間,但他不能為你寫出好的結構。首先,我開始考慮是一個命名約定,讓我們來看以下的HTML標籤:
1 2 3 4 |
<header class="site-header"> <div class="logo"></div> <div class="navigation"></div> </header> |
可能會寫出對應的樣式:
1 2 3 |
.site-header { ... } .logo { ... } .navigation { ... } |
這樣的樣式能正常的工作,但它有一個問題——閱讀CSS,讓人難於理解。例如,logo是屬於header部分的,你可能有另一個logo要放在頁尾footer處。那麼將新增一個後代選擇器來控制:
1 |
.site-header .logo { ... } |
但是使用這些選擇器並不是很好的主意,因為它始終需要依賴於特定的標記和結構。一旦你把logo移到<header>外面,樣式將會丟失。另外一種做法是給logo新增一個site-header,給其重新命名:
1 |
.site-header-logo { ... } |
很棒了,自己就是說明,但它並不能運用於所有情況之下。例如,我想在12的聖誕節使用一個聖誕節版本的logo。所以,我不能寫:
1 |
.site-header-logo-xmas { ... } |
因為我的邏輯是,寫一個選擇器要像巢狀HTML標籤一樣匹配。
BEM可能解決這樣的情況。這意味著塊(Block)、元素(Element)和修改器(Modifier)和一些建立規則,你可以遵循這些規則。使用BEM,我們的小例將變成:
1 2 3 4 |
.site-header { ... } /* block */ .site-header__logo { ... } /* element */ .site-header__logo--xmas { ... } /* modifier */ .site-header__navigation { ... } /* element */ |
也就是說site-header是我們的塊。那麼logo和navigation是這個塊的元素,聖誕版本的logo是修飾符。也許它看起來簡單,但是它真的很強大。一旦你開始使用它,會發現他能讓你的結構更加的優秀。當然也有反對的理由,那就是因為其語法。是的,或許看起來有點醜,但為了有一個好的結構,我會準備為此做出犧牲。(更好的閱讀請點這和這)。
擴充套件閱讀
- BEM
- BEM Methodlogy
- A New Front-End Methodology: BEM
- Maintainable CSS with BEM
- BEM: The Block, Element, Modifier Approach To Decoupling HTML And CSS
- What is BEM?
- Thoughts About SCSS and BEM
OOCSS
自從我發現BEM,我就開始在思考如何正確的使用我的類名。也許,我的第一件事情要讀一篇關於物件導向的CSS。物件導向程式設計新增了一些抽像的概念,CSS也支援這樣的概念。如果你使用了CSS前處理器,你或多或少知道一些。做為一個編寫程式碼的人,我發現這個概念離我平時程式設計很近,拿JavaScript為例,有兩個主要原則:
單獨的結構和樣式
我們用下面的例子來進行介紹:
1 2 3 4 5 6 7 8 9 10 11 12 |
.header { background: #BADA55; color: #000; width: 960px; margin: 0 auto; } .footer { background: #BADA55; text-align: center; color: #000; padding-top: 20px; } |
其中有一些樣式是重複的,我們可以考慮在另一個類中提取這些相同的樣式:
1 2 3 4 5 6 7 8 9 10 11 12 |
.colors-skin { background: #BADA55; color: #000; } .header { width: 960px; margin: 0 auto; } .footer { text-align: center; padding-top: 20px; } |
所以我們使用colors-skin做為一個物件,用來擴充套件。這樣HTML模板修改成像這樣:
1 2 3 |
<div class="header colors-skin"> ... </div> <div class="colors-skin"> ... </div> <div class="footer colors-skin"> ... </div> |
這樣做有幾個好處:
- 有一個類,它可以使用多次
- 如果需要修改,只需要修改一個地方
- 在CSS樣式中刪除重複的程式碼,使其檔案變得更低
獨立的容器和內容
這裡的想法是,每個元素應該有相同的樣式,不管他放在何處。所以,你應該儘量避免使用像下面演示的選擇器:
1 2 3 |
.header .social-widget { width: 250px; } |
如果你把.social-widget移動.header容器的外面,那麼.social-widget的寬度就變了。尤其是用在頁面上的元件。這也是我鼓勵CSS模組化以及我強烈建議採用更多的時間去嘗試。就我個人而言,以下的原則會將CSS寫得更好。
框架
如果你在GitHub上開啟OOCSS庫你可以看到一個框架。是的,這個框架使用了物件導向的概念,他的CSS有很多很優秀的現成元件。很長一段時間我並不喜歡框架。如果你有在工作使用框架,你會發現他分為兩個部分。事實上,你使用框架處理一些事情,你必須遵守他的規則。不過我更喜歡使用一些微小的框架。當然我不是說我得重新去造輪子,但我總是試圖去尋找一種平衡。通常現成的解決方案導致系統的混亂和複雜。我建議是為一定的目的建立一個特定的東西。如果你試圖去覆蓋一些東西,你總是想到框架中的東西。
但事,我強烈建議你去查尋一個OOCSS框架。這是一個獨特的知識塊,也許它將適合用在你需要的地方。最早是由Nicole Sullivan提出這樣的一個概念。如果你對OOCSS有什麼意向或者想法,可到這裡參加討論。
擴充套件閱讀
- OOCSS
- Object-Oriented CSS
- An Introduction To Object Oriented CSS (OOCSS)
- The Future of OOCSS: A Proposal
- The flag object
- CSS Performance and OOCSS
- OOCSS, for Great Justice
- Nicole Sullivan Talks OOCSS and Process
SMACSS
另一個流行的概念是SMACSS。SMACSS代表可伸縮的和是模組化的CSS結構體系。Jonathan Snook為CSS開發人員介紹了類似於這樣的樣式指南。為了單獨的應用程式將其分為以下幾類:
- 基本(base):用於一個簡單的選擇器的基本樣式,如clearfix
- d佈局(Layout):定義網格
- 模組(Module):一群元素相結合在一起形成的一個模組,比如說header和sidebar
- 狀態(State):元素的不同狀態。如隱藏、按住,擴大等規則定義給物件
- 主題(Them):更多的樣式
我沒有使用SMACSS的任何經驗,便它是非常受歡迎,實際上也能促進你有更好的想法。這比一個框架的概念更強。所以,你不需要遵循任何嚴格的規則、類或者元件。
擴充套件閱讀
- Scalable and Modular Architecture for CSS
- SMACSS: Notes On Usage
- An Introduction To SMACSS Guidelines For Writing CSS
- Let’s Talk SMACSS, Man
- SMACSS
Atomic Design
知道了OOCSS和SMACSS後,請允許我找一個恰當的比喻,請快速登入這個頁面。這裡展示了一個偉大的原子設計概念。它的作者是Brad Frost,眾所周知,他是一位有名的Web開發人員,致力於響應式設計和移動端開發。
這個想法是非常有趣的。以下是一些術語,我們可以說,物質的基本單位是原子。Brad Frost說我們的頁面是用移動的原子構建,一個原子可以是:
1 |
<label>Search the site</label> |
或者
1 |
<input type="text" placeholder="enter keyword" /> |
也就是說原子是包括了一些基本樣式的DOM元素。如顏色、字型大小或者過渡動畫。後來這些部分可以結合成分子,例如:
1 2 3 4 5 |
<form> <label>Search the site</label> <input type="text" placeholder="enter keyword" /> <input type="submit" value="search" /> </form> |
所以表單元素包含了幾個原子。這樣抽象帶來的靈活性,因為我們可以使用相同的原子來構建另一個分子。這樣一來,我們在不同的上下文中可以重用相同的形式:
Brad Frost並沒有停止。生特體是構建分子的東西,遵循同樣的方法,我們可以編寫以下的結構,並將其稱為有機體:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<header> <div class="logo"> </div> <nav> <ul> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Contacts</a></li> </ul> </nav> <form> <label>Search the site</label> <input type="text" placeholder="enter keyword" /> <input type="submit" value="search" /> </form> </header> |
第二件事是模板的概念。他們不是直接相關的化學反應,但被放入了Web的上下文。一旦我們開始結合不同的生物構建我們的模板。後來這些模板形式就是我們最後得到的頁面。
你可能已經使用類似的方法來構建應用程式。然而,命名的東西以一種合理的主式帶來良好的結構。在開發中,你和你的隊友會有很多事情需要去弄明白。分離的原子和分子是很重要的部分,因為它提高了工作效率和讓你更好的維護您的Web應用程式。
擴充套件閱讀
- Atomic Design
- The “Other” Interface: Atomic Design With Sass
- Atomic Design: Some Thoughts and One Example
OrganicCSS
兩個月前,我寫了一篇文章,是有關於Organic的,他是一個用JavaScript寫的一個偉大的小框架。它更像是一種設計模式,我個人非常喜歡。我甚至在好幾個專案中使用了它,並且一切都很順利。如果你有興趣的話,我強烈推薦您閱讀這篇文章。
當我閱讀了Brad Frost的文章,我就已經有了類似於的概念,因為我知道Organic。Brad做的非常的棒,但是我決定更深入的去了解,或嘗試自己在基於原子設計概念的基礎上寫一個小型的框架。我最終選擇了Sass作為前處理器和在Github上建立了一庫organic-css。
原子(Atoms)
讓我勻先從框架最小的一部分開始——原子。維基百科是這樣定義的,原子是物質的基本單位。在CSS中,我認為它是一個屬性和一個屬性值,例如:
1 |
margin-top: 24px; |
僅僅為了寫樣式新增原子而直接新增類名,這並不是我想要的,如果有一個這樣的型別:
1 2 3 4 5 6 |
body { margin-top: 24px; } header { margin-top: 24px; } |
前處理器將會失去他自己作用,因為我想要的結果是這樣的:
1 2 3 |
body, header { margin-top: 24px; } |
在Sass中可以使用placeholders的功能,例如:
1 2 3 4 5 6 7 8 9 10 |
%margin-top-24 { margin-top: 24px; } body { @extend %margin-top-24; } header { @extend %margin-top-24; } |
所以我不得不使用placeholder。這也意味著,我必須要定義很多placeholders,才能使用。在那一刻,我決定,這個框架將只包含原子。也許有一些分子和通用的函式,例如reset.css、網格的定義等等。我想寫點東西,作為一個基礎的CSS開發。也許我會看到專案中的一些模式,將其放在核心處,作為一個開始,並保持乾淨和簡單。
事情變得更加的一致化,我建立了一個mixin作為一個原子。如這個例子:
1 2 3 4 5 6 |
@include define-atom("block") { display: block; } @include define-atom("font-family") { font-family: Georgia; } |
使用這種方法,我建立了一個原子群,並且可以很容易的適合用於每一個專案。你可以點選檢視。我並且拿其他的框架作為對比,讓我更好的去實踐,從中學到很多東西。還可以製作一個mixin分子,將原子相結合在一起:
1 2 3 4 5 6 7 |
@mixin header { // <- molecule called 'header' @include atoms(( block, clearfix, font-family )); } |
分子(Molecules)
分子是一個DOM元素需要樣式,但他沒有子元素。或者他有子元素,便也不會直接連線到它。如<img src=”logo.jpg” />,可能是一個分子。如果你很難在你的頁面識別這些分子,只需要想到什麼是由原子構建就行。有些元素也有可能是構建其他分子的原子。如:
1 2 3 4 5 6 7 8 |
@mixin login-box { @include atoms(( block, font-size-20, margin-top-23, bold )); } |
我們將面對一些很有趣的事。比如說我們的body標籤。他是什麼呢?它是一個分子或其他什麼嗎?當然,這需要一些樣式,但一般在原子中包含其他分子。它應該是其他東西。我的結論是,CSS應該是主要部分,也就是說,如果body樣式需要幾個原子,那麼他就是一個分子。這也就意味著,從理論上講,我不應該附加任何其他的分子給它。這看起來有點不切實際,但在大多數情況下,會讓你使用不同的選擇器,這將是一個好的發展跡象。
細胞器(Organelles)
一旦你認識到這個DOM元素是分子,那麼你可以將其看到是一個細胞器。例如,典型的表單元素是一個很好的細胞器例子,他包含像label、input和textarea這樣的分子。
1 2 3 4 5 |
.login-form { @include label; @include input; @include textarea; } |
這些也許是框軻中的一部分,它緊密的連線到當前應用程式中。原子和分子可能在不同專案之間移動,而細胞器是不可能會移動的。
更抽象(More abstractions)
很多時候你可能想把幾個別的東西放在一起,這樣細胞器變得更加抽象:
1 |
Atom → Molecule → Organelle → Cell → Tissue → Organ → Sys → Organism |
這將面對一個選擇問題,你將如何構建你的CSS。我以前只在一個專案中使用OrganicCSS,到目前為止,我還可以說他是清晰的。我把不同的元素放在他們自己的目錄中和按他們的名命名,這樣我可以很容易的找到他們,並做相應的處理。例如,如果有一個細胞器稱為header,我只需要將其修改為o-header。後來,讓我讀到HTML標記,我就可以看到該元素的CSS樣式就在細胞器資料夾中。
擴充套件閱讀
總結
這是一個很有趣的旅程。我不知道我將來會不會使用OrganicCSS,但這並不是最重要的部分。我能從中學到東西才是最重要的。我知道我必須改變我們的CSS開發過程,我做到了。我認為我們應該多談談CSS的框架。你可以看到我們有很多好的資源。我們必須找到他們,學習他們做什麼以及如何工作。只有這樣我們才可以決定是否使用他們。更好的是,當你看到整個圖片你可以創造一些更適合你的需求。
特別宣告:本文有很多概念也是初次接觸,就對此文進行翻譯,如果有理解錯語的地方,希望不會給您帶來誤解,同時更希望這譯文能改變你對CSS的構建方式,從而找出更適合您或您團隊使用CSS的最佳方式。最後希望更多的同行朋友能指正文中不正確的地方和分享相關的資源(^_^)
譯者手語:整個翻譯依照原文線路進行,並在翻譯過程略加了個人對技術的理解。如果翻譯有不對之處,還煩請同行朋友指點。謝謝!