
- 原文地址:Defensive CSS 和 The Just in Case Mindset in CSS
- 原文作者:Ahmad Shadeed
- 譯者:蠟筆小偉
前言
Ahmad Shadeed 的原創(chuàng)文章 Defensive CSS 非常的干貨和實(shí)用,在你每次動(dòng)鍵盤寫 CSS 之前,都應(yīng)該把它拿出來溫習(xí)一遍,作為你寫 CSS 的指導(dǎo)。
如果你沒有把這些作為寫 CSS 的指導(dǎo),雖然完成業(yè)務(wù)需求,但你就像造人的上帝,只造了骨架沒有填充血液,人面對(duì)龐雜的世間,難免跑蹦跳,摔了是需要肉來減震的,只有一副骨架豈不分分鐘散架了。
業(yè)務(wù)往往是最理想的 CSS 運(yùn)行環(huán)境,部署到服務(wù)器面對(duì)復(fù)雜的網(wǎng)絡(luò)環(huán)境,我們對(duì) CSS 的要求,就像生活對(duì)人的要求,一個(gè)健壯完整的人才能承載生活的苦難,而只有健壯 CSS 才能面對(duì)復(fù)雜的網(wǎng)絡(luò)環(huán)境。
間距(Spacing)
我們開發(fā)人員需要考慮不同的內(nèi)容長(zhǎng)度。這意味著,即使看起來不需要,也應(yīng)該向組件添加間距。
在上圖示中,左側(cè)有一個(gè)標(biāo)題右側(cè)一個(gè)操作按鈕。目前,看起來還可以。但是讓我們看看當(dāng)標(biāo)題更長(zhǎng)時(shí)會(huì)發(fā)生什么。
注意到文本離操作按鈕太近了嗎?您可能正在考慮換行展示,這個(gè)我們先不討論?,F(xiàn)在,讓我們關(guān)注間距。
如果你讓標(biāo)題有間距同時(shí)文本截?cái)?,我們不?huì)看到這樣的問題。
.section__title {
margin-right: 1rem;
}
對(duì)于這個(gè)組件,標(biāo)題可以是一個(gè)詞或多個(gè)詞,甚至是一個(gè)長(zhǎng)句子。為避免標(biāo)題卡在右側(cè)的圖標(biāo)上,最好添加margin-right: 1rem,以防標(biāo)題變長(zhǎng)。
說到文本溢出截?cái)?,還有一個(gè)最常見的就是——長(zhǎng)內(nèi)容。
長(zhǎng)內(nèi)容(Long Content)
在構(gòu)建布局時(shí)考慮長(zhǎng)內(nèi)容很重要。正如您在前面看到的,部分標(biāo)題過長(zhǎng)時(shí)會(huì)被截?cái)啵?dāng)然你也可以換行。
這是一個(gè)人名列表,現(xiàn)在看起來很完美。
但是,由于這是用戶生成的內(nèi)容,因此我們需要注意如何防御布局,以防萬一太長(zhǎng)。
見下圖:
在這樣的布局中,一致性很重要。為了實(shí)現(xiàn)這一點(diǎn),我們可以簡(jiǎn)單地通過 usingtext-overflow和它的朋友來截?cái)嗝Q。
.username {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
提示,當(dāng)文本截?cái)嗟臅r(shí)候千萬不要忘了處理提示,不然用戶怎么看到全部?jī)?nèi)容來。
如果您有興趣提高處理 CSS 中長(zhǎng)內(nèi)容的技能,我寫了一篇關(guān)于該主題的詳細(xì)文章。
卡片上的文字(category tag)
還有一個(gè)長(zhǎng)文本的例子:
對(duì)于卡片組件,您可能需要一個(gè)類別標(biāo)簽。您將如何處理內(nèi)容的不同變化?考慮下圖。
右邊的卡片有一個(gè)很長(zhǎng)的標(biāo)題,這看起來不太好,因?yàn)樗采w了縮略圖的很大一部分。對(duì)于食品網(wǎng)站,圖像非常重要。
這里有一些可能的解決方案,以防萬一標(biāo)簽變得更長(zhǎng)。
第一個(gè)解決方案是使用文本截?cái)嗉夹g(shù),max-width在 CSS 中使用 a 。第二個(gè)只使用max-width,但如果標(biāo)簽有很長(zhǎng)的文本,它可能會(huì)失敗。
.tag {
max-width: 6.25rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
按鈕(Buttons)
在某些情況下,您需要并排放置兩個(gè)按鈕。我們?nèi)绾谓忉屵@種情況,而不margin默認(rèn)添加按鈕?
多虧了相鄰兄弟組合器,讓這很容易做到。它是說:“如果一個(gè)按鈕與另一個(gè)按鈕相鄰,則為第二個(gè)按鈕添加一個(gè)左邊距,以防萬一”。
.button + .button {
margin-left: 1rem;
}
可滾動(dòng)元素(Scrollable Element)
overflow 可拆分為 overflow-x 和 overflow-y 兩者默認(rèn)都是 visible,也就是說當(dāng)內(nèi)容溢出盒子不會(huì)產(chǎn)品滾動(dòng)條。
當(dāng)您需要使元素可滾動(dòng)時(shí),可能很想執(zhí)行以下操作:
.element {
height: 20rem;
overflow-y: scroll;
}
這種方法的問題在于它會(huì)始終顯示 scrollbar,即使內(nèi)容不夠長(zhǎng)無法滾動(dòng)。最好是使用auto關(guān)鍵字。這樣,它可以通過以防萬一的概念。
.element {
height: 20rem;
overflow-y: auto;
}
圖片上的文字(HTML Image Fallback)
如果您用 <img> 來展示圖片,而圖片上有文字內(nèi)容,但圖片可能由于某種原因無法加載。在這種情況下,如果有后備怎么辦?
img {
background-color: #525252;
}
以防萬一圖像無法加載,background-color它將作為后備。
頭像圖片(Avatar Image)
這是一個(gè)非常常見的用例,其中頭像可能看起來被拉伸或壓縮。
年度人氣創(chuàng)作者榜單:

