如何寫好CSS?

好久沒有寫博客了,一是剛入職比較忙,二是由於總有學到新的有趣的東西,停不下腳步來總結一下。最近出差到了帝都,反而能擠出些時間來寫點什麼了,也正好趁着出差作的這個項目討論一下CSS理論。css

我如今面對的CSS基本上就是一個三頭六臂的怪物,一點不誇張,由於真的是三頭六臂,同一個樣式在同一個element上做用了好幾遍,而同一個樣式又分散在4,5個class上,優先級有不少層。能夠看得出這個怪物不是一我的造就的,早期的開發者選擇了SCSS技術,但混亂的import致使了一些基本的樣式被屢次調用,然後面的開發者又爲了擺脫以前的混亂引入了其餘共用樣式,但無濟於事。緣由出在HTML上,CSS依託於HTML沒有被正確的抽象,而HTML又徹底的依賴業務,全部class以業務取名,HTML和CSS基本沒有複用,最終抽出的共用樣式也僅僅是又一次的重複。CSS重構最難的地方在於沒有腳手架,即測試。雖然有一些方法來測試,好比reftest,但還不夠成熟。抱着有總比沒好的心態,CSS被一層又一層的覆蓋了上去。html

真正的問題是什麼?

CSS即層疊樣式表,因此一層一層覆蓋實際上是其本質特徵。真正的問題在於維護,許多人認爲CSS僅是樣式,不是代碼,無需維護,因此任意書寫,只要將本身須要的樣式的優先級設爲最高便可,才致使了深層級CSS的出現,由於每次添加一個樣式就必須比之前的優先級高才能在頁面看到。深層級不只形成維護性下降,可讀性也是一個問題,人不是機器,沒法很優雅的按優先級閱讀,因此很難確認一個樣式用於哪裏,其實還存在許多的冗餘樣式,在任何地方都被覆蓋的樣式。這樣的代碼在擴展性上,一開始反而是有優點的,由於添加一個新class,無需擔憂影響其餘地方,但慢慢隨着項目規模的增大,頁面增多,須要複製樣式的地方也愈來愈多,它們之間又存在微小的差別,設計的更改,需求的變化,這一切都會將這種快餐式的CSS推動柏油坑。由於難以維護,因此沒法響應需求,因此沒法複用,只能複製,惡性循環。前端

正如上面所說的,問題在於可讀性、維護性、擴展性、複用性這幾個方面。因此只要提升它們就能解決問題, 雖然這麼說,也不是如此簡單的。先來談談在CSS中,這些概念都有着怎樣的意義。web

可讀性

有人認爲CSS不是程序,不須要可讀性,有人認爲CSS只要寫出來就有可讀性,由於很簡單。拋開各類預處理器不說,原生CSS結構確實簡單,沒有須要編程的部分,但仍然可能致使混亂。緣由有二,一是CSS能夠層疊,其中涉及到了優先級和做用範圍,若是寫的很差,人很難讀出其中的意義,二是CSS屬性衆多,加上CSS3引入了不少用法獨特的屬性,一個選擇器可能包含幾十個屬性。好比下面這段我隨便寫的CSS代碼:編程

span { -webkit-box-shadow: 6px 4px 4px red; -moz-box-shadow: 6px 4px 4px red; box-shadow: 6px 4px 4px red; } div span { border-width: 4px; border-style: dotted; border-color: blue; } #box { border-left: 2px solid red; border-bottom: 2px solid red; } 

乍一看也沒什麼,都是border,大體能看出來這段CSS只是爲了添加一個紅色的陰影讓box看起來比較立體。但中間的部分彷佛是搗亂的,你可能會說這太傻了,看不到嗎。是的,當這3部分散落在上萬行的CSS中時,確定看不到。因而有人很天然的想起了咱們可愛的瀏覽器,沒錯,在瀏覽器中能夠快速找到做用於目標的CSS樣式,但這也是萬惡之源。首先我假設你不知道中間那部分東西是爲了什麼而寫的,由於你是靠瀏覽器找到它的。而後剩下兩種可能,無論三七二十一改了再說和看看它爲何存在。前者悲劇的可能性是100%,後者悲劇的可能性是90%,由於你已經掉坑裏了,很快咱們會發現要修改它還牽扯到了另外的地方,接着在瀏覽器中探索到另外一個莫名其妙的樣式,當你弄懂所有的時候,你應該已經把上萬行的代碼弄了個一清二楚了,也許最幸運的是,浪費了幾個小時的時間發現只須要修改一行就能達到目的。瀏覽器

