建立真正可用的深色模式
為什麼不能直接反轉顏色?
亮色主題的陰影、灰色文字和邊框在深色背景會失去層次;狀態色太亮又會造成刺眼對比。深色模式是一套新的語意映射,不是 filter: invert(1)。
1. 建立兩組 semantic tokens
:root {
--surface-page: 248 250 252;
--surface-card: 255 255 255;
--text-primary: 15 23 42;
--text-muted: 71 85 105;
--border-default: 226 232 240;
--action-primary: 79 70 229;
}
[data-theme='dark'] {
--surface-page: 15 23 42;
--surface-card: 30 41 59;
--text-primary: 241 245 249;
--text-muted: 148 163 184;
--border-default: 51 65 85;
--action-primary: 129 140 248;
}
元件只讀取角色,不讀取 indigo 或 slate 的 raw 值。
2. 處理偏好順序
常見順序是:使用者明確選擇 > 儲存的偏好 > 作業系統偏好 > 預設亮色。初始化時先驗證值,再套用主題;無法使用 localStorage 時仍應正常顯示。
const stored = localStorage.getItem('theme');
const system = matchMedia('(prefers-color-scheme: dark)').matches;
const theme = stored === 'light' || stored === 'dark'
? stored
: system ? 'dark' : 'light';
document.documentElement.dataset.theme = theme;
正式網站需避免 SSR 頁面先用亮色再跳暗色,可在 head 放最小初始化腳本。
3. 逐元件測試
特別檢查 placeholder、disabled、focus ring、錯誤、成功、圖表輔助線和圖片。對比度不是唯一條件;色彩也不能是唯一的狀態訊號。
驗收
- 系統偏好和使用者偏好不會互相閃爍。
- 亮暗主題都能完成登入、表單和主要操作。
- 減少動態偏好仍然有效。
- 重新整理、無儲存空間和無效主題值都有安全預設。