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

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

來源: https://pickshade.com/zh-tw/css-color-mix
更新於: 2026-09-28

## 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° 的鮮豔藍綠色，保留了兩個輸入顏色的彩度。

- 藍色 #0000ff: #0000ff
- 黃色 #ffff00: #ffff00
- 在 srgb 中：#808080: #808080
- 在 oklab 中：#6cabc7: #6cabc7
- 在 oklch 中：#00bdb0: #00bdb0

| 色彩空間 | 藍色 + 黃色 | OKLCH L, C | blue-600 + yellow-400 | OKLCH L, C |
| --- | --- | --- | --- | --- |
| srgb | #808080 | 0.600, 0 | #909880 | 0.665, 0.035 |
| oklab | #6cabc7 | 0.710, 0.077 | #90a3ad | 0.703, 0.026 |
| oklch | #00bdb0 | 0.710, 0.262 | #00bd9e | 0.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 中混出的 hover | color-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) | #0442c8 | 8.02:1 |
| 更淺 | oklch(from var(--brand) calc(l + 0.1) c h) | #4484ff | 3.51:1 |
| 彩度減半 | oklch(from var(--brand) l calc(c * 0.5) h) | #4e6faf | 4.98:1 |
| 色相 +30° | oklch(from var(--brand) l c calc(h + 30)) | #7a49df | 5.46:1 |
| 互補色 | oklch(from var(--brand) l c calc(h + 180)) | #956700 | 4.98:1 |
| 同亮度的灰色 | oklch(from var(--brand) l 0 h) | #707070 | 4.95:1 |
| 50% alpha | oklch(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 113 | Chrome 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 OK | OKLCH l − 0.1 | ΔE OK |
| --- | --- | --- | --- | --- |
| #2563eb | #134cca | 0.078 | #0442c8 | 0.099 |
| #facc15 | #d7ad05 | 0.099 | #d9ab00 | 0.101 |
| #16a34a | #107636 | 0.136 | #00842b | 0.094 |
| #dc2626 | #b21d1d | 0.090 | #b70000 | 0.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。

## 工具

- [混色工具](https://pickshade.com/zh-tw/mix)
- [OKLCH 取色器](https://pickshade.com/zh-tw/oklch)
- [對比檢查工具](https://pickshade.com/zh-tw/contrast)
- [漸層產生器](https://pickshade.com/zh-tw/gradient)

## 相關指南

- [OKLCH 完整解說：終於和人眼感受一致的顏色格式](https://pickshade.com/zh-tw/oklch)
- [深色模式配色：如何挑選背景、文字與強調色](https://pickshade.com/zh-tw/dark-mode-colors)
- [顏色格式完整解說：HEX、RGB、HSL、OKLCH 等格式](https://pickshade.com/zh-tw/color-formats)
