できない.dev

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 のどちらかに統一する。

よくある原因

  1. 既定 media モード: darkMode を書かないと OS の prefers-color-scheme に追従するだけで、手動トグルは反応しない。
  2. dark クラス未付与: darkMode: 'class' にしても、<html class="dark"> のように親要素にクラスを付けない限り dark:* ユーティリティは発火しない。
  3. v3 / v4 混在: Tailwind v4 は CSS-first 設定に移行しており、tailwind.config.jsdarkMode ではなく CSS の @variant dark で定義する。
    v4 で v3 設定を書いても無視される。
  4. 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 も参照)。

この記事は役立ちましたか?