固然,咱們能夠天真的認爲,只要把他們寫在一塊兒就能夠了,這樣找起來很簡單。而我將繼續順着這樣的思路來嘗試曝露問題。框架

維護性

所謂物以類聚是頗有道理的,人們習慣將事物歸類,但問題是分類標準,樣式並不關心業務,不管是什麼文字內容,仍是功能有何不一樣,它在意的只是樣式,好比文字的尺寸,間距和寬高,顏色等等。若是簡單的將一個組件的樣式放在一塊兒,勢必帶來的就是小段代碼的重複書寫。不以爲有多嚴重?我來舉個栗子。ide

aside { box-shadow: 6px 4px 4px #AA3343; } nav { box-shadow: 6px 4px 4px #AB3633; } .item { box-shadow: 6px 4px 4px #AA3732; } .item.otherStatus { box-shadow: 6px 4px 4px #AA3132; } 

繼續說上面的例子,box須要陰影,但若是這個項目的UI統一風格,包括sidebar,navigator以及item都須要這樣的陰影呢?再若是,明天客戶或者UX一拍腦殼,這個陰影應該是灰色的不應是紅色的呢?不要繼續天真的認爲全局替換是救命稻草。首先,沒有幾個網站會用red,blur作色調的,你用的應該是#AA3333,這樣的代碼,而後你發現sidebar用了#A43433,而navigator是#AB3633,等等,item有兩個狀態,而兩個狀態對應的顏色是不同的。這怎麼可能?但當你打開瀏覽器的時候你會發現原本就相差無幾的顏色,在陰影中變得如出一轍了,誰看的出來呢,當初使用的時候可能也不過是隨意的在mockup中取的一個顏色。模塊化

大量的重複帶來的不只僅是代碼的冗餘,咱們必須靠人力去同步它們,而人很難保證它們的修改是徹底一致的,尤爲是當它們中引入了一些不一致的獨特的東西時。不要小看CSS,其後果就是進度和人力的壓力,後面就是PM有沒有讀過《人月神話》的事了。工具

確定有人在想,誰讓你當初要寫成這樣呢。咱們在讀代碼的時候最喜歡問,當初爲何要這麼寫?但慢慢的你會讀出它的歷史,有時候它是身不禁己的。這就涉及到了下一個要討論的內容。

擴展性

擴展性是一個具備欺騙性的東西,所謂的擴展性其實就是在現有基礎上再次開發新東西的性能,但我認爲它還必須有前提條件,那就是保持可讀性與維護性。

簡單的追求可維護性是自取滅亡,緣由很簡單,將新舊代碼徹底分離的時候擴展性最高,由於沒必要擔憂對之前的部分有影響,新的樣式能夠隨意發揮。是否是很神奇,這樣想的咱們寫下的代碼,確定就是前面咱們追問的代碼。因此本身回答本身吧,當初沒考慮可讀性和維護性,只想着快點增長新的樣式,就這麼寫了。

那什麼纔是一個好的擴展性呢,簡單來講,就是多功能產品。好比一個box,也許它的樣式就時

複用性

彷佛我一直在說的就是重複,那咱們就來講說複用性,如何才能複用CSS代碼是一個很大的問題,好比粒度,是一兩個屬性進行復用仍是一大組選擇器進行復用呢,再好比對象,是爲了class複用屬性,仍是爲了html複用class呢。這些選擇不算過重要,可是帶來的影響卻很重大,能夠說是整個CSS結構的改變。下面繼續用box的陰影來討論複用。

.shadow { -webkit-box-shadow: 6px 4px 4px #A93334; -moz-box-shadow: 6px 4px 4px #A93334; box-shadow: 6px 4px 4px #A93334; border-left: 2px solid #A93334\9; border-bottom: 2px solid #A93334\9; } 

這樣看起來我有了一個shadow的class能夠給任意的目標加上這個陰影了,但這致使了一個複用的問題,和上面那段搗亂的CSS樣式同樣,若是item已有另外2個border了,那這個class是沒法去除的。因此複用時不只要考慮須要什麼,還要考慮不須要什麼。另一些必須的屬性好比display還有overflow等也是要考慮的,由於user agent的緣由,不少屬性是隱藏在element中的。

如何解決問題?

主流的CSS原則有OOCSS,DRY,SMACSS以及BEM,他們皆是爲解決CSS的各類問題而生。

OOCSS

OOCSS即面向對象的CSS,這裏對象指的是頁面中的元素對象,與傳統編程中的面向對象不太相同,好比不存在方法這種東西,硬要說的話,附加的一些class能夠看做是繼承或者接口之類的東西來實現對象的差別化。好比電商網站中的商品就是一個典型的對象,它們既有許多相同的部分,又有許多差別,寬高、按鈕、圖片、標題等基本佈局都是相同的,而邊距、線框、背景顏色、字號等都是差別化的。由此按照OOCSS的指導原則,咱們應該寫一個product class,而後爲其添加一些border、theme之類的class來差別化它:

.product {
    display: block;
    overflow: hidden;
    float: left; width: 200px; height: auto; } .product-head{...} .product-body{...} .product-foot{...} .product-theme-black { background: black; color: white; } .product-border { border: 1px solid #333; } 

這樣在以上兩種附加class的做用下,咱們在html中就能夠得到4種不一樣的product樣式,隨着附加class增長,product的樣式也會呈指數增長,變幻無窮。這僅僅是一個簡單的例子,意在點出OOCSS的理念,但並無突出它的意義所在。彆着急,先來看看OOCSS的兩大原則。

1. 分離容器與內容

所謂的容器即包裹對象的元素,好比一個div,咱們常常會命名爲wrap、container、body等。那麼如何纔算是分離容器與內容呢?很簡單,一句話,內容在哪均可用。也就是說不該該出現這樣的狀況:

.container .product {
    ...
}

這樣乾的結果就是複用性大大下降,由於只能在這個容器內使用它了。但這並不表明咱們應該將所需的樣式所有一股腦的扔進單獨的class中,對於差別化應該單獨放在一個class中,這纔是OOCSS的精髓。

舉個例子,當咱們既不想犧牲太多性能,又想來個瀑布流顯擺顯擺的時候,大部分前端都會使用column,相似泳道的設計。你想說哦不,這是僞pinterest,可是誰在意呢,用戶是不會有閒工夫拖拽瀏覽器的寬度來鑑別它的,在IE下商品多的時候至少不會太卡。哈,別較真,首先分爲幾個column,而後按照高度往裏填放商品,先來看看下面的代碼吧,我有省略一些樣式避免誤導:

.column { height: auto; width: 200px; } .product { width: 180px; margin-right: 20px; margin-bottom: 10px; } 

看起來不錯,每列200px寬,商品放入其中,水平間距要大,垂直間距要小些纔像column。可是等等,咱們總仍是須要整齊擺放的商品列表的對不對。也許margin並非product的必要屬性,至少它應該是可變的。咱們抽出它來:

.product { width: 180px; } .vertical-product { height: 400px; margin-right: 10px; margin-bottom: 10px; } .horizontal-product { height: auto; margin-right: 20px; margin-bottom: 10px; } 

這樣便將column或list之類的容器與product分開來毫無關係了,即便之後出現了其餘組織形式,只要product的基本結構沒有變均可以直接複用,無非是添加一些附屬樣式到新的xxx-product的class中。另外這樣作還有一個好處,設計邏輯放在了HTML中,CSS更增強大。

什麼是樣式邏輯?商品在瀑布流中不定高,在列表中定高,這就是一種樣式的邏輯,若是用父子選擇器的形式寫在CSS中,那它就失去了自由。而放在HTML經過選擇添加何種附屬class來展示不一樣形式的product,則很是的自由與靈活。另外值得一說的是,margin-bottom是同樣的,但咱們應該各自放在各自的class裏面,緣由很簡單,它們僅僅是一不當心剛好同樣,在設計邏輯中它們並非同樣的bottom,這裏並非重複,而是看起來同樣。若是之後須要改變其中的一個bottom,共用則顯得很是彆扭。

2. 分離皮膚與結構

第二點很容易理解,皮膚(theme)就是視覺效果,即便被剔除網頁也沒有什麼影響的就是皮膚;而結構指地並非像HTML這樣抽象的結構,由於CSS畢竟仍是樣式,因此結構只是相對的頁面結構。

先來看看咱們的product吧,添加一些背景色和邊框:

.product { width: 200px; background: #F6F2F2; border: 1px solid #C4A0A0; } 

看起來還不錯,不過設計師都是自大狂,精心的調色,完美的搭配,絕對不會讓你僅僅使用這麼一次的,頁面的其餘模塊、sidebar甚至是header均可能採用相同的背景顏色與邊框,它們甚至可能互相嵌套。好吧,這其實在設計上是爲了視覺統一,畢竟沒有幾個設計大師能hold住3,4種以上的顏色。因此咱們能作的並非在每一個class中添加這樣的樣式,而是把它提出來成爲獨立的class,緣由就像我開篇說的那樣,顏色爲混沌之源。

.main-bg { background: #F6F2F2; } .main-border { border: 1px solid #C4A0A0; } 

這樣就能夠在頁面中隨時使用主要的設計元素了,並且須要修改時也很是的簡單,不用擔憂有什麼地方漏掉。另外我將背景與邊框分爲了兩個class,緣由仍是設計邏輯應該放在HTML,背景與邊框並非必定同時出現的,二者的關係應該由HTML決定,即便設計上邏輯決定了二者的綁定,在實現時也有可能由於HTML結構而放在兩個不一樣的元素上。

OOCSS強調class,將每組樣式寫成一個class方便HTML中使用,衆多class組合起來能變幻無窮組成一個對象。因此若是是想一勞永逸的寫一套UI做爲開發時使用的樣式,我建議使用OOCSS來進行開發。但它也有缺點,過多的將設計邏輯放在HTML中,極大的自由化了頁面開發時的選擇,若是寫HTML的開發者不能很好的理解整套CSS的結構,較易在HTML中形成class混亂。

DRY CSS

DRY就是Donot repeat youself 不要重複。但其實這個名字有點無趣,哪一個理論不是消除重複呢,但如何消除纔是意義所在。總的來講我認爲DRYCSS與OOCSS是兩個極端,因此我將會以對比的方式來說講DRYCSS的內容。使用DRYCSS很簡單,三步。

1. 分組可複用屬性

DRYCSS跟OOCSS有點像,第一步都是分組樣式,消除重複,但就像我說的,關鍵在於如何。OOCSS將樣式集合看做對象,因此分組的邏輯是,某個元素自己應該是什麼樣的,而DRYCSS則關注重複,不管什麼邏輯,只要是同樣的就應該只有一個。其中粒度是值得思考的問題,若是太細,那隻會成爲一行樣式一組這樣無心義的狀況,若是太粗,又會變成毫無複用性的龐然大物。我認爲能夠將一些有關聯的缺了A時B就沒做用的樣式分爲一組,還能夠將某些慣用搭配分爲一組。下面舉個例子:

{ float: left; position: absolute; display: inline-block; overflow: hidden; } 

這是一組樣式,可用來觸發Block formatting Contexts(塊級格式化上下文),如此就完成了一組樣式。接着再寫2組關於尺寸的樣式吧。

{ width: 960px; height: auto; } { width: 720px; height: 600px; } { width: 220px; height: 600px; } 

這是三組樣式用來佈局,將頁面分爲左右兩部分。

2. 按邏輯爲分組命名

接着咱們來爲其命名,其實就是添加一個ID選擇器,可是咱們並不真的使用它,而是用來標示該組樣式。下面就來命名上面所分組的樣式。

#BLOCK_FORMATTING_CONTEXTS { float: left; position: absolute; display: inline-block; overflow: hidden; } #LAYOUT_FULL { width: 960px; height: auto; } #LAYOUT_CONTENT { width: 720px; height: 600px; } #LAYOUT_SIDEBAR { width: 220px; height: 600px; } 

這一步相似OOCSS的class,它決定了每組樣式所表明的邏輯或用途,然而DRYCSS多了最關鍵的下一步,也是與OOCSS本質區別。

3. 爲各個分組添加選擇器

DRYCSS在使用時和OOCSS有着巨大的差別,在CSS文件中寫入HTML中的class選擇器來使用這些分組後的樣式,而不是直接在HTML中使用CSS文件中寫好的class。

.header, .container, .content-right, .content-left, #BLOCK_FORMATTING_CONTEXTS { float: left; position: absolute; display: inline-block; overflow: hidden; } .header, .navigator, .container, #LAYOUT_FULL { width: 960px; height: auto; } .content-right, .section, #LAYOUT_CONTENT { width: 720px; height: 600px; } .content-right, .sidebar, .profile, #LAYOUT_SIDEBAR { width: 220px; height: 600px; } 

能夠看到,使用DRYCSS時,在HTML中所寫的class將會很是表意,元素自己是什麼用來作什麼,就使用其意義的class命名,並且基本上是一個元素對應一個class,HTML將變的簡單明瞭。另外DRYCSS也是相對於OOCSS的一種逆向思惟,這纔是最有趣的地方。在開發中,不該該像OOCSS那樣思考如何應對將來假象的HTML,而是僅僅思考CSS自己。

總的來講,OOCSS適合開發CSS框架或整套UI模版,是自外向內的UI開發方式;而DRYCSS則適合拯救混沌的HTML,或者增強HTML的結構性和表意性,是自內向外的UI開發方式。這裏的內指地是HTML結構,外指地是CSS樣式。

SMACSS

這是一個相對繁雜的CSS理論,分爲Base、Layout、Module、Status和Theme共五個部分。不過它的核心思想仍然和OOCSS相似,鼓勵使用class。

1. Base 基本屬性

基礎屬性很容易理解,就是最基本的東西,不少樣式簡單的網站都採用一個簡單的二級CSS文件模式,一個base.css通用於全部頁面,而每一個頁面有一個特定的CSS文件,我想這就是Base的雛形。要說具體是什麼,好比reset文件,再好比放置clearfix或BFC的一些相似工具集的文件。

其實最終會發現,在Base中的CSS屬性將會是幾乎全站都要用到的屬性,但我不想這麼描述Base,由於這會誤導人。大多數狀況下,在一個網站創建之初也只會有幾個簡單的頁面,因而這幾個頁面都要用到的屬性就變成了通用屬性,但並非這麼簡單的。隨着網站規模的擴大,需求的增長,設計師們靈感的迸發,所謂的通用和統一也在發生着潛移默化。因此在編寫Base時,應該遵循的基準是,哪些樣式是你作下一個網站時也會想用的,哪些樣式即便設計改變了也只須要改變一些數值和顏色,哪些樣式是一些基本原則;而不該該將目前大部分頁面都在使用的樣式放在Base中,仍是那個道理,它們也許僅僅是剛好相同,而非邏輯一致。

2. Layout 佈局

佈局是一個網站的基本,不管是左右仍是居中,甚至其餘什麼佈局,要實現頁面的基本瀏覽功能,佈局必不可少。SMACSS將這一功能單獨提出也是很是正確的,另外還約定了一個前綴l-/layout-來標識佈局的class。舉個最廣泛的例子。

.l-header {} .l-brand {} .l-navigator {} .l-container {} .l-sidebar {} .l-content {} .l-footer {} 

這就是一個簡單的左右佈局,導航和Logo中規中矩在最頂部。

3. Module 模塊

模塊是SMACSS最基本的思想,同時也是大部分CSS理論的基本,將樣式模塊化就能達到複用和可維護的目的,可是SMACSS提出了更具體的模塊化方案。首先表象上來看,SMACSS中的模塊應該擁有一個名字,而且爲其class名,而模塊其餘class皆覺得前綴。好比:

.product {} .product-title {} .product-image {} .product-border {} .product-shadow {} 

能夠看到例子中product是一個模塊,title和image是包含在模塊內的組件,可用可不用;border和shadow是相似OOCSS的附加class用來改變模塊自己。總之,在模塊內可使用其名稱作前綴任意組織模塊結構,但目前是讓其變得更易用,提升可擴展性和靈活度,若是僅僅爲了某些功能而特地寫一些class就有點有形無實的感受了。

4. State 狀態

狀態常常和JavaScript放在一塊兒使用,它是一種用來標識頁面狀態的class,不管是爲用戶標識仍是用程序標識。仍是一個常見的例子,立刻就明白。active常常用來表示當前的tab,或者當前選中的目標,這就是一種狀態,不管是樣式仍是程序都須要知道它。

SMACSS仍然有一個對應的前綴用於標示狀態class,is-是一個合適的詞,指明某一元素是什麼狀態。

5. Theme 主題

主題就是皮膚,和OOCSS的分離皮膚與結構不謀而合。更重要的是對於可更換皮膚的站點來講,這樣的分離是很是必要的,只須要更換加載的theme文件便可將皮膚更換。

總的來講,SMACSS是一個較爲注意細節與實現的CSS理論,很是適合初涉CSS的人,它可讓你的CSS跑在軌道上而不至於脫軌。其思想也與OOCSS有不少相通之處,若是沒有適合的方案,我建議新手能夠適當的融入OOCSS的思想而使用SMACSS的結構,這樣寫出來的網站樣式至少不會立刻陷入泥沼。

哪個好用呢?

談了許多的CSS原理,已經有點眼花繚亂,到底哪一個好呢?這個問題又歸結到了最佳實踐上,雖然我並不認爲有這樣的實踐,但我認爲一個項目必定會有適合的實踐,好比前面說的,若是你想作一個CSS框架而後再寫HTML,那就用OOCSS;若是你想先寫HTML或者已經有一箇舊的頁面,那DRYCSS應該很適合你;若是新手不知如何下手,那SMACSS能夠指導你入門。

不管如何,在我過去很長一段時間的獨立UI開發「生涯」中,這些狀況我都遇到過,也有一些本身的想法,我想我將在下一篇博客中談談本身對CSS的理解,並嘗試整理出一些相似理論的東西來。

 

原文地址:http://www.tychio.net/tech/2014/03/16/css-principle.html

相關文章
相關標籤/搜索