<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CSS on kenji.blog</title><link>http://kenji.blog/zh-tw/categories/css/</link><description>Recent content in CSS on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 12:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-tw/categories/css/index.xml" rel="self" type="application/rss+xml"/><item><title>部落格深色模式支援的 CSS 設計重點</title><link>http://kenji.blog/zh-tw/p/dark-mode-css-design-patterns/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/p/dark-mode-css-design-patterns/</guid><description>&lt;img src="http://kenji.blog/p/dark-mode-css-design-patterns/img/eyecatch.jpg" alt="Featured image of post 部落格深色模式支援的 CSS 設計重點" />&lt;p>現代 Web 開發中，深色模式（Dark Mode）的支援已經從單純的「有了會更好（Nice to have）」的功能，轉變為提升使用者體驗（UX）的「必備條件（Must have）」。特別是在部落格或文件網站這類以長時間閱讀文字為前提的媒體中，因為具有減輕使用者眼睛疲勞、降低裝置電池消耗的效果，深色模式支援的重要性可說是非常高。&lt;/p>
&lt;p>本文將從前端工程師的視角，非常深入地探討在部落格的深色模式支援中無法避免的技術課題，以及高維護性 CSS 設計的重點。從 CSS Custom Properties（CSS 變數）的活用、防止 FOUC（Flash of Unstyled Content）的進階 JavaScript 控制與 SSR 整合、確保無障礙（WCAG 2.1 AAA）的色彩設計（RGB、HSL 以及最新的 OKLCH），甚至到使用 Tailwind CSS 的實作程式碼範例，網羅了深色模式實作的所有內容。&lt;/p>
&lt;hr>
&lt;h2 id="1-透過-css-custom-propertiescss-變數進行主題設計的基礎">1. 透過 CSS Custom Properties（CSS 變數）進行主題設計的基礎
&lt;/h2>&lt;p>在實作深色模式時，目前最標準且最強大的手法就是利用 &lt;strong>CSS Custom Properties（CSS 變數）&lt;/strong> 的方法。相對於 Sass 等 CSS 預處理器的變數（&lt;code>$color&lt;/code>）是在編譯時靜態解析，CSS 變數則是在瀏覽器的執行階段動態解析並覆寫。這使得只要透過 JavaScript 切換 class，就能瞬間改變整個頁面的色調。&lt;/p>
&lt;h3 id="11-基本色彩主題的定義">1.1 基本色彩主題的定義
&lt;/h3>&lt;p>首先，使用 &lt;code>:root&lt;/code> 偽類別來定義淺色模式（預設）的調色盤。然後，當賦予如 &lt;code>[data-theme='dark']&lt;/code> 這樣的屬性（或是 &lt;code>.dark&lt;/code> class）時，覆寫這些變數，這是最常見的設計模式。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 淺色模式（預設）的變數定義 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f3f4f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4b5563&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#3b82f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e5e7eb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 深色模式時的變數覆寫 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 實際套用 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>像這樣，將排版與字體的指定和顏色（主題）的指定完全分離，能讓 CSS 的維護性有飛躍性的提升。&lt;/p>
&lt;h3 id="12-活用-media-prefers-color-scheme-dark">1.2 活用 @media (prefers-color-scheme: dark)
&lt;/h3>&lt;p>當作業系統層級設定為深色模式時，從使用者初次造訪網站起就自動套用深色主題，這在 UX 的觀點上是比較理想的。能夠實現這點的就是 &lt;code>@media (prefers-color-scheme: dark)&lt;/code> 這個媒體查詢。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 當 OS 環境設定為深色模式時的備用方案（Fallback） */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">media&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nt">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nt">dark&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在這種寫法中，除非使用者明確選擇淺色模式（&lt;code>data-theme='light'&lt;/code>），否則會尊重作業系統的深色模式設定來覆寫變數。&lt;/p>
&lt;hr>
&lt;h2 id="2-理解色彩空間與無障礙設計wcag-21-aaa">2. 理解色彩空間與無障礙設計（WCAG 2.1 AAA）
&lt;/h2>&lt;p>在深色模式的色彩設計中，單純地「把背景變黑、文字變白」是不夠的。對比太強會產生光暈反而難以閱讀，對比太低則會損害易視性。在網頁內容無障礙指南 (WCAG) 中，為了確保易視性，對比度有著嚴格的定義。&lt;/p>
&lt;h3 id="21-wcag-對比度的計算公式">2.1 WCAG 對比度的計算公式
&lt;/h3>&lt;p>WCAG 中的對比度（Contrast Ratio） $CR$ ，是使用背景色與前景色的相對亮度（Relative Luminance）以下列方式定義：&lt;/p>
$$CR = \frac{L_{lighter} + 0.05}{L_{darker} + 0.05}$$&lt;p>這裡的 $L_{lighter}$ 是較亮顏色的相對亮度，$L_{darker}$ 是較暗顏色的相對亮度（值的範圍從 0.0 到 1.0）。為了達到 WCAG 2.1 的 AAA 等級，一般文字需要 &lt;strong>7:1 以上&lt;/strong>，大型文字則需要 &lt;strong>4.5:1 以上&lt;/strong> 的對比度。&lt;/p>
&lt;p>相對亮度 $L$ 是從 sRGB 色彩空間的 RGB 值透過以下複雜的數學公式計算而來：&lt;/p>
$$L = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B$$&lt;p>各成分（$R, G, B$）使用原始 8 位元值（$R_{sRGB}$）除以 255 的標準化值，進行以下轉換以解開伽瑪校正：&lt;/p>
$$
R, G, B =
\begin{cases}
\frac{C_{sRGB}}{12.92} &amp; \text{if } C_{sRGB} \le 0.03928 \\
\left( \frac{C_{sRGB} + 0.055}{1.055} \right)^{2.4} &amp; \text{otherwise}
\end{cases}
$$&lt;p>手動進行這項計算很困難，但透過運用色彩設計工具，可以機械式地選出符合對比度 7:1 ($CR \ge 7.0$) 的顏色。&lt;/p>
&lt;h3 id="22-hsl-vs-rgb-vs-oklch">2.2 HSL vs RGB vs OKLCH
&lt;/h3>&lt;p>過去在建立調色盤時，主流是 RGB 或 HSL。然而，這些在「知覺均勻度」的觀點上有著很大的缺陷。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RGB&lt;/strong>: 是機械式的光的三原色，人類很難直觀地進行「調亮」、「調暗」這類的調整。&lt;/li>
&lt;li>&lt;strong>HSL&lt;/strong>: 使用色相 (Hue)、彩度 (Saturation)、亮度 (Lightness)，但 HSL 的「亮度 (L)」與人類眼睛知覺到的亮度並不一致。例如，在 HSL 中亮度為 50% 的純黃色和純藍色，在數值上亮度相同，但在人類眼中黃色看起來卻亮得多。&lt;/li>
&lt;li>&lt;strong>OKLCH&lt;/strong>: 是近年在 CSS Color Module Level 4 中導入的最新色彩空間。由亮度 (Lightness，知覺明度)、彩度 (Chroma)、色相 (Hue) 組成，&lt;strong>完全符合人類的視覺特性（知覺均勻）&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>透過使用 OKLCH，即使改變色相（Hue），也能保持相同的知覺明度（Lightness），因此深色模式用的調色盤生成變得極具預測性且安全。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 使用 OKLCH 的 CSS 變數定義範例 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 淺色模式的基礎明度較高，彩度較低 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.98&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.25&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 深色模式只需反轉明度，就容易維持知覺對比度 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.20&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.95&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.75&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c">/* 針對深色模式稍微調亮以確保易視性 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>像這樣採用 OKLCH，能夠簡單地建構出在多個主題間確保一致對比度（WCAG AAA 水準）的邏輯。&lt;/p>
&lt;hr>
&lt;h2 id="3-防止-foucflash-of-unstyled-content與-ssr-hydration">3. 防止 FOUC（Flash of Unstyled Content）與 SSR Hydration
&lt;/h2>&lt;p>在支援深色模式時，最讓開發者頭痛的就是被稱為 &lt;strong>FOUC（Flash of Unstyled Content）&lt;/strong> 的畫面閃爍問題。&lt;/p>
&lt;h3 id="31-透過用戶端-js-切換主題的陷阱">3.1 透過用戶端 JS 切換主題的陷阱
&lt;/h3>&lt;p>在 React 或 Vue 等 SPA（或是透過 SSG 建立的靜態網站）中，一般的做法是將使用者的設定儲存在 &lt;code>localStorage&lt;/code>，再透過 JavaScript 讀取並切換主題。然而，如果將這個處理放在 React 的 &lt;code>useEffect&lt;/code> 等地方執行，就會發生以下問題：&lt;/p>
&lt;ol>
&lt;li>瀏覽器渲染淺色模式的 HTML/CSS。&lt;/li>
&lt;li>JS 的 Bundle 被載入並執行。&lt;/li>
&lt;li>從 &lt;code>localStorage&lt;/code> 讀取 &lt;code>dark&lt;/code> 設定。&lt;/li>
&lt;li>HTML 被賦予 &lt;code>dark&lt;/code> class，畫面突然變暗（閃爍）。&lt;/li>
&lt;/ol>
&lt;h3 id="32-完美的-fouc-防止對策活用-cookie-與-ssr">3.2 完美的 FOUC 防止對策：活用 Cookie 與 SSR
&lt;/h3>&lt;p>為了完全防止 FOUC，並預防 Hydration 錯誤，最佳實踐是&lt;strong>將使用者的主題設定儲存在 &lt;code>document.cookie&lt;/code> 中，並在伺服器端渲染（SSR）階段回傳已賦予適當 class 的 HTML&lt;/strong>。&lt;/p>
&lt;p>以下的循序圖展示了利用 Cookie 進行主題初始化的理想流程：&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant User as &amp;#34;使用者&amp;#34;
participant Browser as &amp;#34;瀏覽器 OS&amp;#34;
participant Server as &amp;#34;伺服器 SSR&amp;#34;
participant DOM as &amp;#34;用戶端 DOM&amp;#34;
User-&amp;gt;&amp;gt;Browser: &amp;#34;請求頁面&amp;#34;
Browser-&amp;gt;&amp;gt;Server: &amp;#34;帶有 Cookie (theme=dark) 的 GET 請求&amp;#34;
Server--&amp;gt;&amp;gt;Browser: &amp;#34;回傳帶有 &amp;lt;html data-theme=&amp;#39;dark&amp;#39;&amp;gt; 的 HTML&amp;#34;
Browser-&amp;gt;&amp;gt;DOM: &amp;#34;渲染初始 HTML (無 FOUC)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;執行 Hydration 腳本&amp;#34;
alt &amp;#34;Cookie 不存在 (初次造訪)&amp;#34;
DOM-&amp;gt;&amp;gt;Browser: &amp;#34;檢查 OS prefers-color-scheme&amp;#34;
Browser--&amp;gt;&amp;gt;DOM: &amp;#34;matchMedia 結果 (例如: dark)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;透過行內阻擋腳本設定 data-theme=&amp;#39;dark&amp;#39;&amp;#34;
end
&lt;/pre>
&lt;h3 id="33-透過行內腳本建立防線無法使用-cookie-的靜態網站情況">3.3 透過行內腳本建立防線（無法使用 Cookie 的靜態網站情況）
&lt;/h3>&lt;p>如果是只有 SSG（靜態網站生成）而無法進行 SSR 的部落格（如 Hugo、Gatsby，或 Astro 的靜態匯出等），必須在 &lt;code>&amp;lt;head&amp;gt;&lt;/code> 標籤內部配置會阻擋渲染執行的行內（inline）JavaScript，在 DOM 渲染前一刻賦予 class 的手法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- 配置在 &amp;lt;head&amp;gt; 內的最後面 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">osTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">osTheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data-theme&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>這個小腳本會阻擋瀏覽器的渲染並立即執行，因此在畫面渲染的時間點，&lt;code>data-theme&lt;/code> 屬性已經被設定好，可以完全防止畫面的閃爍（FOUC）。&lt;/p>
&lt;hr>
&lt;h2 id="4-tailwind-css-與原生-scsscss-的實作方法">4. Tailwind CSS 與原生 SCSS/CSS 的實作方法
&lt;/h2>&lt;p>在將深色模式實際整合進專案時，必須理解各項工具的做法。&lt;/p>
&lt;h3 id="41-tailwind-css-中的深色模式">4.1 Tailwind CSS 中的深色模式
&lt;/h3>&lt;p>Tailwind CSS 預設提供了 &lt;code>dark:&lt;/code> 變體（variant），可以非常簡單地實作深色模式。在設定檔（&lt;code>tailwind.config.js&lt;/code>）中設定 &lt;code>darkMode&lt;/code> 屬性。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tailwind.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;#39;media&amp;#39; (依賴 OS 設定) 或是 &amp;#39;class&amp;#39; (可手動切換)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">darkMode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 利用 CSS 變數來擴充 Tailwind 的調色盤 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">primary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-primary) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-background) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在 HTML 端只需像下面這樣賦予 class 即可。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Hello World&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind makes dark mode incredibly easy.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>然而，對所有元素都寫上 &lt;code>dark:bg-xxx&lt;/code> 也會成為元件肥大的原因。在大型部落格或應用程式中，推薦採用&lt;strong>以 CSS 變數為基礎，並從 Tailwind 參考這些 CSS 變數&lt;/strong>的混合式設計（語意化色彩設計）。&lt;/p>
&lt;p>以下是展示 CSS 變數的繼承與套用層級的類別圖。&lt;/p>
&lt;pre class="mermaid">
classDiagram
class GlobalCSSVariables {
&amp;#34;--color-brand-500&amp;#34;
&amp;#34;--color-gray-900&amp;#34;
}
class SemanticVariables {
&amp;#34;--bg-primary&amp;#34;
&amp;#34;--text-base&amp;#34;
&amp;#34;--accent&amp;#34;
}
class TailwindConfig {
&amp;#34;theme.colors.background&amp;#34;
&amp;#34;theme.colors.primary&amp;#34;
}
class UIComponents {
&amp;#34;class=&amp;#39;bg-background text-primary&amp;#39;&amp;#34;
}
GlobalCSSVariables &amp;lt;|-- SemanticVariables : &amp;#34;:root &amp;amp; .dark&amp;#34;
SemanticVariables &amp;lt;|-- TailwindConfig : &amp;#34;tailwind.config.js&amp;#34;
TailwindConfig &amp;lt;.. UIComponents : &amp;#34;套用工具類別&amp;#34;
&lt;/pre>
&lt;h3 id="42-原生-scsscss-的實作活用-mixin">4.2 原生 SCSS/CSS 的實作（活用 Mixin）
&lt;/h3>&lt;p>在不使用 Tailwind，而是自行撰寫 SCSS 的專案中，可以活用 &lt;code>@mixin&lt;/code> 來封裝深色模式的樣式。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* SCSS Mixin 的定義 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">@mixin&lt;/span>&lt;span class="nf"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 支援 [data-theme=&amp;#39;dark&amp;#39;] 屬性，或是 OS 設定這兩者 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@media&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">dark&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">:root:not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 使用範例 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#eeeeee&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@include&lt;/span>&lt;span class="nd"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1a202c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#e2e8f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#2d3748&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>這個方法很直觀，但編譯後的 CSS 檔案大小容易肥大（媒體查詢會複製到各個選擇器上），因此目前趨勢依然是轉向以 CSS 變數（Custom Properties）為中心的設計。&lt;/p>
&lt;hr>
&lt;h2 id="5-圖片image與-svg-的深色模式最佳化">5. 圖片（Image）與 SVG 的深色模式最佳化
&lt;/h2>&lt;p>即使文字或背景的色彩設計完成，如果配置為內容的圖片或圖示（SVG）仍維持淺色模式的狀態，在深色模式下會顯得非常刺眼且突兀。對這些元素進行最佳化也是不可或缺的。&lt;/p>
&lt;h3 id="51-降低圖片亮度的-css-濾鏡">5.1 降低圖片亮度的 CSS 濾鏡
&lt;/h3>&lt;p>像是照片等點陣圖，在深色模式下直接顯示有時會過於刺眼。使用 CSS 的 &lt;code>filter&lt;/code> 屬性，稍微降低圖片的亮度（brightness）與對比度（contrast），就能讓圖片自然地融入深色主題的 UI 中。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">src&lt;/span>&lt;span class="o">*=&lt;/span>&lt;span class="s2">&amp;#34;.svg&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 降低亮度，稍微提高對比度 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">filter&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 懸停時恢復原始亮度（使用者想要看清楚細節時） */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-透過-picture-標籤切換圖片">5.2 透過 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 標籤切換圖片
&lt;/h3>&lt;p>Logo 圖片或是說明的圖解（背景固定為白色的 JPEG 等），光靠濾鏡處理是無法解決的。在這種情況下，正確的做法是使用 HTML 的 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 元素和媒體查詢，來切換顯示深色模式用的另一張圖片檔案。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 針對深色模式 OS 設定的使用者顯示這張 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-dark.png&amp;#34;&lt;/span> &lt;span class="na">media&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 預設（淺色模式） --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-light.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;網站 Logo&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※不過，這個方法無法與透過 &lt;code>localStorage&lt;/code> 等進行的手動切換連動（只依賴 OS 設定），因此如果實作了手動切換功能，就需要透過 JS 動態改寫圖片的 &lt;code>src&lt;/code>，或是透過 CSS class 切換 &lt;code>display: none&lt;/code>。&lt;/p>
&lt;h3 id="53-svg-圖示的-currentcolor-支援">5.3 SVG 圖示的 &lt;code>currentColor&lt;/code> 支援
&lt;/h3>&lt;p>在圖示等地方使用的行內（inline）SVG，最聰明的做法是讓填滿的顏色與父元素的文字顏色連動。在 SVG 的 &lt;code>fill&lt;/code> 或 &lt;code>stroke&lt;/code> 屬性中指定 &lt;code>currentColor&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- CSS 的 color 屬性值（如 var(--text-primary)）會自動套用 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">viewBox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0 0 24 24&amp;#34;&lt;/span> &lt;span class="na">fill&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;currentColor&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">path&lt;/span> &lt;span class="na">d&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;M12 2L2 22h20L12 2z&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>這樣一來，當切換到深色模式且父元素的文字顏色變成白色系時，SVG 圖示也會自動變成白色系。&lt;/p>
&lt;hr>
&lt;h2 id="6-總結邁向永續的深色模式設計">6. 總結：邁向永續的深色模式設計
&lt;/h2>&lt;p>為了在部落格或 Web 應用程式中實作高品質的深色模式，網羅了以下重點的 CSS 設計是不可或缺的。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>活用 CSS Custom Properties&lt;/strong>：避免硬編碼（hardcoding）指定顏色，將其抽象化為語意化的變數名稱（例如：&lt;code>--bg-primary&lt;/code>）。&lt;/li>
&lt;li>&lt;strong>採用 OKLCH 色彩空間&lt;/strong>：在知覺均勻的色彩空間中，有邏輯地設計出滿足 WCAG 2.1 AAA 的高無障礙對比度（7:1 以上）。&lt;/li>
&lt;li>&lt;strong>貫徹 FOUC 對策&lt;/strong>：透過 SSR 與 Cookie 的整合，或是在 &lt;code>&amp;lt;head&amp;gt;&lt;/code> 內阻擋渲染的行內腳本，完全排除初次載入時的畫面閃爍。&lt;/li>
&lt;li>&lt;strong>媒體與資源的最佳化&lt;/strong>：充分運用 &lt;code>filter: brightness()&lt;/code>、&lt;code>currentColor&lt;/code> 以及 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 標籤，讓文字以外的元素也能與深色主題協調。&lt;/li>
&lt;/ol>
&lt;p>超越單純的「顏色反轉」，這些細膩的考量正是現代部落格能長久受使用者喜愛、提供不易造成眼睛疲勞的優良閱讀體驗（Reading Experience）的必備條件。即將導入深色模式的開發者們，請務必參考本文的設計模式。&lt;/p></description></item></channel></rss>