Tailwind CSS 深色模式完整實作:從 Toggle 切換到系統偏好自動偵測
最近幫朋友改一個個人部落格的時候,他問我:「為什麼我的網站晚上看眼睛好痛?」嗯,說真的這年頭如果你的網站沒有深色模式,大概就像咖啡廳沒有 Wi-Fi 一樣,使用者會直接離開。
好消息是,如果你已經在用 Tailwind CSS v4,實作深色模式比你想像的簡單很多。今天就來手把手教你怎麼做。
為什麼深色模式已成為標配
根據 Android 官方的統計,超過 80% 的使用者會在手機上開啟深色模式。而 Web 端也有類似的趨勢 — 特別是開發者和重度使用者,幾乎都是深色模式的愛好者。
除了使用者偏好,深色模式還有幾個實際的好處:
- 減少眼睛疲勞:在低光環境下,深色背景能降低螢幕的整體亮度
- 省電:OLED 螢幕在顯示黑色時完全不發光,可以有效延長電池壽命
- 提升閱讀體驗:許多使用者反映深色模式下長時間閱讀更舒適
- SEO 間接影響:更好的使用體驗 → 更長的停留時間 → 搜尋引擎更喜歡你
Tailwind 的 dark: 前綴機制
Tailwind CSS 內建了 dark: 前綴,讓你可以針對深色模式設定不同的樣式。用法超級直覺:
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<h1 class="text-2xl font-bold text-black dark:text-white">
歡迎來到我的網站
</h1>
<p class="text-gray-600 dark:text-gray-400">
這段文字會根據模式自動切換顏色
</p>
</div>
看到了嗎?每個 utility class 前面加上 dark: 就能定義深色模式下的樣式。Tailwind 會自動幫你處理剩下的事。
class 策略 vs media 策略
Tailwind 提供兩種深色模式策略:
media 策略(預設):跟著系統設定走。如果使用者的作業系統設定為深色模式,網站就自動切換。設定很簡單,在 Tailwind v4 中這是預設行為。
class 策略:透過在 <html> 標籤加上 class="dark" 來手動控制。這個方式更靈活,因為你可以讓使用者自己選擇,而不是被系統設定綁定。
如果你想做 Toggle 切換,一定要用 class 策略。在 Tailwind v4 中,你只需要在 CSS 檔案裡加一行:
@custom-variant dark (&:where(.dark, .dark *));
這樣就從 media 策略切換成 class 策略了。接下來可以搭配 CSS Container Queries 做出更精細的響應式深色模式。
實作 Toggle 切換元件
來寫一個簡單又好看的 Toggle 元件。我習慣用太陽和月亮的 icon 來代表淺色和深色模式:
// ThemeToggle.jsx
import { useState, useEffect } from 'react';
export default function ThemeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// 初始化:讀取 localStorage 或系統偏好
const saved = localStorage.getItem('theme');
if (saved === 'dark' ||
(!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
setIsDark(true);
document.documentElement.classList.add('dark');
}
}, []);
const toggleTheme = () => {
const newIsDark = !isDark;
setIsDark(newIsDark);
document.documentElement.classList.toggle('dark', newIsDark);
localStorage.setItem('theme', newIsDark ? 'dark' : 'light');
};
return (
<button onClick={toggleTheme} aria-label="切換深色模式"
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700">
{isDark ? '☀️' : '🌙'}
</button>
);
}
這段 code 做了三件事:初始化時讀取之前的設定、點擊時切換 class、把偏好存到 localStorage。
用 localStorage 記住使用者偏好
有個常被忽略的問題:使用者切換了深色模式,但下次進來又變回淺色了。這就是為什麼一定要搭配 localStorage。
但光靠 React 的 useEffect 還不夠,因為在 React hydration 之前會有一瞬間的閃爍(俗稱 FOUC — Flash of Unstyled Content)。解法是在 <head> 裡面加一段 blocking script:
<script>
(function() {
var theme = localStorage.getItem('theme');
if (theme === 'dark' ||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
})();
</script>
這段 script 會在任何 CSS 或 JS 載入之前就先把 dark class 加上去,完全避免閃爍。
偵測系統深色模式偏好
有些使用者沒有手動切換過,你應該尊重他們的系統設定。用 matchMedia 就能偵測:
// 偵測系統偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
// 監聽系統偏好變化
prefersDark.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
// 只有在使用者沒有手動設定時,才跟著系統走
document.documentElement.classList.toggle('dark', e.matches);
}
});
這邊有個重要的邏輯:如果使用者已經手動選擇了模式,就不要再被系統設定覆蓋。優先順序是「使用者手動選擇 > 系統偏好 > 預設淺色」。
深色模式的配色設計要點
很多人以為深色模式就是把白變黑、黑變白,但這樣做出來的效果通常很醜。分享幾個設計心得:
- 不要用純黑:用
gray-900(#111827)而非black(#000000),純黑背景配白字對比太強烈 - 降低文字亮度:用
gray-100或gray-200而非純白,減少刺眼感 - 調整品牌色彩度:深色背景上,高飽和度的顏色會顯得太刺眼,適度降低飽和度
- 用 elevation 表達層級:深色模式中不能用陰影(看不到),改用不同深淺的灰色表示層級
- 圖片加暗色濾鏡:在深色背景上,亮色圖片會很突兀,適度降低亮度
Google Material Design 3 的深色主題指南是很好的參考資料,建議搭配 CSS Grid 一起學習版面配色。
常見踩坑與解決方案
問題 1:第三方元件不支援深色模式
很多 UI library 的元件有自己的 inline style,不會被你的 dark class 影響。解法是用 CSS 變數來覆蓋,或選擇原生支援深色模式的元件庫。
問題 2:圖片在深色背景上太亮
<img class="dark:brightness-90 dark:contrast-95" />
問題 3:Tailwind 的 dark: 在某些元件裡不生效
這通常是因為你的元件被包在一個沒有繼承 dark class 的 portal 或 shadow DOM 裡。確保 dark class 在 DOM 的最上層(<html> 標籤)。
問題 4:SSR 環境下的閃爍
Next.js 等 SSR 框架需要特別處理,記得把剛才提到的 blocking script 放在 _document 的 <Head> 裡面。
結語
深色模式不只是一個視覺功能,它代表著你對使用者體驗的重視。有了 Tailwind CSS 的 dark: 前綴,實作成本真的很低。花個半天把它搞定,你的使用者(和他們的眼睛)會感謝你的。
如果你剛接觸 Tailwind,建議先看看 Tailwind CSS v4 新功能完整指南,打好基礎再來做深色模式會更順手。
繼續閱讀
Tailwind CSS 元件庫推薦:daisyUI、Headless UI、shadcn/ui 完整比較
Tailwind CSS 元件庫太多不知道怎麼選?這篇幫你比較 daisyUI、shadcn/ui、Headless UI 三大方案的差異。
相關文章
Tailwind CSS 元件庫推薦:daisyUI、Headless UI、shadcn/ui 完整比較
Tailwind CSS 元件庫太多不知道怎麼選?這篇幫你比較 daisyUI、shadcn/ui、Headless UI 三大方案的差異。
Tailwind CSS v4 新功能完整教學:升級指南與實戰範例
Tailwind CSS v4 帶來 CSS-first 設定、5 倍速度提升、原生 Container Queries 支援與統一工具鏈。本文完整介紹 v4 所有新功能,並提供從 v3 升級的實戰遷移指南與程式碼範例。
你可能也喜歡
探索其他領域的精選好文