跳到取色器

CSS color-mix() 與相對色彩語法:淺色、hover 與透明度

color-mix() 會在你指定的色彩空間裡混合兩個顏色,相對色彩語法則讓你修改現有顏色的單一通道,例如 oklch(from var(--brand) calc(l - 0.1) c h)。兩者都在瀏覽器中執行、可搭配 CSS 變數,也能取代大部分 Sass 色彩函式。做淺色和 hover 時在 oklab 裡混色,做透明效果時用 srgb。

閱讀時間約 2 分鐘

color-mix() 如何運作

color-mix() 接收一個色彩空間和兩個顏色(百分比可省略),回傳兩者之間的顏色。color-mix(in oklab, var(--brand) 90%, black) 代表 90% 品牌色加 10% 黑色,在 Oklab 中混合。只寫一個百分比時,另一個顏色會拿到剩下的部分;都不寫時,各占 50%。

瀏覽器在執行時才計算結果,所以能搭配自訂屬性、currentColor 和主題切換。Sass 函式只在建置時執行一次,看不到 var() 的值。

如果兩個百分比加起來不到 100%,結果也會變成部分透明:color-mix(in srgb, red 30%, blue 30%) 是均勻混合、不透明度 60% 的顏色。除非你就是要這個效果,否則讓總和保持 100%。

CSS

.button {
    --brand: #2563eb;
    background: var(--brand);
}

.button:hover {
    background: color-mix(in oklab, var(--brand) 90%, black);
}

.badge {
    background: color-mix(in oklab, var(--brand) 10%, white);
    border-color: color-mix(in srgb, var(--brand) 30%, transparent);
}

該在哪個色彩空間混色:srgb、oklab 還是 oklch

色彩空間對結果的影響比多數人想像的大。下面是純藍 #0000ff 和純黃 #ffff00 以 50/50 混合的結果,計算方式和瀏覽器完全相同。

在 srgb 中,各通道平均剛好是 128、128、128:混出來是 #808080,彩度為零的灰色。Oklab 也是沿著直線混合,而藍色和黃色分別位在直線兩端,所以大多互相抵消,變成暗淡的灰藍色。Oklch 則是對色相角度做內插,從 264° 走捷徑到 110°,中點是色相 187° 的鮮豔藍綠色,保留了兩個輸入顏色的彩度。

色彩空間藍色 + 黃色OKLCH L, Cblue-600 + yellow-400OKLCH L, C
srgb#8080800.600, 0#9098800.665, 0.035
oklab#6cabc70.710, 0.077#90a3ad0.703, 0.026
oklch#00bdb00.710, 0.262#00bd9e0.703, 0.194

第二組是 Tailwind 的 #2563eb 和 #facc15。oklch 的結果超出 sRGB,所以表中顯示的是色域映射前的彩度,HEX 則是 sRGB 螢幕能顯示的最接近顏色。

有兩點特別明顯。srgb 的亮度落在 0.600,但藍色(0.452)和黃色(0.968)在感知上的中點是 0.710:這就是 srgb 漸層中間又暗又髒的原因。而只有 oklch 能讓混色保持鮮豔,代價是會經過一個兩個輸入顏色都沒有的色相。

經驗法則:做淺色、深色和 hover 狀態時用 oklab,因為你停留在同一個色相上,想要均勻的亮度階層。兩個顏色都很飽和、而你也想要飽和的結果時,用 oklch。做透明效果時用 srgb,它和瀏覽器合成圖層的方式一致。

實用寫法:hover、淺色與透明版本

以下所有數值都以 #2563eb 作為 --brand。白色文字放在它上面的對比值是 5.17:1。

混入 10% 黑色時,oklab 比 srgb 暗得更多:白色文字的對比值提高到 6.50:1,srgb 則是 6.11:1。第二個顏色是黑或白時,oklab 和 oklch 的結果相同,因為黑白沒有色相可以內插。

