網站導覽

設計基礎

色彩

以自適應角色建立介面色彩,結合圓角與陰影凸顯表面,並選擇明暗模式。

本頁目錄

#概覽

以角色建立介面色彩:表面、線條與文字的 class 在明暗模式中維持不變。當顏色本身就是重點,例如插圖或品牌點綴時,再選用色階。色彩變化應對應到角色、狀態,或讀者需要辨識的差異。

模式改變,角色不變

只需選擇一次角色。它的 light-dark() 值會跟隨元素配色方案,包括嵌在另一配色中的區域。

文字說明
  1. 保留角色. 兩個區域使用相同的表面、分隔線、強調文字與次要文字 class。
  2. 依區域解析. 角色值使用 light-dark()。每個區域的 color-scheme 會選擇明色或暗色,不需改變 class。

#選擇角色或色階

需求使用範例
頁面結構與文字層級色彩角色bg-surface-raised, b-line-divider, fg-text-muted
特定色彩色階bg-blue-5, fg-blue-60

同一個版面,兩種模式

亮色
暗色
兩個文件使用完全相同的 class。作用中的模式提供表面、線條與文字的值。
<section class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised fg-text-body">  <h2 class="m(0) text-lg font-regular fg-text-strong">Project updates</h2>  <p class="mt-xs mb-md text-sm fg-text-muted">Three milestones changed this week.</p>  <a class="fg-text-link underline" href="/updates">Continue</a></section>
產生的 CSS
@layer theme {  :root,  :host {    --color-zinc-40: oklch(68.6% .0028 265);    --color-zinc-60: oklch(48.6% .003 265);    --color-zinc-90: oklch(24.6% .0022 265)  }  :root,  :host {    --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90));    --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12));    --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40))  }  :root,  :host {    --color-white: oklch(100% 0 none)  }}@layer utilities {  .bg-surface-raised {    background-color: var(--color-surface-raised)  }  .b-line-divider {    border-color: var(--color-line-divider)  }  .fg-text-muted {    color: var(--color-text-muted)  }}

角色是色彩鍵名的一部分:bg-surface-raised 讀取 --color-surface-raised,b-line-divider 讀取 --color-line-divider。線條角色只設定顏色,因此邊框寬度與樣式須另外指定。色彩角色列出所有角色,調色盤則列出完整色階。

邊框色彩需要寬度與樣式搭配

所有方框起始皆為 border-style: none。範例的 border-box 尺寸相同,只改變所列邊框宣告。

文字說明
  1. b-line-divider 提供 border-color,b(2) 提供 border-width。兩者都不會改變 none 的邊框樣式。
  2. 加入 b-solid 後,2px 邊框才會畫出。邊框可沿用元件中其他已撰寫的必要條件,因此只看到色彩 class 時,請檢查完整樣式。

區分固定色階與語意角色

特定色彩使用色階;需要隨情境調整的決策則使用角色。

文字說明

兩條路徑分別呈現固定色階,以及選擇明暗值的角色。

#固定與自適應色相別名

fg-yellow 這類色相別名是固定色彩,在每個模式中都使用相同的值。fg-text-yellow 這類文字色相別名則會依模式選擇適合文字的前景色階。請在實際背景上檢查對比;自適應別名無法保證每種搭配都合適。

<svg class="fg-yellow" fill="currentColor" aria-hidden="true">…</svg><p class="fg-text-yellow">Pending review</p>
產生的 CSS
@layer theme {  :root,  :host {    --color-yellow-40: oklch(91.5% .1909 101.1);    --color-yellow-90: oklch(52.17% .1199 62.2)  }  :root,  :host {    --color-yellow: var(--color-yellow-40);    --color-text-yellow: light-dark(var(--color-yellow-90), var(--color-yellow-40))  }}@layer utilities {  .fg-text-yellow {    color: var(--color-text-yellow)  }  .fg-yellow {    color: var(--color-yellow)  }}

