不做暗色模式的網站,在 2026 年不會停留在亮色,而是會被別人的演算法改成暗色。Chrome for Android 的 Auto Dark Theme 會對「沒有表態」的網站自動生成暗色版本,Android WebView 的 algorithmic darkening 在 app 端做同一件事,兩者的觸發條件都寫得很清楚:當網頁內容沒有使用 prefers-color-scheme、且作者沒有明確關閉時,UA 就接手。 所以真正的選項只有兩個——設計一份暗色主題,或明確宣告只支援亮色。沉默是最糟的第三種。
本專欄不談「暗色比較潮」。每一篇原子筆記處理一個可落地的元件:給完整可貼上的 CSS/JS、標明各自的必要條件與失效情境、並清楚區分穩定標準、瀏覽器相依行為與 hack。最重點的是 in-app WebView 那兩篇——那是雙主題最容易失控、也最少被正確處理的環境。
三個先決事實
一、prefers-color-scheme 與 color-scheme 是相反方向的兩件事。 前者是「瀏覽器告訴你使用者想要什麼」,後者是「你告訴瀏覽器這份文件支援什麼」。只寫前者的網站會得到白捲軸配深頁面、輸入框還是白底的半套畫面,因為 UA 從沒被告知它可以用暗色去畫那些原生元件。
二、In-app WebView 的 prefers-color-scheme 不是使用者的系統設定,是宿主 app 的佈景設定。 Android 官方文件寫得很明白:WebView 依 app 佈景的 isLightTheme 決定回報值;iOS 則來自 overrideUserInterfaceStyle。使用者手機是亮色、app 是暗色,你的頁面就會收到 dark。這不是你能修的,你只能讓兩套主題都完整可用。
三、閃爍問題只有一半需要 JS 解決。 系統偏好由 CSS 媒體查詢處理,不需要 JS 也不需要伺服器知道;真正需要首屏腳本補救的只有「使用者手動覆寫了系統偏好」這一種情況。搞清楚這個區分,就不會寫出一堆把系統偏好判斷塞進 JS 的錯誤實作。
能力階段
| 階段 | 你已經能做到 | 對應筆記 |
|---|---|---|
| 認識 | 說得出 prefers-color-scheme 與 color-scheme 的方向差異、知道不宣告的代價 | 01–02 |
| 會實作 | 能用語意化 token 與 light-dark() 做出可維護的兩套主題,並處理 fallback | 03–04 |
| 會切換 | 能做出三態切換器、選對儲存方式、首屏不閃爍 | 05–06 |
| 會設計 | 兩套主題都過 WCAG、暗色用明度而非陰影表達層次、圖片與圖示各用對的做法 | 07–08 |
| 會收邊 | 原生控件、捲軸、autofill、Windows 高對比模式都不破 | 09 |
| 會上行動端 | theme-color、安全區域、Auto Dark Theme、iOS/Android 差異 | 10 |
| 會處理 WebView | 理解 WebView 的機制、能診斷強制反色、有完整測試矩陣 | 11–12 |
| 能排錯 | 從症狀反推機制,三步定位問題 | 13–14 |
「我想做到⋯⋯」索引
- 我只想要最小可行的正確版本 → 三行:
<meta name="color-scheme" content="light dark">放在 CSS 之前、:root { color-scheme: light dark }、顏色用light-dark()。見 prefers-color-scheme 與 color-scheme。 - 我的網站載入時會白閃一下 → 避免主題閃爍:先分辨是「宣告太晚」還是「JS 還原太慢」,兩者修法不同。
- 我要做切換器,但不想害使用者脫離系統偏好 → 三態切換器與偏好儲存:用「鍵不存在」表示跟隨系統,並處理跨分頁同步。
- 我有一份寫死 hex 的舊 CSS 要改 → 語意化顏色 token 設計 的五步遷移法,中間狀態可上線。
- 我切了暗色但輸入框、下拉、捲軸還是亮的 → 沒設
color-scheme。見 原生控件、捲軸與 forced-colors。 - 暗色下卡片全部塌成一片/陰影看不見 → 對比、暗色不是純黑、高程與陰影:暗色靠背景明度表達層次。
- 暗色下圖片刺眼、logo 變成亮斑 → 圖片、圖示與 media 的決策樹,不同類型的圖用不同做法。
- 同一個連結在 Facebook/LINE 裡的主題跟 Safari 不一樣 → In-app WebView 專篇,含可直接複製的一頁式診斷頁。
- 頁面被 Android 強制反成暗色,我的暗色 CSS 沒生效 → 強制反色的偵測與對抗:JS 偵測不可靠,正解是讓觸發條件不成立。
- 我要一份可以逐格打勾的驗收清單 → 測試矩陣 的 17 格表,以及 排錯手冊。
- 我想知道哪些是標準、哪些是 hack → 主張 vs 可佐證 的 A/B/C/D 四級。
學習路徑
- 為什麼要支援雙主題:不宣告的代價不是「維持亮色」
- prefers-color-scheme 與 color-scheme:偵測偏好 vs 宣告能力
- 語意化顏色 token 設計:不要寫死 hex
- CSS 實作:變數切換、light-dark() 與 fallback
- dark
- 避免主題閃爍:FOUC 的真正成因與四種解法
- 對比、暗色不是純黑、高程與陰影
- 圖片、圖示、logo 與 media 的雙主題處理
- 原生控件、捲軸與 forced-colors
- Android 差異
- In-app WebView 專篇:WKWebView、Android WebView 與社群 App 內建瀏覽器
- 強制反色的偵測、對抗與測試矩陣
- 常見錯誤與排錯手冊
- 主張 vs 可佐證,與延伸資源
最小正確實作(可直接複製)
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<script>
// 只還原「手動覆寫」,系統偏好交給 CSS
try {
var t = localStorage.getItem('theme');
if (t === 'light' || t === 'dark') {
document.documentElement.dataset.theme = t;
document.documentElement.style.colorScheme = t;
document.querySelector('meta[name="color-scheme"]').content = t;
}
} catch (e) {}
</script>
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0e1013">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="/style.css">
</head>:root {
color-scheme: light dark;
--bg-canvas: light-dark(#ffffff, #0e1013);
--bg-surface: light-dark(#f7f8fa, #14161a);
--text: light-dark(#16191d, #e6e8eb);
--border: light-dark(#e2e6ea, #262a30);
--accent: light-dark(#3b6fd4, #8fb3f0);
--accent-text: light-dark(#ffffff, #0e1013);
accent-color: var(--accent);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
html, body { background: var(--bg-canvas); color: var(--text); }這段涵蓋了:早期宣告(不閃爍)、原生控件跟著換色、手動覆寫可還原、行動端瀏覽器 UI 染色、以及讓 Android/Chrome 的自動反色不必出手。剩下的都是細節與收邊。
🔍 待解問題 / 持續追蹤
- Facebook/Instagram/LINE/X 等 app 內建瀏覽器的具體版本行為未逐一實測。本專欄的相關結論是從 Android WebView 與 WKWebView 的官方機制推導,需要用診斷頁做實機驗證並建立記錄。
- 桌面 Chrome 對
theme-colormedia屬性的支援是否已從「僅限已安裝 PWA」擴大到一般分頁?引用資料為 2021 年前後,可能已變動。 light-dark()何時實際進入 Baseline widely available(預估 2026-11 前後),屆時建構期降級是否還必要?- 哪些 in-app browser 使用非持久化資料存放區導致主題偏好無法保存?這會影響切換器的降級設計,但缺乏公開清單。
- 主題切換用 View Transitions 的效能特性,尤其在低階 Android 裝置上的掉幀情形,尚未實測。
- CSS 的
contrast-color()/color-contrast()這類自動選前景色的功能成熟後,能否取代手工維護的--accent-text配對?
🕒 更新紀錄
- 2026-08-14:初版,建立 hub + 14 篇原子筆記(基礎兩個關鍵字、token 設計、CSS 實作與 fallback、三態切換器、避免閃爍、視覺細節、圖片處理、原生控件與 forced-colors、mobile web、in-app WebView 專篇、強制反色與測試矩陣、排錯手冊、證據分級)。