寫法CSS結果對比
hover,暗 10%color-mix(in oklab, var(--brand) 90%, black)#1f55cc白色文字 6.50:1
active,暗 20%color-mix(in oklab, var(--brand) 80%, black)#1947ae白色文字 8.22:1
在 srgb 中混出的 hovercolor-mix(in srgb, var(--brand) 90%, black)#2159d3白色文字 6.11:1
淺色背景,10%color-mix(in oklab, var(--brand) 10%, white)#e8f0ff品牌色文字 4.51:1
淺色背景,20%color-mix(in oklab, var(--brand) 20%, white)#d2e2ff品牌色文字 3.95:1
透明,20%color-mix(in srgb, var(--brand) 20%, transparent)rgba(37, 99, 235, 0.2)在白底上看起來像 #d3e0fb

淺色要特別留意。品牌色文字在白底上是 5.17:1,在 10% 淺色上是 4.51:1,在 20% 淺色上只剩 3.95:1,一般文字就達不到 WCAG AA。如果要把品牌色文字放在品牌淺色背景上,文字請改用 hover 的深色版本。

和 transparent 混合時,顏色本身不變,只降低 alpha,因為瀏覽器混合的是預乘後的數值。結果會隨背後的內容而變:同一條規則在白底上看起來像 #d3e0fb,在深色的 #0f172a 背景上則像 #132651。和白色混出的淺色在深色頁面上仍然是淡淡的一塊,所以同時出現在淺色與深色主題的元件,通常用透明版本比較好。

相對色彩語法:只改一個通道

相對色彩語法會把現有顏色拆成各個通道,讓你用 calc() 改寫其中任何一個。在 oklch(from var(--brand) l c h) 裡,關鍵字 l、c、h 分別代表 --brand 的亮度(0 到 1)、彩度和色相。

和 color-mix() 不同,它只改變一個屬性:降低亮度而不動彩度、旋轉色相而不改變亮度,或是替以 HEX 儲存的顏色加上 alpha。

寫法CSS由 #2563eb 得到的結果白色文字
更深oklch(from var(--brand) calc(l - 0.1) c h)#0442c88.02:1
更淺oklch(from var(--brand) calc(l + 0.1) c h)#4484ff3.51:1
彩度減半oklch(from var(--brand) l calc(c * 0.5) h)#4e6faf4.98:1
色相 +30°oklch(from var(--brand) l c calc(h + 30))#7a49df5.46:1
互補色oklch(from var(--brand) l c calc(h + 180))#9567004.98:1
同亮度的灰色oklch(from var(--brand) l 0 h)#7070704.95:1
50% alphaoklch(from var(--brand) l c h / 0.5)rgba(37, 99, 235, 0.5)取決於背景

CSS

:root {
    --brand: #2563eb;
    --brand-hover: oklch(from var(--brand) calc(l - 0.08) c h);
    --brand-muted: oklch(from var(--brand) l calc(c * 0.5) h);
    --brand-ring: oklch(from var(--brand) l c h / 0.4);
    --accent: oklch(from var(--brand) l c calc(h + 30));
}

看看保留 l 的那四列:改變彩度或色相時,白色文字的對比值只在 4.95:1 到 5.46:1 之間變動,原色則是 5.17:1。決定對比的是亮度,所以用這種方式做出的色相變化,無障礙程度大致相同。更淺和互補色這兩個顏色在完整彩度下超出 sRGB,瀏覽器會把它們映射到能顯示的最接近顏色。

color-mix()、相對色彩語法與 Sass darken() 比較

當你想的是「加一點黑」或「透明一點」時,用 color-mix()。當你用通道思考,例如「同一個顏色,暗 0.1」,就用相對色彩語法。兩者都能取代大部分 Sass 色彩函式,切換主題時也不必重新建置。

color-mix()相對色彩語法Sass darken()
執行時機在瀏覽器中在瀏覽器中建置時
可搭配 var() 和 currentColor可以可以不行
色彩模型你指定的空間:srgb、oklab、oklch、hsl 等任何色彩函式:oklch()、rgb()、hsl()、lab()只有 HSL
最適合淺色、深色、透明度、混合兩個設計變數修改單一通道:亮度、彩度、色相或 alpha使用固定顏色的舊程式碼
調整幅度到第二個顏色距離的一部分精確,以該通道的單位計算固定的 HSL 點數,肉眼看來不均勻
支援起始版本Chrome 111、Safari 16.2、Firefox 113Chrome 119、Safari 18、Firefox 128所有瀏覽器,輸出是靜態值

為什麼 Sass darken() 的變化不均勻

