ダークモード
この章では、Tailwind CSS でダークモードを実装する方法を学びます。
ダークモードの基本
dark: バリアントを使用してダークモード時のスタイルを指定します。
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
ライトモードでは白背景、ダークモードでは黒背景
</div>
切り替え方式
方式 1: メディアクエリ (デフォルト)
OS の設定に基づいて自動で切り替わります。
/* v4 のデフォルト動作 */
@import "tailwindcss";
方式 2: クラスベース
FOUC (Flash of Unstyled Content) に注意
クラスベース方式では、ページ読み込み時に JavaScript が実行されるまで一瞬ライトモードが表示される「FOUC (Flash of Unstyled Content)」が発生する場合があります。これを防ぐには、<head> 内の <script> タグで localStorage の値を読み取り、<html> にクラスを付与する処理を同期的に実行してください。
HTML の class 属性で手動制御します。
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
<!-- ルート要素に dark クラスを追加 -->
<html class="dark">
<body class="bg-white dark:bg-gray-900">
...
</body>
</html>
方式 3: セレクターベース
任意のセレクターでダークモードを制御します。
@import "tailwindcss";
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
<html data-theme="dark">
...
</html>
実装パターン
JavaScript での切り替え
<button id="theme-toggle" class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700">
<span class="hidden dark:block">🌙</span>
<span class="block dark:hidden">☀️</span>
</button>
<script>
const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
});
</script>
システム設定に従う + 手動切り替え
// 初期化
if (localStorage.theme === 'dark' ||
(!('theme' in localStorage) &&
window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// 手動でライトモードに
localStorage.theme = 'light';
document.documentElement.classList.remove('dark');
// 手動でダークモードに
localStorage.theme = 'dark';
document.documentElement.classList.add('dark');
// システム設定に従う
localStorage.removeItem('theme');
カラー設計
ダークモード用のカラーパレット
<!-- 背景色 -->
<div class="bg-white dark:bg-gray-900">レベル 1</div>
<div class="bg-gray-50 dark:bg-gray-800">レベル 2</div>
<div class="bg-gray-100 dark:bg-gray-700">レベル 3</div>
<!-- テキスト色 -->
<p class="text-gray-900 dark:text-white">メインテキスト</p>
<p class="text-gray-600 dark:text-gray-300">セカンダリテキスト</p>
<p class="text-gray-400 dark:text-gray-500">補足テキスト</p>
<!-- ボーダー -->
<div class="border-gray-200 dark:border-gray-700">ボーダー</div>
推奨カラーマッピング
| 用途 | ライト | ダーク |
|---|---|---|
| 背景 (ベース) | white | gray-900 |
| 背景 (セカンダリ) | gray-50 | gray-800 |
| 背景 (三次) | gray-100 | gray-700 |
| テキスト (メイン) | gray-900 | white |
| テキスト (セカンダリ) | gray-600 | gray-300 |
| テキスト (補足) | gray-400 | gray-500 |
| ボーダー | gray-200 | gray-700 |
| ホバー背景 | gray-100 | gray-800 |
CSS 変数を使ったカラーシステム
v4 では CSS 変数でテーマカラーを管理できます。
@import "tailwindcss";
@theme {
/* ライトモードのデフォルト値 */
--color-bg-primary: #ffffff;
--color-bg-secondary: #f9fafb;
--color-text-primary: #111827;
--color-text-secondary: #4b5563;
}
/* ダークモード用の値 */
.dark {
--color-bg-primary: #111827;
--color-bg-secondary: #1f2937;
--color-text-primary: #ffffff;
--color-text-secondary: #d1d5db;
}
<div class="bg-(--color-bg-primary) text-(--color-text-primary)">
CSS 変数を使用
</div>
実践例
ヘッダー
<header class="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<nav class="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between">
<a href="/" class="font-bold text-xl text-gray-900 dark:text-white">
Logo
</a>
<div class="flex items-center gap-4">
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
About
</a>
<!-- テーマ切り替えボタン -->
<button class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700">
<span class="dark:hidden">🌙</span>
<span class="hidden dark:inline">☀️</span>
</button>
</div>
</nav>
</header>
カード
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg dark:shadow-gray-900/50 overflow-hidden">
<img src="..." class="w-full h-48 object-cover" />
<div class="p-6">
<span class="text-sm text-blue-600 dark:text-blue-400 font-semibold">
カテゴリ
</span>
<h3 class="mt-2 text-xl font-bold text-gray-900 dark:text-white">
カードタイトル
</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300">
説明文がここに入ります...
</p>
<button class="mt-4 px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
詳細を見る
</button>
</div>
</div>
フォーム
<form class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg">
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-6">
お問い合わせ
</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
名前
</label>
<input
type="text"
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-700 text-gray-900 dark:text-white
focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
メッセージ
</label>
<textarea
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-700 text-gray-900 dark:text-white
focus:ring-2 focus:ring-blue-500 focus:border-transparent"
rows="4"
></textarea>
</div>
<button class="w-full py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
送信
</button>
</div>
</form>
テーブル
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
名前
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
ステータス
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
<td class="px-6 py-4 text-gray-900 dark:text-white">田中太郎</td>
<td class="px-6 py-4">
<span class="px-2 py-1 text-xs rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
アクティブ
</span>
</td>
</tr>
</tbody>
</table>
</div>
モーダル
<!-- オーバーレイ -->
<div class="fixed inset-0 bg-black/50 dark:bg-black/70 flex items-center justify-center">
<!-- モーダル -->
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-lg w-full mx-4 p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-900 dark:text-white">
モーダルタイトル
</h2>
<button class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
✕
</button>
</div>
<p class="text-gray-600 dark:text-gray-300">
モーダルの内容...
</p>
<div class="mt-6 flex justify-end gap-3">
<button class="px-4 py-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg">
キャンセル
</button>
<button class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
確認
</button>
</div>
</div>
</div>
ダークモードのベストプラクティス
1. コントラストを意識する
<!-- Good: 十分なコントラスト -->
<p class="text-gray-900 dark:text-white">メインテキスト</p>
<!-- Avoid: 低コントラスト -->
<p class="text-gray-500 dark:text-gray-600">読みにくいテキスト</p>
2. 純粋な白黒を避ける
<!-- Good: ソフトな色 -->
<div class="bg-gray-50 dark:bg-gray-900">...</div>
<!-- Avoid: 純粋な白黒 -->
<div class="bg-white dark:bg-black">...</div>
3. シャドウを調整する
<!-- ダークモードではシャドウを濃く -->
<div class="shadow-lg dark:shadow-gray-900/50">...</div>
4. 画像の明るさを調整
<img src="..." class="dark:brightness-90" />
まとめ
dark:バリアントでダークモードスタイルを指定- メディアクエリ方式とクラス方式を選択可能
- カラーパレットを適切にマッピング
- コントラストと可読性を意識
- CSS 変数でテーマカラーを一元管理可能
次に読む
- コンテナクエリ — コンポーネント自己完結のレスポンシブ
- カスタマイズ —
@themeで色を独自定義する - Tailwind CSS ガイド 目次 — 全 18 章の構成に戻る