可以通過使用來避免object-fit: cover。它將充當(dāng)以防萬一的事情。沒有它,圖像會(huì)看起來很糟糕。
掘金的 2021 最佳創(chuàng)作者活動(dòng)就是一個(gè)例子:

圖片最大寬度(Image maximum width)
防止圖片被拉伸,作為一般規(guī)則,不要忘記設(shè)置max-width: 100%為所有圖像。這可以添加到您使用的 CSS 重置中。
img {
max-width: 100%;
object-fit: cover;
}
flex 布局換行(Flexbox Wrapping)
CSS flexbox 是當(dāng)今最有用的 CSS 布局功能之一。
添加display: flex 布局在而是以世界已經(jīng)是常見操作,但問題是當(dāng)空間不足時(shí),默認(rèn)情況下這些子項(xiàng)不會(huì)換行。我們需要使用 flex-wrap: wrap。
這是一個(gè)典型的例子。我們有一組應(yīng)該并排顯示的選項(xiàng)。
.options-list {
display: flex;
}
當(dāng)空間較少時(shí),將發(fā)生水平滾動(dòng)。這應(yīng)該是意料之中的,實(shí)際上并不是一個(gè)“問題”。
請(qǐng)注意這些項(xiàng)目如何仍然彼此相鄰。為了解決這個(gè)問題,我們需要允許 flex wrapping。
.options-list {
display: flex;
flex-wrap: wrap;
}
使用 flexbox 時(shí)的一般經(jīng)驗(yàn)法則是允許換行,除非您想要滾動(dòng),但那是另一回事,所以盡量使用flex-wrap以避免意外的布局行為(在我們的例子中是水平滾動(dòng))。
鎖定滾動(dòng)鏈接(Lock scroll chaining)
您是否曾經(jīng)打開一個(gè)模態(tài)并開始滾動(dòng),然后當(dāng)您到達(dá)末尾并繼續(xù)滾動(dòng)時(shí),模態(tài)下方的內(nèi)容(主體元素)會(huì)滾動(dòng)?這稱為滾動(dòng)鏈接。
在過去的幾年里,有一些技巧可以使這項(xiàng)工作發(fā)揮作用,但是現(xiàn)在,由于overscroll-behaviorCSS 屬性,我們只能使用 CSS 來做到這一點(diǎn)。
在下圖中,您會(huì)看到默認(rèn)的滾動(dòng)鏈接行為。
為了提前避免這種情況,我們可以將其添加到任何需要滾動(dòng)的組件(例如:聊天組件、移動(dòng)菜單等)。這個(gè)屬性的好處是它在滾動(dòng)之前不會(huì)產(chǎn)生影響。
.modal__content {
overscroll-behavior-y: contain;
overflow-y: auto;
}
如果您想了解更多信息,我寫了一篇詳細(xì)的文章。
CSS 變量的默認(rèn)值(CSS Variable Fallback)
CSS 變量在網(wǎng)頁設(shè)計(jì)中得到越來越多的使用。我們可以應(yīng)用一種方法以不破壞體驗(yàn)的方式使用它們,以防 CSS 變量值由于某種原因?yàn)榭铡?/p>
這在通過 Javascript 提供 CSS 變量的值時(shí)特別有用。下面是一個(gè)例子:
.message__bubble {
max-width: calc(100% - var(--actions-width));
}
該變量--actions-width正在calc()函數(shù)中使用,其值來自 Javascript。讓我們假設(shè) Javascript 由于某種原因失敗了,會(huì)發(fā)生什么?在max-width將計(jì)算到none。
我們可以提前避免這種情況,并向var().
.message__bubble {
max-width: calc(100% - var(--actions-width, 70px));
}
這樣,如果未定義變量,則將使用回退 ( 70px)。如果變量可能失敗(例如:來自 Javascript),則可以使用此方法。否則,它是不需要的。
使用固定寬度或高度(Using fixed width or height)
破壞布局的常見事情之一是對(duì)具有不同長(zhǎng)度內(nèi)容的元素使用固定寬度或高度。
固定高度(The fixed height)
我經(jīng)??吹揭粋€(gè)固定高度的部分和大于該高度的內(nèi)容,這導(dǎo)致布局損壞。不知道這看起來如何?
.hero {
height: 350px;
}
為了避免內(nèi)容溢出,我們需要使用 min-height 代替 height。
.hero {
min-height: 350px;
}
這樣,如果內(nèi)容變大,布局就不會(huì)中斷。
固定寬度(The fixed with)
您是否見過標(biāo)簽離左右邊緣太近的按鈕?這是由于使用了固定寬度。
.button {
width: 100px;
}
如果按鈕的標(biāo)簽長(zhǎng)于100px,它將靠近邊緣。如果太長(zhǎng),文本會(huì)漏出來。這不好!
為了解決這個(gè)問題,我們可以簡(jiǎn)單地替換width為min-width.
.button {
min-width: 100px;
}
忘記 Background-Repeat(Forgetting background-repeat)
通常,當(dāng)使用一張大圖作為背景時(shí),我們往往會(huì)忘記考慮在大屏幕上查看設(shè)計(jì)的情況。默認(rèn)情況下,該背景將重復(fù)。
這在筆記本電腦屏幕上通??床坏剑谳^大的屏幕上可以清楚地看到。
為了提前避免這種行為,請(qǐng)確保重置background-repeat。
.hero {
background-image: url('..');
background-repeat: no-repeat;
}
使用 Justify-Content: Space-Between(Using justify-content: space-between)
在 flex 容器中,您可能會(huì)使用justify-content將子項(xiàng)彼此隔開。使用一定數(shù)量的子項(xiàng),布局看起來不錯(cuò)。但是,當(dāng)它們?cè)黾踊驕p少時(shí),布局會(huì)看起來很奇怪。
考慮以下示例。
我們有一個(gè)包含四個(gè)項(xiàng)目的 flex 容器。每個(gè)項(xiàng)目之間的間距不是gap或margin,它在那里是因?yàn)槿萜饔?code>justify-content: space-between.
.wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
當(dāng)項(xiàng)目數(shù)少于四個(gè)時(shí),將發(fā)生以下情況。
這不好。對(duì)此有不同的解決方案:
- margin
- flex gap(謹(jǐn)慎使用)
- Padding(可以應(yīng)用于每個(gè)子元素的父元素)
- 添加空元素作為間隔。
為簡(jiǎn)單起見,我將使用gap.
.wrapper {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
小心使用 CSS 網(wǎng)格中的固定值(Be careful with fixed values in a CSS grid)
假設(shè)我們有一個(gè)包含 aside 和 main 的網(wǎng)格。CSS 看起來像這樣:
.wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
由于空間不足,這將在小視口尺寸上中斷。為避免此類問題,請(qǐng)?jiān)谑褂蒙鲜?CSS 網(wǎng)格時(shí)始終使用媒體查詢。
@media (min-width: 600px) {
.wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
}
滾動(dòng)條槽(Scrollbar Gutter)
與滾動(dòng)相關(guān)的另一件事是scrollbar gutter。以前面的例子為例,當(dāng)內(nèi)容變長(zhǎng)時(shí),添加滾動(dòng)條會(huì)導(dǎo)致布局偏移,發(fā)生布局偏移的原因是為滾動(dòng)條占用布局寬度。
考慮下圖。
請(qǐng)注意當(dāng)內(nèi)容因顯示滾動(dòng)條而變長(zhǎng)時(shí)如何移動(dòng)。我們可以通過使用scrollbar-gutter屬性來避免這種行為,滾動(dòng)條不占用布局寬度,可以理解為滾動(dòng)條槽變?yōu)楦?dòng)。
.element {
scrollbar-gutter: stable;
}
兼容不太好,作為增強(qiáng)屬性可以:

CSS Flexbox 中的最小內(nèi)容大?。∕inimum content size in CSS flexbox)
下面的例子省略了細(xì)節(jié),可以看這個(gè)我講的案例 讓 overflow-wrap 失效的 flex-wrap,更好理解。
如果一個(gè) flex 項(xiàng)目有一個(gè)文本元素或一個(gè)比項(xiàng)目本身長(zhǎng)的圖像,瀏覽器不會(huì)縮小它們。這是 flexbox 的默認(rèn)行為。
考慮以下示例。
.card {
display: flex;
}
當(dāng)標(biāo)題有一個(gè)很長(zhǎng)的詞時(shí),它不會(huì)換行。
即使我們使用overflow-wrap: break-word,它也不起作用。
.card__title {
overflow-wrap: break-word;
}
要更改該默認(rèn)行為,我們需要min-width將 flex 項(xiàng)的 設(shè)置為0。那是因?yàn)?code>min-width默認(rèn)值是auto,就會(huì)發(fā)生溢出。
.card__title {
overflow-wrap: break-word;
min-width: 0;
}
同樣的事情適用于列 flex 包裝器,但我們將使用它min-height: 0。