darken($color, 10%) 會把 HSL 亮度減去 10 點。HSL 亮度不等於感知亮度,所以肉眼看到的變化取決於色相。下面在四個常見的介面顏色上比較 darken(10%) 和 oklch(from … calc(l - 0.1) c h),以 ΔE OK 衡量,約 0.02 就是人眼能分辨的最小差異。

顏色Sass darken(10%)ΔE OKOKLCH l − 0.1ΔE OK
#2563eb#134cca0.078#0442c80.099
#facc15#d7ad050.099#d9ab000.101
#16a34a#1076360.136#00842b0.094
#dc2626#b21d1d0.090#b700000.089

同樣的 darken(10%),綠色的變化是藍色的 1.7 倍。OKLCH 版本在每個色相上都維持在 0.089 到 0.101 之間;這點小差異來自色域映射,因為有些變暗後的顏色剛好落在 sRGB 之外。Sass 官方也建議用 color.adjust() 或 color.scale() 取代 darken(),而在純 CSS 裡兩者都用不到。

瀏覽器支援與備援寫法

color-mix() 可在 Chrome 和 Edge 111、Safari 16.2 以及 Firefox 113 中使用,這些版本都在 2023 年推出。相對色彩語法在 Chrome 和 Edge 119、Firefox 128 加入;Safari 在 16.4 推出早期版本,Safari 18 才支援目前的語法。所有現行瀏覽器都支援這兩者。

常見的技巧是在前一行先寫一個普通值,但當新值裡含有 var() 時就行不通。含有 var() 的宣告在解析時一定會被接受,所以瀏覽器會丟掉你的備援那一行;如果它算不出 color-mix(),就會退回繼承值或初始值。請改用 @supports 包住新寫法。

CSS

.button:hover {
    background: #1f55cc;
}

@supports (color: color-mix(in oklab, red, blue)) {
    .button:hover {
        background: color-mix(in oklab, var(--brand) 90%, black);
    }
}

@supports (color: oklch(from red l c h)) {
    .badge {
        border-color: oklch(from var(--brand) l c h / 0.3);
    }
}

常見問題

CSS 的 color-mix() 有什麼用?

color-mix() 會在你指定的色彩空間裡混合兩個顏色並回傳結果,例如 color-mix(in oklab, var(--brand) 90%, black) 會得到比品牌色暗 10% 的顏色。它在瀏覽器中執行,所以能搭配 CSS 變數和 currentColor。

color-mix() 該用哪個色彩空間?

做淺色、深色和 hover 狀態時用 oklab,因為它的亮度階層均勻,而且能保留色相。混合兩個飽和色、也想要飽和的結果時用 oklch;和 transparent 混合時用 srgb。在 srgb 中混合藍色和黃色會得到平淡的灰色 #808080,oklch 則得到鮮豔的藍綠色。

如何用純 CSS 在 hover 時讓顏色變深?

使用 background: color-mix(in oklab, var(--brand) 90%, black) 或 background: oklch(from var(--brand) calc(l - 0.1) c h)。以 #2563eb 為例,前者得到 #1f55cc,後者得到 #0442c8,白色文字的對比值分別從 5.17:1 提高到 6.50:1 和 8.02:1。

如何讓 CSS 變數中的顏色變透明?

寫 color-mix(in srgb, var(--brand) 20%, transparent) 或 oklch(from var(--brand) l c h / 0.2)。即使 --brand 是以 HEX 儲存,兩種寫法都能用,而且顏色本身不變,只降低不透明度。

所有瀏覽器都支援 color-mix() 嗎?

是的,所有現行瀏覽器都支援:Chrome 和 Edge 從 111 起、Safari 從 16.2 起、Firefox 從 113 起。若要照顧舊版瀏覽器,先寫一個靜態顏色,再把 color-mix() 版本放進 @supports,因為含有 var() 的那一行無法自行退回備援值。

color-mix() 和相對色彩語法有什麼不同?

color-mix() 依百分比混合兩個顏色,適合做淺色、深色和透明度。相對色彩語法,例如 oklch(from var(--brand) l c calc(h + 30)),會改寫單一顏色的個別通道,適合精確的亮度階層、色相偏移和 alpha。

在 PickShade 中試試看

相關指南