基礎概念簡介:如何建立可設定的動態色彩配置
在這篇文章中,我想分享如何管理 CSS 中的多種色彩配置。立即試用。
如果比較喜歡看影片,可以觀看這篇貼文的 YouTube 版本:
總覽
我們會使用自訂屬性和 calc() 建構無障礙色彩系統,製作可配合使用者偏好設定調整的網頁,同時盡量簡化撰寫體驗。我們從基本品牌顏色開始,並從中建構變體系統:2 種文字顏色、4 種表面顏色和相符的陰影。
本指南一開始會先定義每個色彩配置的所有顏色。直到最後才會用於變更頁面。
品牌
通常品牌顏色已建立完成,並以 hex 或 rgb 格式提供。這項 GUI 挑戰的品牌底色為 #0af。首先,對於這個色彩系統,十六進位值需要轉換為 hsl。
* {
--brand: #0af;
--brand: hsl(200 100% 50%);
}
如要啟用品牌顏色變深或變淺的概念 (例如 20%),hsl 顏色值的 3 個管道必須擷取至各自的自訂屬性,如下所示:
* {
--brand-hue: 200;
--brand-saturation: 100%;
--brand-lightness: 50%;
}
CSS 可以對這些顏色屬性執行數學運算,例如 calc(var(--brand-lightness) -
20%) 將亮度值減少 20%。這是建構色彩配置的基礎,因為 CSS 可調整 HSL 飽和度和明度,讓所有顏色保持在同一色調系列中。
淺色主題
每個顏色子類都會標示相符的配置,在本例中,每個子類都會附加 -light。

品牌
從品牌顏色開始,將 --brand-hue、--brand-saturation 和 --brand-lightness 自訂屬性包裝在 hsl () 函式括號內,即可重建,不需進行任何計算:
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
}
文字顏色
接著,色彩配置的基本要素需要文字顏色。在淺色主題中,文字應為深色。請注意,下列色彩的明度較低,遠低於 50%。
* {
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
}
--text1-light,由於 10% 亮度時非常暗,因此保留 100% 的飽和度,讓品牌顏色仍能透出深藍色。
--text2-light,這個顏色不像第一個顏色那麼深,這很好,因為這是次要顏色,而且飽和度也低得多。
表面顏色
表面顏色是文字所在或所屬的背景、邊框和其他裝飾表面。在淺色主題中,這些是淺色,與深色的文字顏色相反。如要使用 HSL 建立淺色,我們會在第三個亮度值中使用較高的百分比值。我們也會降低飽和度,避免淺灰色看起來過於有色調。
* {
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
}
由於裝飾色通常需要更多變體,因此我們建立了 4 種表面色,可用於 :focus 或 :hover 等互動時刻,或建立紙張圖層的外觀。在這些情況下,最好在懸停時從 --surface2-light轉換至 --surface3-light,這樣懸停時對比度就會增加 (從 99% 亮度降至 92% 亮度,也就是變暗)。
陰影
色彩配置中的陰影效果超越一般效果,能為效果增添真實感,並有別於不切實際的黑色陰影,讓效果更加突出。為此,陰影的顏色會使用色相自訂屬性,並稍微飽和,但仍非常深。基本上是建立非常深色的微藍陰影。
* {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
--surface-shadow-light 未包裝在 hsl 函式中。這是因為 --shadow-strength 值會合併來建立不透明度,而 CSS 需要這些片段才能執行計算。如要瞭解詳情,請跳至徑向陰影部分。
同時調亮所有燈
不必費心尋找任何淺色調的製作方式,因為這些資訊都集中在 CSS 中。
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
--surface-shadow-light: var(--brand-hue) 10% calc(var(--brand-lightness) / 5);
--shadow-strength-light: .02;
}
深色主題
大多數品牌不會從深色主題開始,而是從主要 (通常是較淺) 主題的變體開始。另一方面,使用者通常會在不同情境下選擇深色主題,例如夜間。基於上述因素,我在設計深色主題時會注意以下兩點:
- 使用者通常會在黑暗環境中使用這個主題,因此請在黑暗環境中測試。
- 顏色應降低飽和度,以免過於鮮豔而造成螢幕閃爍。

品牌
淺色主題會使用 3 個品牌 HSL 色彩通道值,且不會變更,但深色主題不會。飽和度減半,亮度降低相對 50%。
* {
--brand-dark: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 2)
calc(var(--brand-lightness) / 1.5)
);
}
文字顏色
在深色主題中,文字顏色應為淺色。下列顏色具有高亮度值,因此較接近白色。
* {
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
}
表面顏色
在深色主題中,介面顏色應為深色。下列顏色亮度與飽和度偏低,其中第 1 個表面最暗,亮度為 10%。
* {
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
}
陰影
在深色主題中,陰影可能很難看見。因為很難讓已經相當暗的物體變得更暗,所以這項限制有其必要性。這時 --shadow-strength-dark 就非常實用,因為只要變更一個變數,就能調暗陰影。
* {
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
此外,也要注意陰影的飽和度。查看介面時,您是否注意到顏色?請嘗試從開發人員工具中移除飽和度,你比較喜歡哪種方式?
深色系
* {
--brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5));
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
昏暗主題
這個色彩配置的重點在於協調亮度與飽和度。飽和度應足夠,讓色調仍可見,但同時也應僅略高於對比度分數,因為原本就打算採用低對比度的暗色。

