CSS通用類和“關注點分離” - 眾成翻譯
http://www.zcfy.cc/article/css-utility-classes-and-quote-separation-of-concerns-quote-4149.html
CSS通用類和“關注點分離”
過去的幾年里绒尊,我編寫CSS的方式已經(jīng)從“語義化”轉變?yōu)椤昂瘮?shù)式”(經(jīng)常被這樣稱呼)了缸匪。
用“函數(shù)式”方式編寫css可以使許多開發(fā)者的內心激動起來。所以我想介紹一下我是如何做到的蟀淮,并且和大家分享一些經(jīng)驗和見解淳玩。
第 1 階段: "語義化" CSS
當你正努力學習如何把CSS寫的更好地時候谆奥,會有人告訴你最好的方法是“關注點分離”见坑。
這種方式的主旨是粹污,你的HTML只能包含與內容相關的信息段多,所有與樣式相關的信息都應寫在CSS里。
看下面這段HTML:
<p>
Hello there!
</p>
看到.text-center
類了嗎壮吩?文本居中屬于樣式进苍,因此這段代碼違背了“關注點分離”原則,因為我們把樣式信息混入HTML了鸭叙。
那么推薦的方法是觉啊,根據(jù)內容來給元素定義類名。然后這些類名就如同一個鉤子递雀,你可以在CSS中用這些類名給標簽增加樣式:
<style>
.greeting {
text-align: center;
}
</style>
<p>
Hello there!
</p>
這種方法的典范是禪意花園柄延。如果使用“關注點分離”原則,那么想要改變一個網(wǎng)站的樣式只需要更換樣式表就可以了。
我寫碼的過程大致如下:
- 根據(jù)設計稿把需要的標簽寫好(這里拿一個作者簡介卡作為例子):
<div>
<img src="http://p0.qhimg.com/t01e2c15da7b6fa9aba.jpg" alt>
<div>
<h2>Adam Wathan</h2>
<p>
Adam is a rad dude who likes TDD, Active Record, and garlic bread with cheese. He also hosts a decent podcast and has never had a really great haircut.
</p>
</div>
</div>
- 根據(jù)內容添加一兩個具有描述性的類名:
- <div>
+ <div>
<img src="http://p0.qhimg.com/t01e2c15da7b6fa9aba.jpg" alt>
<div>
<h2>Adam Wathan</h2>
<p>
Adam is a rad dude who likes TDD, Active Record, and garlic bread with cheese. He also hosts a decent podcast and has never had a really great haircut.
</p>
</div>
</div>
- 將這些類名作為“鉤子”搜吧,在我們的CSS/Less/Sass中給新標簽添加樣式:
.author-bio {
background-color: white;
border: 1px solid hsl(0,0%,85%);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
> img {
display: block;
width: 100%;
height: auto;
}
> div {
padding: 1rem;
> h2 {
font-size: 1.25rem;
color: rgba(0,0,0,0.8);
}
> p {
font-size: 1rem;
color: rgba(0,0,0,0.75);
line-height: 1.5;
}
}
}
下面是結果演示:
See the Pen
"Semantic" mapping layer (terrible idea!)
by Adam Wathan (@adamwathan) on
我認為這很有道理市俊,所以很長一段時間都是這么寫HTML和CSS的。
但后來滤奈,我感覺有點兒不對勁摆昧。
雖然我將“關注點分離”了,但HTML和CSS還是有很明顯的耦合蜒程。大多數(shù)時候我的CSS看起來就像是HTML標簽的鏡子绅你,嵌套的CSS選擇器將HTML結構完全映射出來了。
我的標簽確實與樣式分離了昭躺,但我的CSS卻與HTML結構有很強的聯(lián)系忌锯。
也許是我的分離做的不夠徹底。
第 2 階段: 讓樣式與結構解耦
在尋找解決辦法的過程中领炫,我發(fā)現(xiàn)大家更傾向于給標簽添加更多的類名偶垮,這樣定義起來就會更直觀。能夠保持較低的優(yōu)先級帝洪,并使CSS更少的依賴DOM結構似舵。
倡導這種思想并且最廣為人知的技術是
BEM.
用類似BEM的方法,我們的作者簡介標簽看上去就會是這樣:
<div>
<img src="http://p0.qhimg.com/t01e2c15da7b6fa9aba.jpg" alt>
<div>
<h2>Adam Wathan</h2>
<p>
Adam is a rad dude who likes TDD, Active Record, and garlic bread with cheese. He also hosts a decent podcast and has never had a really great haircut.
</p>
</div>
</div>
...CSS是這樣:
.author-bio {
background-color: white;
border: 1px solid hsl(0,0%,85%);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.author-bio__image {
display: block;
width: 100%;
height: auto;
}
.author-bio__content {
padding: 1rem;
}
.author-bio__name {
font-size: 1.25rem;
color: rgba(0,0,0,0.8);
}
.author-bio__body {
font-size: 1rem;
color: rgba(0,0,0,0.75);
line-height: 1.5;
}
對我來說這已經(jīng)是很大的進步了葱峡。我的標簽看上去仍然很“語義化”且與樣式分離砚哗,而且現(xiàn)在CSS也與標簽結構解耦合了。額外的好處是避免了不必要的選擇器嵌套砰奕,保持了低優(yōu)先級蛛芥。
但是很快我又陷入了兩難境地。
處理相似的組件
話說我需要給網(wǎng)站加個新的內容:用一個卡片布局來展示文章預覽脆淹。
話說這個文章預覽卡片的頭部是一張照片常空,下邊是內容部分,包括一個粗體標題和一些較小的正文文本盖溺。
話說它看起來跟作者簡介非常像漓糙。
[圖片上傳失敗...(image-f0f4c9-1513407635278)]
如果用“關注點分離”的原則,如何處理最好呢烘嘱?
我們不能將
.author-bio
的類名用在文章預覽卡中昆禽,因為這不符合語義化原則。所以我們肯定要用
.article-preview
來給組件命名蝇庭。
下邊將是標簽的樣子:
<div>
<img src="http://p0.qhimg.com/t01f3a22fad3306d5cf.webp" alt>
<div>
<h2>Stubbing Eloquent Relations for Faster Tests</h2>
<p>
In this quick blog post and screencast, I share a trick I use to speed up tests that use Eloquent relationships but don't really depend on database functionality.
</p>
</div>
</div>
但是我們應該如何處理CSS呢醉鳖?
選擇 1: 復制樣式
一種方法是直接復制.author-bio
的樣式,然后重命名一下:
.article-preview {
background-color: white;
border: 1px solid hsl(0,0%,85%);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.article-preview__image {
display: block;
width: 100%;
height: auto;
}
.article-preview__content {
padding: 1rem;
}
.article-preview__title {
font-size: 1.25rem;
color: rgba(0,0,0,0.8);
}
.article-preview__body {
font-size: 1rem;
color: rgba(0,0,0,0.75);
line-height: 1.5;
}
這樣沒問題哮内,但很明顯不夠簡潔盗棵。如果這個組件在樣式上與.author-bio
只有稍微的不一樣(可能是不同的填充或字體顏色)壮韭,改起來也會非常容易。
選擇 2:用
@extend
拓展作者簡介卡
另一種選擇是使用預處理器的
@extend
屬性纹因。使你可以借用已經(jīng)在
.author-bio
組件定義好的樣式:
.article-preview {
@extend .author-bio;
}
.article-preview__image {
@extend .author-bio__image;
}
.article-preview__content {
@extend .author-bio__content;
}
.article-preview__title {
@extend .author-bio__name;
}
.article-preview__body {
@extend .author-bio__body;
}
一般不建議使用
@extend
屬性喷屋。但撇開這件事, 這樣可以解決我們的問題對嗎?
我們移除了CSS中重復的部分瞭恰,并且標簽與樣式仍然是分離的屯曹。
但是讓我們再看一個選項...
Option 3: 創(chuàng)建與內容無關的組件
從“語義化”角度來看,.author-bio
和
.article-preview
組件并沒有共同處惊畏。一個是作者簡介卡, 一個是文章預覽卡恶耽。
但是正如我們已經(jīng)看到的,他們從設計的角度來看有很多共同點颜启。
因此偷俭,我們可以這樣做焰盗。創(chuàng)建一個新的組件勾徽,根據(jù)他們的共同點起類名,然后在兩個內容中復用挚躯。
我們給它起名叫
.media-card
乳规。
下面是CSS:
.media-card {
background-color: white;
border: 1px solid hsl(0,0%,85%);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.media-card__image {
display: block;
width: 100%;
height: auto;
}
.media-card__content {
padding: 1rem;
}
.media-card__title {
font-size: 1.25rem;
color: rgba(0,0,0,0.8);
}
.media-card__body {
font-size: 1rem;
color: rgba(0,0,0,0.75);
line-height: 1.5;
}
...我們的作者簡介標簽看起來會像下邊這樣:
<div>
<img src="http://p0.qhimg.com/t01e2c15da7b6fa9aba.jpg" alt>
<div>
<h2>Adam Wathan</h2>
<p>
Adam is a rad dude who likes TDD, Active Record, and garlic bread with cheese. He also hosts a decent podcast and has never had a really great haircut.
</p>
</div>
</div>
...我們的文章預覽看起來會像下邊這樣:
<div>
<img src="http://p0.qhimg.com/t01f3a22fad3306d5cf.webp" alt>
<div>
<h2>Stubbing Eloquent Relations for Faster Tests</h2>
<p>
In this quick blog post and screencast, I share a trick I use to speed up tests that use Eloquent relationships but don't really depend on database functionality.
</p>
</div>
</div>
這種方法也不會有重復的CSS,但現(xiàn)在是不是“結構與樣式混合”了合呐?
我們希望兩個文本片段都使用.media-card
做樣式暮的。那如果我們想改變作者簡介卡的樣式,而不改變文章預覽的淌实,那該怎么辦呢冻辩?
之前,我們只需要打開樣式表拆祈,給需要改變的組件隨便添加樣式就可以了恨闪。但現(xiàn)在我們需要編輯HTML!這很糟放坏!
但讓我們先花幾分鐘換個角度想一想咙咽。
如果我們要添加一個有著相同樣式的新內容,該怎么辦呢淤年?
如果用“語義化”方法钧敞,我們需要寫一段新的HTML,添加一些根據(jù)內容起的類名麸粮,打開樣式表溉苛,為新的內容添加一個新的CSS樣式組件,并通過復制弄诲、@extend
愚战、mixin來借用已經(jīng)存在的樣式。
如果用與內容無關的
.media-card
類名, 我們所需要做的只是寫一段新的HTML,不需要修改樣式表寂玲。
如果我們真的將“結構與樣式混合”了塔插,那么無論HTML還是CSS,不都得修改嗎敢茁? (譯者:作者想表達的是佑淀,其實結構與樣式還是分離的。)
“關注點分離”是個稻草人
當你用"關注點分離"的原則來思考HTML和CSS的關系時彰檬,就會是非黑即白的伸刃。
分離了(好!)或者沒分離(糟糕7瓯丁)捧颅。
這并不是思考HTML與CSS關系的正確方式。
相反,
要從依賴的角度來思考
有兩種編寫HTML和CSS方式:
-
"關注點分離"CSS依賴HTML较雕。
根據(jù)內容給類起名(例如
.author-bio
)碉哑,將CSS視為HTML的附屬品。
HTML是獨立的亮蒋。他并不在乎你是怎么定義它的扣典,它只暴露像.author-bio
這樣的鉤子。
另一方面慎玖,你的CSS并不是獨立的贮尖。他需要知道你的HTML暴露了哪些類名,然后通過這些類名給HTML添加樣式趁怔。
在這個模型中湿硝,你可以任意編寫HTML,但CSS卻不能被復用润努。
-
"結構與樣式混合"HTML依賴CSS
根據(jù)設計稿提煉出樣式相同的部分关斜,并用與內容無關的名字作為類名,就是將HTML作為CSS的附屬品铺浇。
CSS是獨立的痢畜。它并不關注自己被應用的地方內容是什么。他只是提供一系列代碼塊鳍侣,哪個標簽需要就添加在哪裁着。
HTML不是獨立的。它需要使用由CSS提供的類拱她, 它需要知道有哪些類是定義好的二驰,并且將這些類結合起來,來完成跟設計稿一樣的樣式秉沼。
在這個模型中桶雀,你的CSS是可復用的矿酵,但你的HTML不能隨意編寫。
CSS禪意花園采用第一種方式矗积,而UI框架如Bootstrap
或
則用的第二種方式全肮。
兩種方法本質上都不是“錯誤的”。只是你要弄清楚你所在的語境中棘捣,什么才是最重要的辜腺。基于這點乍恐,再決定你要用哪種方法评疗。
對于你正在編寫的項目, 什么會更有價值: 可以隨意編寫的HTML,還是可復用的CSS茵烈?
選擇可重用性
當我閱讀了Nicolas Gallagher的
關于HTML語義化和前端架構一文后百匆,我改變了自己的觀點。
我不會在這里重申他的所有觀點呜投。但毫無疑問加匈,這個博客給了我深刻印象。我完全相信仑荐,對于我所編寫的各種項目來說雕拼,選擇可復用的CSS是最正確的選擇。
第3階段: 與內容無關的CSS組件
此時我的目標很清楚粘招,就是避免根據(jù)內容來創(chuàng)建類名悲没。而是盡可能使起的類名復用性更高。
比如類名看上去如下:
.card
-
.btn
,.btn--primary
,.btn--secondary
.badge
-
.card-list
,.card-list-item
.img--round
-
.modal-form
,.modal-form-section
...等等等等.
當我開始專注于創(chuàng)建可復用的類名時男图,我注意到一些事情:
組件做的事情越多,組件細節(jié)越多甜橱,越難以服用逊笆。
下面是一個直觀的例子。
話說我們要建立一個表單岂傲,包括幾個表單部分和一個在底部的提交按鈕难裆。
如果我們將所有表單內容都視為.stacked-form
組件的一部分,我們就會把按鈕命名成
.stacked-form__button
:
<form class="stacked-form" action="#">
<div>
</div>
<div>
</div>
<div>
<button class="stacked-form__button">Submit</button>
</div>
</form>
但也許在我們的網(wǎng)站中還有另外一個按鈕镊掖,它并不是表單的一部分乃戈,但是卻跟表單按鈕看上去一樣。
給這個按鈕使用
.stacked-form__button
類名就不太合適了亩进,它不是.stacked-form
組件的一部分症虑。
這兩個按鈕在各自的頁面上都是主要行為,那么如果我們根據(jù)組件的共同特征來給按鈕命名的話归薛,就是.btn--primary
谍憔,完全去掉
.stacked-form__
前綴匪蝙。
<form class="stacked-form" action="#">
<div>
- <button class="stacked-form__button">Submit</button>
+ <button class="btn btn--primary">Submit</button>
</div>
</form>
話說現(xiàn)在我們想讓.stacked-form
組件跟前文中的作者簡介、文章預覽一樣习贫,使用卡片樣式的布局逛球。
一種方法是創(chuàng)建一個修飾符并將其應用于此表單:
- <form class="stacked-form" action="#">
+ <form class="stacked-form stacked-form--card" action="#">
</form>
但是如果我們已經(jīng)有了.card
類,那我們?yōu)楹尾挥靡呀?jīng)存在的.card
和.stacked-form
組合起來苫昌,實現(xiàn)設計稿的樣式呢颤绕?
+ <div>
<form class="stacked-form" action="#">
</form>
+ </div>
用這個方法,我們可以用.card
類作為任何內容的容器祟身,然后.stacked-form
類則可以用在任何容器內奥务。
我們已經(jīng)從組件中提取出了更多的可復用部分,
并且我們不需要編寫任何新的CSS
構建子組件
話說我們需要在表單的底部添加另一個按鈕月而,并且和之前的按鈕之間有一點兒距離:
<form class="stacked-form" action="#">
<div>
<button class="btn btn--secondary">Cancel</button>
<button class="btn btn--primary">Submit</button>
</div>
</form>
一種方法是創(chuàng)建一個新的子組件汗洒,例如.stacked-form__footer
,為每個按鈕添加一個類父款,例如.stacked-form__footer-item
溢谤,并使用子選擇器添加一些邊距:
<form class="stacked-form" action="#">
- <div>
+ <div>
- <button class="btn btn--secondary">Cancel</button>
- <button class="btn btn--primary">Submit</button>
+ <button class="stacked-form__footer-item btn btn--secondary">Cancel</button>
+ <button class="stacked-form__footer-item btn btn--primary">Submit</button>
</div>
</form>
CSS看上去就像下邊這樣:
.stacked-form__footer {
text-align: right;
}
.stacked-form__footer-item {
margin-right: 1rem;
&:last-child {
margin-right: 0;
}
}
那如果我們在子導航或者header中遇到相同的問題時,怎么辦憨攒?
我們不能在.stacked-form
組件之外復用.stacked-form__footer
世杀, 因此也許我們會在header里新建一個新的子組件:
<header>
<h2>New Product</h2>
+ <div>
+ <button class="header-bar__action btn btn--secondary">Cancel</button>
+ <button class="header-bar__action btn btn--primary">Save</button>
+ </div>
</header>
...但現(xiàn)在我們不得不把已經(jīng)寫好的.stacked-form__footer
類,復制到新的.header-bar__actions
子組件中肝集。
這非常像剛開始我們根據(jù)內容來給類命名時遇到的問題瞻坝,對不對?
解決這個問題的一個方法是杏瞻,想出一個全新的組件所刀,這個組件易于復用,并且易于組合捞挥。
例如我們可以給它起名
.actions-list
:
.actions-list {
text-align: right;
}
.actions-list__item {
margin-right: 1rem;
&:last-child {
margin-right: 0;
}
}
現(xiàn)在我們可以完全擺脫
.stacked-form__footer
和.header-bar__actions
子組件, 而是用.actions-list
在各自的場景中替換他們:
<form class="stacked-form" action="#">
<div>
<div>
<button class="actions-list__item btn btn--secondary">Cancel</button>
<button class="actions-list__item btn btn--primary">Submit</button>
</div>
</div>
</form>
<header>
<h2>New Product</h2>
<div>
<button class="actions-list__item btn btn--secondary">Cancel</button>
<button class="actions-list__item btn btn--primary">Save</button>
</div>
</header>
但如果其中一個.actions-list
支持左對齊浮创,而另一個支持右對齊,我們應該怎么辦呢砌函?難道要用.actions-list--left
和
.actions-list--right
來作修飾嗎斩披?
第4階段: 內容無關組件 + 通用類
給這些組件起名字總是很耗費時間。
當你創(chuàng)建例如.actions-list--left
這樣的修飾類名時, 你完全就是在創(chuàng)建一個新的組件讹俊,這個組件只是為了適配一個CSS屬性垦沉。 因為類名里已經(jīng)有了left
,因此你不可能欺騙任何人說這是“語義化”的(譯者:作者的意思是結構與樣式在這里是混合的)仍劈。
如果我們有一個組件需要使用left-align和right-align來修飾厕倍,那我們用這個當做新的組件類名來用,合適嗎贩疙?
這個就回到了當初我們面臨的一個相同問題绑青,那個問題我們是用.actions-list
子組件替代
.stacked-form__footer
和
.header-bar__actions
來解決的:
我們傾向于組合诬像,而不是復制
那么如果我有兩個地方用到.actions-list
組件,一個需要左對齊闸婴,而另一個需要右對齊坏挠,那我們怎么用組合的方式來解決這個問題呢?
創(chuàng)建好通用類
要用組合的方法解決這個問題邪乍,我們就需要給我們的組件添加一個新的可復用的類名降狠,以便達到我們想要的效果:
我們已經(jīng)想用
.actions-list--left
和
.actions-list--right
來當做類名了,那么給這些新的類起名為.align-left
和
.align-right
也是合情合理的:
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
現(xiàn)在我們可以用組合的方式使我們的表單按鈕左對齊了:
<form class="stacked-form" action="#">
<div>
<div>
<button class="actions-list__item btn btn--secondary">Cancel</button>
<button class="actions-list__item btn btn--primary">Submit</button>
</div>
</div>
</form>
...在header標簽里的按鈕右對齊:
<header>
<h2>New Product</h2>
<div>
<button class="actions-list__item btn btn--secondary">Cancel</button>
<button class="actions-list__item btn btn--primary">Save</button>
</div>
</header>
不要害怕
如果在HTML的類名中見到"left" 和 "right"會使你感到不舒服庇楞,請記住榜配,我們已經(jīng)不是在使用“關注點分離”方法給類命名了,我們是根據(jù)設計稿提煉出通用的部分來給類命名的吕晌。所有類名中帶有樣式的描述是很自然的蛋褥。
不要在假裝.stacked-form
比.align-right
更“語義化”了。他們都是在決定如何給標簽添加樣式之后命名的睛驳。我們在標簽中使用這些類名是為了達到特殊的樣式效果烙心。
我們正在寫依賴于CSS的HTML。如果我們想把類名從.stacked-form
改為.horizontal-form
的話乏沸,那我們就是在改變標簽淫茵,而不是CSS。
刪除無用的抽象
解決了這個問題后蹬跃,一件有趣的事情出現(xiàn)了〕妆瘢現(xiàn)在我們的.actions-list
組件基本上沒用了。它之前的功能只有一個就是把內容右對齊蝶缀。
讓我們把它刪除:
- .actions-list {
- text-align: right;
- }
.actions-list__item {
margin-right: 1rem;
&:last-child {
margin-right: 0;
}
}
但現(xiàn)在丹喻,沒有了.actions-list
類,而只有.actions-list__item
類翁都,看上去有些奇怪碍论。有沒有一個方法,可以使我們在沒有創(chuàng)建.actions-list__item
組件的情況下荐吵,解決我們最初的問題?
如果你回想一下赊瞬,我們創(chuàng)建這個組件的原因先煎,只是為了在兩個按鈕之間增加一些間距。對于一組按鈕來說.actions-list
是非常恰當?shù)拿智山В驗樗芡ㄓ貌⑶铱梢院芎玫谋粡陀檬硇5隙ㄒ矔羞@種情況,就是我們需要在兩個元素之間設置相同的間距谤绳,但這個元素并不屬于.actions
組件占锯,對不袒哥?
也許我們可以創(chuàng)建一個更適合復用的名字,比如
.spaced-horizontal-list
怎么樣消略?我們已經(jīng)刪除了
.actions-list
,因為他是一個不需要任何樣式的子組件堡称。
間距通用類
如果只是子元素需要樣式,也許只給子元素添加樣式會比使用偽選擇器把它們當做一個組件來寫樣式更簡單呢艺演?
給元素添加間距的却紧,復用性更好的方法也許是給它取這樣一個名字,一看便知“這個元素應該與它周圍的元素有一些間距”胎撤。
我們已經(jīng)添加了諸如.align-left
和
.align-right
的通用類, 那我們再加一個只能添加右邊距的新通用類怎么樣晓殊?
讓我們來創(chuàng)建一個通用類,例如
.mar-r-sm
伤提。功能是在一個元素的右邊添加一段距離的間距:
- .actions-list__item {
- margin-right: 1rem;
- &:last-child {
- margin-right: 0;
- }
- }
+ .mar-r-sm {
+ margin-right: 1rem;
+ }
下面是我們的表單和header現(xiàn)在的樣子:
<form class="stacked-form" action="#">
<div>
<button class="btn btn--secondary mar-r-sm">Cancel</button>
<button class="btn btn--primary">Submit</button>
</div>
</form>
<header>
<h2>New Product</h2>
<div>
<button class="btn btn--secondary mar-r-sm">Cancel</button>
<button class="btn btn--primary">Save</button>
</div>
</header>
.actions-list
組件已經(jīng)沒有了, 我們的CSS量更小, 類更利于復用巫俺。
第5階段: 實用性第一的 CSS
在達到這個階段的時候,我剛剛建立了一整套實用類肿男,用于常見的視覺微調介汹,例如:
文字的 大小、顏色次伶、粗細
邊框的 顏色痴昧、寬度、位置
背景的 顏色
Flexbox 通用類
Padding 和 margin 的助手
你甚至可以在不寫新的CSS的情況下冠王,創(chuàng)建一個全新的UI組件赶撰,這真是件令人興奮的事情。
從我的一個項目中來看下這個“產品卡”組件:
[圖片上傳失敗...(image-381772-1513407635276)]
以下是我的標記:
<div>
<a href>
<img src>
</a>
<div>
<div>
<a href>
Test-Driven Laravel
</a>
</div>
<a href>
@icon('link')
</a>
</div>
<div>
<div>
@icon('currency-dollar', 'icon-sm text-dark-softest mr-4')
<span>$3,475</span>
</div>
<div>
@icon('user', 'icon-sm text-dark-softest mr-4')
<span>25</span>
</div>
</div>
</div>
第一眼看到這么多類名柱彻,也許會使你感到失望豪娜。但是說我們確實想使它成為一個真正的CSS組件,而不是從通用類庫里挑出需要的類名進行組合哟楷。那么我們應該怎么稱呼它呢瘤载?
我們不打算使用根據(jù)具體內容而起的類名,因為那樣的話我們的組件只能在特定情境中使用一次卖擅。
難道是向下邊這樣命名鸣奔?
.image-card-with-a-full-width-section-and-a-split-section
當然不是,這看上去太荒謬了惩阶。 我們更傾向于將它組合成小一些的組件挎狸,就像我們之前討論過的那樣。
那么這些小一些的組件會是什么断楷?
它可能被放在.card
容器里锨匆。 并不是所有卡片都有陰影,所以我們可以用.card--shadowed
來修飾作為類名冬筒】致啵或者我們可以創(chuàng)建一個新的通用類
.shadow
茅主。這個類可以用在任何元素上。這樣看起來更加利于復用土榴,那我們就這么做吧诀姚!
我們的網(wǎng)站大部分卡片樣式?jīng)]有圓角,但是這個有鞭衩。我們可以給他起名.card--rounded
学搜。但我們的網(wǎng)站會有一些別的元素也有相同幅度的圓角樣式,但他們不是卡片论衍。一個
.rounded
通用類的復用率也許更高瑞佩。
那位于上邊的圖片呢?因為它要保持與父元素寬度一樣坯台,所以給它起名叫.img--fitted
合適嗎炬丸? 這個網(wǎng)站上有些元素也是需要保持與父元素寬度一樣的,但并不一定就是個圖片蜒蕾。 那么僅僅起名為
.fit
也許會更合適稠炬。
...你可以看出我們最終想要的效果。
如果您將足夠的重點放在可復用性上咪啡,那么就會很自然的做到用可復用的通用類來創(chuàng)建組件首启。
加強一致性
使用較小的,可組合的通用類的最大好處是撤摸,你團隊的所有開發(fā)者可以從一個固定的列表里面選擇類名毅桃。
想想有多少次你寫樣式的時候心里想著“這個文本需要更深一點的顏色”,然后用darken()
函數(shù)去微調變量
$text-color
的值准夷?
又或者,
"這個字號應該再小一點,"
然后給你正在開發(fā)的組件添加了
font-size: .85em
钥飞?
看上去你做的很“對”,因為你使用了相對的顏色和相對的字號衫嵌,而不是固定的值读宙。
但如果你想在組件中把字色調深10%,而其他人則想調深12%楔绞,那該怎么辦结闸?當你回過神來的時候,發(fā)現(xiàn)你的樣式表中 有380種獨特的文字顏色.
只要你正在寫新的CSS酒朵,這種情況在每個代碼庫中都會發(fā)生:
GitLab: 380個文本顏色桦锄,202個背景顏色,58個字體大小
Buffer: 124個文本顏色耻讽,86個背景顏色察纯,54個字體大小
HelpScout: 198個文字顏色帕棉,133個背景顏色针肥,67個字體大小
Gumroad: 91個文字顏色饼记,28個背景顏色,48個字體大小
Stripe: 189個文字顏色慰枕,90個背景顏色具则,35個字體大小
GitHub: 157個文字顏色,155個背景顏色具帮,56個字體大小
ConvertKit: 124個文本顏色博肋,123個背景顏色,69個字體大小
這是因為你要寫的每個CSS代碼塊都可以視為一個空白的畫布蜂厅,你可以不受限制的隨意使用任何值匪凡。
你可以通過聲明變量和使用mixins加強代碼的一致性。但
每一行新的CSS仍然有可能會使樣式變得更復雜掘猿,添加更多的CSS是永遠不會使CSS更簡單的病游。
相反,用已經(jīng)存在的CSS類名來給HTML添加樣式稠通,用這種解決方式的話衬衬,空白畫布的問題就不存在了。
想要顏色更深的字色嗎改橘?添加
.text-dark-soft
類滋尉。
想要稍小一些的字號嗎?使用
.text-sm
類飞主。
當項目組中的每個人都可以從一個有限的列表中狮惜,選擇他們的樣式時。CSS樣式表容量就不會跟隨項目變大而直線上升既棺,你就會獲得了相對的自由讽挟。
你仍然應該創(chuàng)建組件
我的觀點與那些頑固的實用派CSS擁護者的區(qū)別之一就是,我不認為你應該僅用通用類來給標簽添加樣式丸冕。
如果你看了一些時下流行的通用類框架耽梅,例如
(一個非常棒的庫),你會發(fā)現(xiàn)他們在純粹的通用類之外胖烛,甚至創(chuàng)建了按鈕的樣式:
<button class="f6 br3 ph3 pv2 white bg-purple hover-bg-light-purple">
Button Text
</button>
哇哦眼姐,讓我來把這個分解一下:
f6
: 使用字體大小中的第6個字號 ( 在 Tachyons 是.875rem)br3
: 使用圓角弧度中的第3號(.5rem)ph3
: 使用上下padding中的第3號(1rem)pv2
: 使用左右padding中的第2號(.5rem)white
: 使用白色字色bg-purple
: 使用字色背景色hover-bg-light-purple
: 鼠標懸浮時使用淺紫色背景作為高亮
如果你需要多個有相同類的按鈕(譯者:這個例子里的標簽由多個類名組合修飾。)佩番,Tachyons 建議的方法是創(chuàng)建一個抽象的HTML模版众旗,而不是CSS。
舉個例子趟畏,如果你正在使用
Vue.js贡歧,你可以創(chuàng)建一個組件,像下邊這樣使用類名:
`<ui-button color="purple">Save</ui-button>`
...組件的內容如下:
<template>
<button class="f6 br3 ph3 pv2" :class="colorClasses">
<slot></slot>
</button>
</template>
<script>
export default {
props: ['color'],
computed: {
colorClasses() {
return {
purple: 'white bg-purple hover-bg-light-purple',
lightGray: 'mid-gray bg-light-gray hover-bg-light-silver',
// ...
}[this.color]
}
}
}
</script>
對于大多數(shù)項目來說,這是一個很好的方法利朵。但
我依然認為創(chuàng)建一個CSS組件比
創(chuàng)建一個基于HMTL模版的組件更加實用律想。
在我工作的這些項目中,會將這7個通用類組合起來绍弟,創(chuàng)建一個新的
.btn-purple
類技即。這要比給標簽一個一個添加顆粒度很小的類名要簡單的多。(譯者:一旦其中一個類名不需要了樟遣,每個用到這個組合類的標簽都要被修改而叼,而作者的方法只要改新添加的
.btn-purple
類就可以了,不需要改HTML)豹悬。
...但是先要用通用類構建樣式
我的方法需要先使用通用類給標簽添加樣式的原因是葵陵,我希望使用通用類來構建網(wǎng)站樣式(譯者:言外之意,不添加任何新樣式)瞻佛。而且只有當他們出現(xiàn)時才會精確的復用重復的部分埃难。
如果你用
作為你的預處理器,你可以將現(xiàn)有的類用minxins混合涤久。那意味著你只需要利用編輯器的多光標功能就可以創(chuàng)建
.btn-purple
組件了:
[圖片上傳失敗...(image-abe0f4-1513407635274)]
但如果你用的是Sass或者其他沒有這種可以將多個通用類進行混合而生成新類的方法的話涡尘,就會稍微麻煩一些,你需要做一些別的工作响迂,才能支持考抄。
當然并不是每個組件類都可以通過多個通用類組合而來。元素間復雜的交互是很難只是用通用類來解決的蔗彤。例如當鼠標懸浮在父元素時希望子元素改變屬性時川梅。所以你要經(jīng)過思考,選擇你認為最簡單的方法然遏。
不要提前抽象
使用“組件優(yōu)先”的方法寫CSS贫途,意味著你創(chuàng)建的組件有可能永遠不會被復用。這種提前抽象就是樣式表如此臃腫和復雜的原因待侵。
以一個導航條為例丢早。在你的應用中重復寫了多少次主導航標簽?
在我的項目里秧倾,我一般只會寫一次怨酝,在我的主布局文件里。
如果你先創(chuàng)建通用類那先,然后將這些通用類組合农猬。只有當你看到令人煩惱的重復時再提取出小型組件,你也許永遠不需要提取一個導航條組件售淡。
相反斤葱,你的導航條看起來也許會像下邊這樣:
<nav>
<div></div>
<div>
</div>
</nav>
這里沒有什么可值得提取的慷垮。
難道這不是內聯(lián)樣式嗎?
這種方法很容易讓人認為是內聯(lián)樣式揍堕。這種樣式是在你需要的時候將一些樣式屬性放在HTML的標簽上换帜。但以我的經(jīng)驗來看,這兩者有很大不同鹤啡。
如果是內聯(lián)樣式的話,你在選擇值的時候是沒有任何約束的蹲嚣。
這個標記可能是
font-size: 14px
,另一個就可能是font-size: 13px
, 還有一個就可能是
font-size: .9em
, 也有可能是
font-size: .85rem
.
當為每個新組件編寫新的CSS時递瑰,它與您所面臨的空白畫布問題相同。
通用類則強迫你選擇:
是用
text-sm
還是
text-xs
?
我們可以用
py-3
和
py-4
嗎?
我是想用
text-dark-soft
還是
text-dark-faint
?
你不能隨意使用你想用的值隙畜,而是只能從一個策劃好的列表里選擇抖部。
你最終只用了10個或者12個文字顏色,而不是380個议惰。
我的經(jīng)驗是先創(chuàng)建通用類慎颗,再創(chuàng)建組件類,可以使設計保持一致性言询,剛開始時這聽起來可能不太直觀俯萎。
如何開始
如果你對這個方法感興趣,下邊是一些值得參考的框架运杭,不妨一試:
我在開發(fā)一個免費的開源LESS框架夫啊,叫做
這個框架是圍繞本文的主旨開發(fā)的,即先創(chuàng)建通用類辆憔,然后從重復的部分中提取可復用的組件: