建構可重複使用的 UI 元件 (例如徽章或按鈕) 或支援動態主題時,確保文字在無法預測的背景顏色上清晰可辨,可能會是一項挑戰。contrast-color() CSS 函式會解析為 black 或 white,盡可能提高與指定背景顏色的對比度。
決定最佳文字顏色
如果元素具有變數或動態插入的背景顏色,您可以依賴瀏覽器計算最安全的文字顏色。
必要:使用 contrast-color() 函式設定 color 屬性,並傳遞背景顏色 (或其自訂屬性) 做為引數。
.badge {
/* Define the background color as a custom property */
--badge-bg: #e02424;
background-color: var(--badge-bg);
/* MANDATORY: Dynamic contrasting color */
color: contrast-color(var(--badge-bg));
}
請勿:將 contrast-color() 用於 background-color。這項功能專為前景顏色 (例如文字或邊框) 設計,可與特定背景顏色形成對比。
與主題設定整合
contrast-color() 可接受任何有效的 <color> 值,包括 CSS 自訂屬性,因此在設計系統中特別實用。將背景主題集中為變數,即可確保前景內容在主題切換時 (即使切換為使用者指定的顏色),自動更新對比度。
建議做法:在 contrast-color() 中參照共用背景變數,確保應用程式同步。
.theme-card {
background-color: var(--theme-surface-color);
color: contrast-color(var(--theme-surface-color));
}
.theme-container[data-theme="light"] {
--theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
--theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
--theme-surface-color: var(--user-custom-theme);
}
不建議:背景為動態時,為每個互動狀態硬式編碼不同的文字顏色。否則會再次出現 contrast-color() 旨在避免的不一致問題。
備用策略
如要支援不含 contrast-color() 的瀏覽器,您必須提供備用方案。如果背景顏色已知且固定,請使用硬式編碼對比色。如果背景是動態背景,且顏色未知,請根據 UI 需求和瀏覽器支援目標,從下表選擇策略。
| 策略 | 方案特點 | 注意事項 |
|---|---|---|
| 相對顏色語法 | 自動化、高品質、僅限 CSS 的對比度計算。 | 最高品質的 CSS 專用備用廣告。 |
| 文字陰影 | 在任何背景上都能快速提升可讀性。 | 可能看起來「髒髒的」或「發光」,不一定適合所有設計。 |
| 文字筆劃 | 保留字重,同時確保邊緣對比度。 | 如果可以,請使用 paint-order 避免字體變細。 |
| 半透明疊加層 | 確保文字後方有最小對比區域。 | 變更文字下方背景顏色的外觀,需要獨立的文字元素。 |
| SVG 濾鏡 | 對比度會隨著背景變化而調整。 | 需要獨立的文字元素,實作方式較為粗糙。 |
建議:相對顏色語法 (RCS)
在支援 RCS 的瀏覽器中,RCS 可為動態色彩提供最穩定的自動備用機制。
@supports (color: oklch(from red l c h)) {
.badge {
/* Highest threshold that passes WCAG. Higher values may be more legible. */
--threshold: 0.623;
--l: max(0, sign(var(--threshold) - l));
color: oklch(from var(--badge-bg) var(--l) 0 h);
}
}
@supports (color: contrast-color(red)) {
.badge {
color: contrast-color(var(--badge-bg));
}
}
替代 CSS 備用廣告
建議做法:如果 RCS 不足或不支援,請選取最符合設計限制的備用方案。
方法 1:文字筆劃
.badge--stroke {
-webkit-text-stroke: 4px black;
}
方法 2:使用 paint-order 描邊文字,保留字母形狀
.badge--stroke {
-webkit-text-stroke: 4px black;
paint-order: stroke fill;
}
選項 3:半透明背景疊加層
.badge--overlay {
position: relative;
color: #fff;
overflow: hidden;
}
.badge--overlay::before {
content: "";
position: absolute;
inset: 0;
background-color: rgb(0 0 0 / 0.4);
z-index: 0;
}
.badge--overlay span {
position: relative;
z-index: 1;
}
選項 4:文字陰影
.badge--shadow {
text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}
選項 5:SVG 濾鏡
載入這個 SVG 濾鏡定義。如要支援受這個 WebKit 錯誤影響的 Safari 版本,則必須直接將其新增至 HTML 文件,以免篩選器參照外部和資料網址。否則,應從外部檔案載入,可以是 SVG 檔案或內嵌在 CSS 中。
<!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ --> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;"> <defs> <filter id="contrast-filter" color-interpolation-filters="sRGB"> <feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/> <feMorphology operator="dilate" radius="2"/> <feComponentTransfer> <feFuncR type="linear" slope="-255" intercept="128"/> <feFuncG type="linear" slope="-255" intercept="128"/> <feFuncB type="linear" slope="-255" intercept="128"/> </feComponentTransfer> <feComposite operator="in" in2="SourceGraphic"/> </filter> </defs> </svg>將文字納入
<span>。<div class="badge--svg"> <span>Badge content</span> </div>將篩選器套用至
<span>。
.badge--svg span{
color: var(--badge-bg);
filter: url(#contrast-filter);
}