メインコンテンツまでスキップ

ダークモード

この章では、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>

推奨カラーマッピング

用途ライトダーク
背景 (ベース)whitegray-900
背景 (セカンダリ)gray-50gray-800
背景 (三次)gray-100gray-700
テキスト (メイン)gray-900white
テキスト (セカンダリ)gray-600gray-300
テキスト (補足)gray-400gray-500
ボーダーgray-200gray-700
ホバー背景gray-100gray-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 変数でテーマカラーを一元管理可能

次に読む