品牌
* {
--brand-dim: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 1.25)
calc(var(--brand-lightness) / 1.25)
);
}
文字顏色
* {
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
}
表面顏色
* {
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
}
陰影
* {
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
一次調暗所有顏色
* {
--brand-dim: hsl(var(--brand-hue) calc(var(--brand-saturation) / 1.25) calc(var(--brand-lightness) / 1.25));
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
無障礙顏色
請注意,深色文字顏色組的最低亮度為 65%,深色介面的最高亮度為 25%。這表示兩者之間有 40% 的明度空間。在淺色主題中,淺色主題的呼吸空間為 55%。將文字和介面色彩之間的明度差異維持在 40% 到 50% 左右,有助於維持高色彩對比度,同時也是調整分數不佳情況的細微槓桿。
我稱之為「bump bump til ya pass」,也就是不斷調整亮度值,直到工具顯示我通過為止。
這項挑戰中建立的每個主題都會通過對比度分數。暗淡色彩配置的對比度最低,但仍符合最低要求。為協助團隊成員使用對比度高的顏色,建議您建立類別名稱,將表面顏色與無障礙文字顏色配對。
.surface1 {
background-color: var(--surface1);
color: var(--text2);
}
.surface2 {
background-color: var(--surface2);
color: var(--text2);
}
.surface3 {
background-color: var(--surface3);
color: var(--text1);
}
.surface4 {
background-color: var(--surface4);
color: var(--text1);
}
Rad Shadow
主題會使用名為 .rad-shadow 的公用程式類別。這個陰影是在「平滑陰影」工具中生成,我非常喜歡。我採用這段程式碼,並以自己的顏色和不透明度計算加以自訂。這是為了建立陰影,方便我在每個色彩配置中調整。

為此,我為每個色彩配置建立了 2 個變數,分別用來調整陰影顏色和陰影強度。顏色用於調整飽和度和深淺度,強度則用於在深色色彩配置中輕鬆提高陰影強度。最終結果如下所示。
:root {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
.rad-shadow {
box-shadow:
0 2.8px 2.2px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 6.7px 5.3px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .01)),
0 12.5px 10px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 22.3px 17.9px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 41.8px 33.4px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 100px 80px hsl(var(--surface-shadow) / var(--shadow-strength))
;
}
如果要在配色方案中進一步使用陰影,我會將陰影角度也設為設計符記常數,因為設計中所有陰影的光線方向都應相同。
使用色彩配置
預先定義顏色後,即可將其轉換為與配置無關的屬性。我的意思是,在顏色配置專案中擔任 CSS 作者時,應該很少需要存取特定顏色配置的值。我想讓使用者輕鬆維持主題風格。
為達成這個目的,色彩配置的使用方式應僅限於一般自訂屬性,我們稍後會定義這些屬性。這樣一來,使用設計變數的人員就不必擔心目前設定的色彩配置,只要使用表面和文字顏色即可。請改用 color: var(--text1),不要使用 color: var(--text1-light)。所有顏色調整和樞紐作業都是在 CSS 中較高的層級完成。
深入瞭解後,您會發現下列程式碼區塊中的淺色主題連結樣式,會將一般自訂屬性與淺色主題專屬顏色連結。現在所有 var(--brand) 的用途都會使用淺色品牌顏色。
淺色主題 (自動)
:root {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
網站現在會使用淺色主題。這是一個非常有趣的成功時刻! 讓我們在其他色彩配置環境中使用預先定義的顏色,再多體驗幾次這種時刻。
深色主題 (自動)
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
}
淺色主題
[color-scheme="light"] {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
深色主題
[color-scheme="dark"] {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
昏暗主題
[color-scheme="dim"] {
color-scheme: dark;
--brand: var(--brand-dim);
--text1: var(--text1-dim);
--text2: var(--text2-dim);
--surface1: var(--surface1-dim);
--surface2: var(--surface2-dim);
--surface3: var(--surface3-dim);
--surface4: var(--surface4-dim);
--surface-shadow: var(--surface-shadow-dim);
--shadow-strength: var(--shadow-strength-dim);
}
此時,作者可以視需要使用提供的色彩配置泛型,且不必再擔心主題問題。
結論
現在您已瞭解我的做法,您會怎麼做呢?🙂
讓我們多元化地運用各種方法,學習在網路上建構內容的所有方式。 建立 Codepen 或代管自己的試用版,然後在推文中提及我,我會將其新增至下方的「社群混音」部分。
來源
社群改造
- @chris-kruining 為 no-preference、more 和 less 新增色調滑桿、狀態顏色和對比模式:
示範。