為什么我們不擅長 CSS,看完這篇你就知道啦!
本文探討了為什么人們在CSS方面表現(xiàn)不佳。文章提到CSS的復(fù)雜性和不斷變化的標準是導(dǎo)致問題的主要原因。作者還討論了開發(fā)者和設(shè)計師之間的溝通問題,以及缺乏足夠的培訓(xùn)和教育。他提到了一些常見的CSS錯誤,例如盒模型和浮動,以及如何避免它們。
下面是正文~~
許多開發(fā)人員一想到 CSS,就會想到彼得-格里芬(Peter Griffin)試圖打開百葉窗。但對其他人來說,CSS 更像是把手伸進《沙丘》中的痛苦之箱,而某個產(chǎn)品經(jīng)理卻拿著匕首抵著他們的脖子,讓他們不敢把手抽出來。
有幾個原因可以解釋為什么科技公司在 CSS 方面一直舉步維艱。
- 我們不擅長教授 CSS。雖然有大量優(yōu)秀的 CSS 實踐者在分享他們的知識(如 Stephanie Eckles、Kevin Powell 和 Adam Argyle 等),但很多人在大學(xué)或訓(xùn)練營中學(xué)習(xí) HTML 和 CSS,而這些人的知識可能并不淵博,他們使用過時的技術(shù),或者為了偏愛 Bootstrap 或 Tailwind 等框架而忽略了基礎(chǔ)知識。因此,很多人對 HTML 和 CSS(網(wǎng)絡(luò)的基本構(gòu)件)的了解并不深入。
- 我們不擅長招聘 CSS。幾乎每個全?;蚯岸斯こ處煹恼衅感畔⒍紩⒕?HTML、CSS 和 JavaScript 作為必備條件,但在面試求職者時,他們很少會測試 JavaScript 以外的技能。如果公司最終錄用了掌握 CSS 技能的人,那通常是偶然的。如果你沒有掌握這些技能的人,你就無法審查其他人是否掌握這些技能,問題就會一直延續(xù)下去。
- 我們不擅長編寫 CSS。由于缺乏對 CSS 的深入了解,又無法聘請到具備這方面知識的人才,人們不得不通過依賴 Bootstrap/Tailwind 或嘗試使用 JavaScript 來完成所有工作,來避免編寫 CSS。最終,他們把事情搞得過于復(fù)雜,導(dǎo)致 CSS 極難維護。
編寫 CSS 就是將同一套視覺樣式反復(fù)應(yīng)用于各種不同的環(huán)境中,直到你死去
盡管 CSS 技術(shù)取得了最新進展,但許多人仍停留在這種 BEM 思維模式中,試圖完美地封裝一切,以免在進行更改時出現(xiàn)意想不到的結(jié)果。
以 BEM 文檔中的這個例子為例:
.page__header {
padding: 20px;
}
.page__footer {
padding: 50px;
}
這實際上與使用 Tailwind 等框架中的實用工具類并無太大區(qū)別,只不過在任何其他情況下,你都不會使用 page__header 為元素添加 20 像素的 padding 。
使用 Tailwind 的 "原子優(yōu)先"方法,你需要為每一個單獨的設(shè)計決策應(yīng)用一個類,這樣就會產(chǎn)生像他們網(wǎng)站上的這個例子一樣的標記:
<figure class="md:flex bg-slate-100 rounded-xl p-8 md:p-0 dark:bg-slate-800">
<img class="w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto" src="/sarah-dayan.jpg" alt="" width="384" height="512">
<div class="pt-6 md:p-8 text-center md:text-left space-y-4">
<blockquote>
<p class="text-lg font-medium">
“Tailwind CSS is the only framework that I've seen scale
on large teams. It’s easy to customize, adapts to any design,
and the build size is tiny.”
</p>
</blockquote>
<figcaption class="font-medium">
<div class="text-sky-500 dark:text-sky-400">
Sarah Dayan
</div>
<div class="text-slate-700 dark:text-slate-500">
Staff Engineer, Algolia
</div>
</figcaption>
</div>
</figure>
我們基本上是將這些相同的上下文設(shè)計決策(在這個例子中,就是這張卡片看起來如何)轉(zhuǎn)移到標記中的類名上,而不是在我們的CSS中添加新的類名。
那么,答案是什么呢?
我們希望我們的風(fēng)格足夠通用,可以在不同的語境中重復(fù)使用,但又不會太通用,以至于我們不得不在這些語境中不斷重復(fù)自己的風(fēng)格。
簡而言之,我們的想法是用單個類為單個組件設(shè)計樣式,用實用工具類在不同上下文中組成或修改組件,并提供布局以保持頁面之間和頁面內(nèi)部的一致性。
酷酷的樣子
讓我們重構(gòu) Tailwind 網(wǎng)站上的卡片示例。
這張卡片包含一個推薦信,但我們可能想在不同的上下文中使用這種卡片模式。我們的卡片不應(yīng)關(guān)心其內(nèi)部的內(nèi)容。也就是說,在這個特定的卡片示例中,我們不會使用 .card- 對所有內(nèi)容進行限定。這些樣式只決定了卡片容器的外觀。
/* /scss/components/_card.scss */
.cool-card {
border-radius: $radius-medium;
background-color: $color-surface-brand-light;
overflow: hidden;
}
@media (prefers-color-scheme: dark) {
.cool-card {
background-color: $color-surface-brand;
color: $color-text-inverse;
}
}
這里使用的是 SCSS 變量,而不是 CSS 標記的自定義屬性。我喜歡自定義屬性,但有爭議的是,我不喜歡使用標記。
我們的設(shè)計系統(tǒng)不僅定義了我們使用的特定值(顏色、類型、間距),還定義了我們使用這些值的上下文。我們不給開發(fā)人員提供允許他們應(yīng)用任何顏色的實用類(例如 .bg-slate-100 ),我們只希望在特定的上下文中使用特定的顏色。
每當我看到一個 mixin 會對調(diào)色板中的每種顏色進行排查,并為每種顏色創(chuàng)建一個背景色實用工具類時,我都會感到惡心。你永遠不會用到每一種顏色,如果你提供了這樣的選項,你最終會得到一些缺乏足夠?qū)Ρ榷鹊念伾M合。
這就是為什么我使用單獨的標記層來定義上下文。 $color-surface-brand-light 可能指向 $slate-100 。如果我們想更改我們的品牌顏色用于背景的值,我們可以更改一個標記,將其應(yīng)用于不同的組件,而無需查找 $slate-100 的每個實例并將其替換為不同的顏色。
與其讓開發(fā)人員訪問所有令牌,不如將它們抽象到我們的類中,開發(fā)人員可以根據(jù)不同的上下文使用相應(yīng)的類。
此外,由于我們使用的是 SCSS,因此我們可以在標記名上使用更多字數(shù),因為無論如何,它們都會編譯成更小的值。
這個特定卡片中的內(nèi)容包括一張圖片和一個塊狀引文,使用 flexbox 水平排列。讓我們添加一個 flex 工具。
/* /scss/utilities/_flex.scss */
.cool-flex {
--flex-align: center;
--flex-gap: $spacing-16;
display: flex;
align-items: var(--flex-align);
gap: var(--flex-gap);
}
在這里,我們在我們的彈性布局(flex)工具中使用CSS自定義屬性,以便從我們的設(shè)計系統(tǒng)中提供一些常見的默認值。這樣,我們就不需要提供一大堆額外的工具類來支持每個彈性布局屬性的所有可能值。
如果開發(fā)者遇到需要覆蓋默認設(shè)置的情況,他們可以通過在樣式屬性(style attribute)中聲明來實現(xiàn)這一點。在這種情況下,我們不希望圖片和引用塊(blockquote)之間有間隙,因為這將由內(nèi)邊距(padding)來處理。
<figure class="cool-card cool-flex" style="--flex-gap: 0">
...
</figure>
當然,我們可能還想使用其他靈活的屬性,但我堅信需要時才添加,而不是試圖考慮所有可能的使用情況。就這張卡而言,這已經(jīng)足夠了。
在本設(shè)計中,flex 只在視口寬度超過一定值時才會應(yīng)用,因此我們可以創(chuàng)建另一個只在某個斷點以上應(yīng)用的 flex 工具。
/* /scss/utilities/_flex.scss */
@media (width >= $breakpoint-medium) {
.cool-flex-responsive {
--flex-align: center;
--flex-gap: $spacing-16;
display: flex;
align-items: var(--flex-align);
gap: var(--flex-gap);
}
}
我從未真正開發(fā)過需要一個以上斷點的系統(tǒng)(也許有些布局需要斷點,但單個組件不需要),因此我傾向于使用 -responsive 來表示只應(yīng)在某個斷點之上發(fā)生的事情。隨著組件查詢得到更廣泛的支持,基于視口的媒體查詢在類似情況下可能很快就不需要了。
現(xiàn)在,我們還可以在常青樹瀏覽器中使用新的范圍語法進行媒體查詢!我們可以使用 width >= $breakpoint-medium 代替 max-width: $breakpoint-medium 。
圖像
當設(shè)計師在大屏幕和小屏幕之間采用完全不同的設(shè)計時,我有點抓狂。我會盡我所能讓它發(fā)揮作用。
在這里,我們的圖像會從一個小圓圈變成大屏幕上的全尺寸圖像。這可能需要一個獨特的組件。
/* /scss/components/_avatar.scss */
.cool-avatar {
width: $avatar-medium;
height: $avatar-medium;
border-radius: $radius-round;
object-fit: cover;
}
@media (width >= $breakpoint-medium) {
.cool-avatar {
--width: 100%;
max-width: var(--width);
width: auto;
height: auto;
border-radius: 0;
}
我們?yōu)樾∑聊簧系膱A角頭像大小添加了一個標記,并設(shè)置 object-fit 以考慮到長寬比不是正方形的圖像。在大屏幕上,我們使用自定義屬性來覆蓋圖像的寬度。
實際上,我們必須將 .cool-flex 的 --flex-align 屬性重新設(shè)置為默認的 stretch,以支持引用塊(blockquote)中的文本高度超過圖片的情況。因此,我們的 --width 屬性實際上是設(shè)置了最大寬度,而寬度和高度都設(shè)置為自動,由圖片的寬高比來決定。為了補償這一點,我在文本容器中內(nèi)聯(lián)添加了一個 align-self: center。(這是針對一個非常具體的設(shè)計選擇需要考慮的很多事情,但這種情況確實會發(fā)生。)
我們還需要考慮頭像在小屏幕上的定位問題。這就需要一些只出現(xiàn)在小屏幕上的實用類。是的,這些類名有點冗長,但我覺得它們比 md:h-auto 更清晰,而且還利用了邏輯屬性。
/* /scss/utilities/_spacing.scss */
@media (width < $breakpoint-medium) {
.cool-margin-auto-on-small {
margin-inline: auto;
}
.cool-margin-block-start-on-small {
--size: $spacing-32;
margin-block-start: var(--size);
}
}
文本容器
包含我們的引用塊(blockquote)和圖像標題(figcaption)的容器應(yīng)用了一些內(nèi)邊距(padding),同時元素之間也有一些外邊距(margin),并且在小屏幕上文本是居中的?,F(xiàn)在是時候添加更多工具類了!
/* /scss/utilities/_spacing.scss */
:where(.cool-flow) {
--flow-size: $spacing-16;
& > :not(:last-child) {
margin-block-end: var(--flow-size);
}
}
.cool-inset-square-32 {
padding: $spacing-32;
}
/* /scss/utilities/_text.scss */
@media (width < $breakpoint-medium) {
.cool-text-center-on-small {
text-align: center;
}
}
我已經(jīng)將它包含在一個 :where() 偽類函數(shù)中,以將其特異性降低到零,這樣你就可以在需要時使用另一個工具類來覆蓋任何子元素的底部外邊距。
文本
在 Tailwind 的版本中,他們應(yīng)用了 .text-medium 來設(shè)置 blockquote 文本和其下方 figcaption 的字體權(quán)重。我們可以使用類似的類,將其應(yīng)用于整個容器,但在這種情況下,我們可以讓字體權(quán)重繼承自 body 。
然后我們需要一種用于大文本的文字樣式,以及我所說的“柔和文本”樣式——這種文本使用較低對比度的顏色來表示其重要性降低,而不是通過調(diào)整字體大小或字體粗細來實現(xiàn)。
還有一些藍色文字看起來像鏈接,但其實不是。我假設(shè)這實際上是一個鏈接,在這種情況下,我們可以在全局樣式中為鏈接應(yīng)用 .cool-text-interactive 樣式,這樣我們就可以直接使用不帶類的 <a> 。
/* /scss/components/_text.scss */
.cool-text-large {
font-size: $text-large-font-size;
line-height: $text-large-line-height;
}
/* /scss/utilities/_text.scss */
.cool-text-interactive {
color: $color-text-interactive;
}
.cool-text-subdued {
color: $color-text-subdued;
}
@media (prefers-color-scheme: dark) {
.cool-text-interactive {
color: $color-text-interactive-inverse;
}
.cool-text-subdued {
color: $color-text-subdued-inverse;
}
}
完成后的標記
下面就是我們重構(gòu)后的標記。
<figure class="cool-card cool-flex-responsive" style="--flex-gap: 0; --flex-align: stretch">
<img class="cool-avatar cool-margin-auto-on-small cool-margin-block-start-on-small" style="--width: 12rem" src="https://assets.codepen.io/281/sarah-dayan_1.jpg" alt="" width="384" height="512">
<div class="cool-flow cool-text-center-on-small cool-inset-square-32" style="align-self: center">
<blockquote class="cool-text-large">
<p>
“Tailwind CSS is the only framework that I've seen scale
on large teams. It’s easy to customize, adapts to any design,
and the build size is tiny.”
</p>
</blockquote>
<figcaption>
<div class="cool-text-interactive">
Sarah Dayan
</div>
<div class="cool-text-subdued">
Staff Engineer, Algolia
</div>
</figcaption>
</div>
</figure>
乍一看,這并不比 Tailwind 示例簡潔多少,直到你實際查看了 Tailwind 示例的源代碼,看到了他們實際使用的所有實用類和內(nèi)聯(lián)樣式,而這些在代碼示例中并沒有顯示出來。這里僅以圖片元素為例:
<img src="/_next/static/media/sarah-dayan.de9b3815.jpg" decoding="async" alt="" class="absolute max-w-none object-cover bg-slate-100 rounded-full" style="width: 100%; height: 100%; left: 0px; top: 0px; transform-origin: 50% 50% 0px;">
不過,最終的代碼總體上減少了類的數(shù)量,更容易解析類的作用,而且在不同的上下文中重復(fù)使用這些樣式時可以減少重復(fù)。
完整事例:https://codepen.io/peruvianidol/pen/VwEqERR?editors=1100。