使用動態背景顏色,確保文字清晰易讀

網頁功能: contrast-color()

建構可重複使用的 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 可為動態色彩提供最穩定的自動備用機制。

@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 濾鏡

  1. 載入這個 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>
    
  2. 將文字納入 <span>。

    <div class="badge--svg">
      <span>Badge content</span>
    </div>
    
  3. 將篩選器套用至<span>。

.badge--svg span{
  color: var(--badge-bg);
  filter: url(#contrast-filter);
}