Tailwind CSS の dark モードが切り替わらない/適用されない
`darkMode` の設定値(`media` / `class` / `selector`)と HTML 側のトグル方法がずれているのが大半の原因。
Tailwind v4 は CSS-first 設定(`@variant dark`)に変わったため、バージョン確認も同時に行う。
#tailwindcss#dark-mode#theme#jit#config
公開: 更新:
要約
Tailwind の dark モードが効かないとき、原因はほぼ次の 4 つに収まる: 既定の media モードで OS 追従になっている、dark クラスを HTML に付与し忘れている、v3 と v4 で設定方法が違うのに混在している、dark: プレフィックスが JIT スキャナで検出されていない。
バージョンを確認したうえで設定を v3 / v4 のどちらかに統一する。
よくある原因
- 既定
mediaモード:darkModeを書かないと OS のprefers-color-schemeに追従するだけで、手動トグルは反応しない。 darkクラス未付与:darkMode: 'class'にしても、<html class="dark">のように親要素にクラスを付けない限りdark:*ユーティリティは発火しない。- v3 / v4 混在: Tailwind v4 は CSS-first 設定に移行しており、
tailwind.config.jsのdarkModeではなく CSS の@variant darkで定義する。
v4 で v3 設定を書いても無視される。 - JIT 検出漏れ:
dark:bg-{color}を変数補完で組み立てると完全クラス名がソース上に出てこず、出力 CSS に含まれない。
解決策
1. v3 / v4 で設定を書き分ける
// Tailwind v3: tailwind.config.js
module.exports = {
content: ["./src/**/*.{ts,tsx,html}"],
darkMode: "class",
};/* Tailwind v4: src/app.css */
@import "tailwindcss";
@variant dark (.dark &);Dark Mode 公式ドキュメント のとおり、v3 は JS 側、v4 は CSS 側で宣言する。
バージョンは npx tailwindcss --help の先頭行で確認できる。
2. ルート要素にクラスを付与する
// app/providers.tsx
"use client";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const [dark, setDark] = useState(false);
useEffect(() => {
document.documentElement.classList.toggle("dark", dark);
localStorage.setItem("theme", dark ? "dark" : "light");
}, [dark]);
return <button onClick={() => setDark((v) => !v)}>toggle</button>;
}SSR の初期表示でフラッシュを避けたければ、<head> 内のインライン script で localStorage を読んで先に dark クラスを当てる定番パターンを使う。
3. 完全クラス名で書く
// NG: スキャンで検出されない
<div className={`dark:bg-${color}-900`} />
// OK: 完全文字列を分岐
<div className={dark ? "dark:bg-blue-900" : "dark:bg-red-900"} />dark: プレフィックス付きでも JIT の規則は同じで、完全文字列がソース上に存在する必要がある。content の glob に該当ファイルが含まれていることも合わせて確認する(Upgrade Guide も参照)。