教學·網頁7 分鐘閱讀·Updated 2026 年 10 月·ExodeUI 團隊
平滑的明暗模式轉換:令牌、CSS 和運動
如何建立一個尊重系統設定、避免載入時閃爍、設定本機控制項樣式和動畫而不影響可訪問性的明/暗主題切換。
直接總結與定義
核心要點
在語義標記上建立主題,預設遵循首選顏色方案的系統設置,讓人們覆蓋它,並設置顏色方案屬性以使表單控制項和捲軸匹配。在頁面呈現之前套用已儲存的主題以避免閃爍。保持切換動畫簡短,例如快速交叉淡入淡出或視圖轉換,並在減少運動時跳過它。
從系統設定開始
首選顏色方案媒體功能會告訴您使用者是否向作業系統要求淺色或深色主題。使用它作為預設值,並添加一個覆蓋它並被記住的應用內開關。
帶有令牌的主題,而不是覆蓋
將顏色定義為語義標記(表面、文字、邊框、重音),並為每個主題賦予自己的值,通常作為根元素上的 CSS 自訂屬性。組件在主題之間永遠不會改變;只有代幣值可以。這與多品牌設計系統使用的三層令牌方法相同。
不要忘記本機控件
MDN 解釋 color-scheme 屬性告訴瀏覽器元素支援哪種方案,這會更改表單控制項、捲軸和其他內建 UI 的預設顏色。沒有它,黑暗的頁面可以顯示明亮的白色輸入和捲軸。
避免載入時出現閃光
如果在頁面渲染後透過 JavaScript 應用程式儲存的主題,人們會看到錯誤主題的閃爍。在樣式表繪製之前,儘早應用它,在文件頭中使用一個小的內聯腳本或伺服器渲染的屬性。
仔細製作開關動畫
大約 150 到 250 毫秒的短暫交叉淡入淡出使這種變化感覺是經過深思熟慮的。避免在每個元素上轉換每個顏色屬性,這在大頁面上可能會很慢;單一視圖轉換快照通常更平滑。使用標籤或工具提示使切換本身清晰,並在啟用減少運動時刪除動畫。
生產實施片段
互動組件實現
/* Tokens per theme */
:root { color-scheme: light dark; --surface: #ffffff; --text: #121212; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) { --surface: #121212; --text: #f2f2f2; }
}
:root[data-theme="dark"] { color-scheme: dark; --surface: #121212; --text: #f2f2f2; }
:root[data-theme="light"] { color-scheme: light; }
body { background: var(--surface); color: var(--text); }
// Switch with a View Transition where supported, instantly otherwise
function setTheme(theme) {
const apply = () => { document.documentElement.dataset.theme = theme; localStorage.setItem('theme', theme); };
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!document.startViewTransition || reduce) return apply();
document.startViewTransition(apply);
}要記住的關鍵 Principle
- 預設為首選顏色方案;讓人們推翻並記住它
- 帶有語義標記的主題,而不是每個組件的覆蓋
- 設定顏色方案以使本機控制項相符
- 首次繪製先前套用已儲存的主題
- 保持開關動畫簡短並尊重減少的動作
常見問題解答
來源
- prefers-color-scheme — MDN 網路文檔
- color-scheme — MDN 網路文檔
- View Transition API — MDN 網路文檔