彩色標籤使用文字色相別名;內文、強調文字與連結則使用文字角色。


#凸起表面

凸起表面結合表面角色、圓角 token 與陰影 token。可互動的表面在滑鼠移入或取得焦點時,將陰影提高一級。一般卡片與控制項使用小圓角;pill 留給刻意完全圓潤的形狀,例如下方徽章。

凸起表面

亮色
暗色
兩份文件使用相同 class。表面、文字與陰影顏色由作用中的模式提供;陰影幾何形狀保持不變。
<a class="block p-md r-lg bg-surface-raised fg-text-body shadow-sm shadow-md:hover shadow-md:focus-visible" href="/notes">  <span class="flex items-center justify-between gap-sm">    <span class="fg-text-strong">Field notes</span>    <span class="px-xs py-3xs pill bg-line-subtle text-xs fg-text-muted">12 new</span>  </span></a>
產生的 CSS
@layer theme {  :root,  :host {    --radius-lg: .5rem  }  :root,  :host {    --shadow-sm: 0 0 0 1px light-dark(oklch(0% 0 none / .04), oklch(100% 0 none / .05)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24));    --shadow-md: 0 0 0 1px light-dark(oklch(0% 0 none / .05), oklch(100% 0 none / .06)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)), 0 8px 8px -4px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .26))  }}@layer utilities {  .r-lg {    border-radius: var(--radius-lg)  }  .pill {    border-radius: calc(infinity * 1px)  }  .shadow-sm {    box-shadow: var(--shadow-sm)  }  .shadow-md\:hover:hover {    box-shadow: var(--shadow-md)  }}

預設集的陰影以 light-dark() 設定顏色,因此 shadow-sm 保留幾何形狀,並跟隨元素的配色方案。pill 是固定 utility,不是圓角 token;寬高相等時會形成圓形。使用 rtl-lg、rtr-lg、rbl-lg 與 rbr-lg 可分別設定單一角落。各種尺度與自訂方式請見圓角與陰影。

相同 pill 圓角隨方框形狀調整

高度維持 64px。pill 提供圓角半徑,範例尺寸來自獨立的尺寸宣告。

文字說明
  1. pill 設定全圓角半徑,不設定寬度、高度或內距。
  2. 寬高相等時形成圓形。同高度但較寬的方框會形成膠囊,因此單靠 class 不保證圓形。

#明暗模式

預設集在根元素設定 color-scheme: light dark,角色與陰影則使用 light-dark(),因此頁面不需額外 class 就會跟隨系統偏好。若要指定模式,在根元素、巢狀區域或 Shadow Host 上設定 data-theme="light" 或 data-theme="dark":

一個 token,兩種區域配色

@theme {  --color-surface-notes: light-dark(#f4f4f5, #27272a);}@layer base {  [data-theme="light"] {    color-scheme: light;  }  [data-theme="dark"] {    color-scheme: dark;  }}
產生的 CSS
@layer base {  [data-theme="light"] {    color-scheme: light;  }  [data-theme="dark"] {    color-scheme: dark;  }}@layer theme {  :root,  :host {    --color-zinc-30: oklch(78% .0025 265);    --color-zinc-70: oklch(39.8% .0028 265)  }  :root,  :host {    --color-text-body: light-dark(var(--color-zinc-70), var(--color-zinc-30))  }  :root,  :host {    --font-weight-regular: 400;    --font-size-sm: .875rem;    --font-size-md: 1rem;    --font-size-lg: 1.125rem;    --text-sm: var(--font-size-sm);    --text-sm--line-height: 1.5;    --text-sm--letter-spacing: 0;    --text-md: var(--font-size-md);    --text-md--line-height: 1.6;    --text-md--letter-spacing: 0;    --text-lg: var(--font-size-lg);    --text-lg--line-height: 1.5;    --text-lg--letter-spacing: 0;    --radius-md: .375rem;    --radius-lg: .5rem;    --spacing-xs: .5rem;    --spacing-md: 1rem  }  :root,  :host {    --color-surface-notes: light-dark(#f4f4f5, #27272a)  }}@layer utilities {  .text-lg {    font-size: var(--text-lg);    line-height: var(--text-lg--line-height, normal);    letter-spacing: var(--text-lg--letter-spacing, normal)  }  .text-md {    font-size: var(--text-md);    line-height: var(--text-md--line-height, normal);    letter-spacing: var(--text-md--letter-spacing, normal)  }  .text-sm {    font-size: var(--text-sm);    line-height: var(--text-sm--line-height, normal);    letter-spacing: var(--text-sm--letter-spacing, normal)  }  .bg-surface-notes {    background-color: var(--color-surface-notes)  }  .r-lg {    border-radius: var(--radius-lg)  }  .r-md {    border-radius: var(--radius-md)  }  .fg-text-body {    color: var(--color-text-body)  }  .font-regular {    font-weight: var(--font-weight-regular)  }  .m\(0\) {    margin: calc(0 * 1px)  }  .mb-md {    margin-bottom: var(--spacing-md)  }  .mt-xs {    margin-top: var(--spacing-xs)  }  .mb\(0\) {    margin-bottom: calc(0 * 1px)  }  .wrap-anywhere {    overflow-wrap: anywhere  }  .p-md {    padding: var(--spacing-md)  }}
兩個區域都使用 bg-surface-notes。data-theme 屬性選擇不同配色,因此 light-dark() 提供暗色外層表面與亮色內層表面。

預設集已內建這兩個 data-theme 選擇器;範例 CSS 再次列出它們,以說明啟用方式。以 light-dark() 定義的自訂顏色也會以相同方式調整,切換模式不必重新編譯。巢狀區域會繼承父層 token,除非自行重新宣告;品牌主題應使用自己的選擇器,例如 [data-brand="ocean"],並在其中重新宣告角色 token。

@light 與 @dark 後綴是 prefers-color-scheme 媒體查詢。手動設定 data-theme 會改變 token,但不會啟用這些查詢。若 class 必須回應手動選擇,請宣告選擇器 mixin 並套用:

index.css
@mixin --manual-dark {  &:where([data-theme="dark"], [data-theme="dark"] *) { @contents; }}
<img class="(opacity:.85)@apply(--manual-dark)" src="/chart.png" alt="Weekly usage" />

這個 mixin 檢查的是屬性,而不是實際使用的配色方案,因此巢狀的亮色區域內也會相符。優先使用自適應 token;作用範圍與解析方式請見變數與模式。

祖先選擇器可以進入淺色區域

虛線路徑追蹤明確的 dark 屬性。內層淺色邊界改變 color-scheme,不會改變選擇器檢查的祖先關係。

文字說明
  1. 本文的 --manual-dark mixin 比對 [data-theme="dark"] 本身或其後代元素。
  2. 巢狀 data-theme="light" 區域中的圖表,仍是外層 dark 區域的後代。因此即使 light-dark() 值使用本地淺色配置,它的不透明度包裝仍符合。
  3. 應跟隨實際採用色彩配置的值,請使用自適應 token;屬性選擇器只檢查其明確指定的關係。

最近的配色方案決定顏色

巢狀邊界代表 color-scheme。Token 名稱與標記維持不變。

文字說明

亮色文件包住一個暗色區域。同一個語意 token 在外側繪製亮色表面,在區域內繪製暗色表面。

#在實際情境中檢查角色

在兩種配色中檢查相同內容。確認強調與次要文字和實際表面的對比、連結的一般與焦點狀態,以及用來辨識控制項的邊界。色彩 token 提供一個值,本身不會建立對比、確保焦點可見,或賦予狀態意義。

選取項目或狀態應搭配標籤、圖示或其他可見提示,避免只靠顏色。巢狀區域須個別驗證:深色頁面中的手動亮色區域,仍需有適當且易讀的表面與前景搭配。