Engineering

Tailwind CSSでカラーテーマの切り替えを簡単に実装する

  • Tailwind CSS
  • React
  • CSS
  • CSS変数
  • CSSカスタムプロパティ
  • カラーテーマ
  • ダークモード

data-theme 属性を切り替えるだけで、ページ全体のカラーテーマを変更できます。CSSカスタムプロパティとTailwind CSSを組み合わせた実装方法を紹介します。ReactとTailwind CSSでの実装例を示しますが、他のフレームワークでも同じアプローチが使えます。

CodePenTailwind CSS Color Theme DemoCSS カスタムプロパティと Tailwind CSS を使ったカラーテーマ切り替えデモです。

CSSカスタムプロパティで管理するメリット

CSS変数でテーマを管理する利点は、変更の影響範囲をCSS側に閉じ込められる点です。

  • JavaScriptからの操作が setAttribute の1行で完了する
  • 色の変更がCSS変数の定義箇所だけで完結し、コンポーネントに波及しない
  • ブラウザーの開発者ツールでCSS変数の値をリアルタイムに変更してデバッグできる

新しいテーマの追加も :root[data-theme="新テーマ名"] にCSS変数を定義するだけで済みます。コンポーネントのスタイリングコードを触る必要はありません。

CSSカスタムプロパティでテーマを定義する

テーマの色をCSSカスタムプロパティ(変数)として定義します。:root にデフォルトテーマ、data-theme 属性付きのセレクターに別テーマの値を設定します。

:root { /* デフォルトテーマ */
  --background-color: #fcfcfc;
  --foreground-color: #333;

  --primary-color-50: #fff1f3;
  --primary-color-100: #ffdfe4;
  /* ... 中間の色は省略 ... */
  --primary-color-900: #891325;
  --primary-color-950: #4b040f;

  --secondary-color-50: #eefffb;
  --secondary-color-100: #c4fff3;
  /* ... 中間の色は省略 ... */
  --secondary-color-900: #095850;
  --secondary-color-950: #003634;
}

ダークテーマなどの別テーマも同じ変数名で定義します。

:root[data-theme="dark"] {
  --background-color: #333;
  --foreground-color: #f5f5f5;

  --primary-color-50: #feffe7;
  /* ... 色定義 ... */

  --secondary-color-50: #edfffc;
  /* ... 色定義 ... */
}

data-theme 属性の値を切り替えるだけで、全ページの色が一括で変わります。

カラーパレットを設計する

50〜950のカラーパレットを作成する場合、以下のツールが便利です。

Tailwind CSSを使う場合、Tailwind CSS Color Generatorは必須です。ベースカラーを1つ指定するだけで、50〜950のパレット全体を生成してくれます。

Tailwind CSSの設定

TailwindのconfigでCSS変数を参照するように記述します。

tailwind.config = {
  theme: {
    extend: {
      colors: {
        background: "var(--background-color)",
        foreground: "var(--foreground-color)",

        primary: {
          "50": "var(--primary-color-50)",
          "100": "var(--primary-color-100)",
          // ... 中間の色は省略 ...
          "900": "var(--primary-color-900)",
          "950": "var(--primary-color-950)",
        },

        secondary: {
          "50": "var(--secondary-color-50)",
          "100": "var(--secondary-color-100)",
          // ... 中間の色は省略 ...
          "900": "var(--secondary-color-900)",
          "950": "var(--secondary-color-950)",
        },
      },
    },
  },
};

この設定により、bg-primary-500text-secondary-300 といったTailwindのユーティリティクラスがCSS変数の値を参照するようになります。テーマごとにクラス名を変更する必要はありません。

テーマ切り替えの実装

テーマの切り替えは、data-theme 属性の書き換えだけで完了します。

type ColorScheme = "default" | "dark" | "custom";

const App = () => {
  const [colorScheme, setColorScheme] = React.useState<ColorScheme>("default");

  React.useEffect(() => {
    document.documentElement.setAttribute("data-theme", colorScheme);
  }, [colorScheme]);

  return (
    <div className="min-h-screen bg-background text-foreground">
      <div className="flex items-center justify-center">
        <label htmlFor="color-scheme" className="text-lg font-bold mb-2 mr-4">
          Color Scheme
        </label>
        <select
          id="color-scheme"
          value={colorScheme}
          onChange={(e) => setColorScheme(e.target.value as ColorScheme)}
          className="p-2 border border-gray-300 rounded-md"
        >
          <option value="default">Default</option>
          <option value="dark">Dark</option>
          <option value="custom">Custom</option>
        </select>
      </div>

      <div className="mt-8">
        <button className="px-4 py-2 bg-primary-500 text-white rounded-md">
          Primary Button
        </button>
        <button className="ml-4 px-4 py-2 bg-secondary-500 text-white rounded-md">
          Secondary Button
        </button>
      </div>
    </div>
  );
};

colorScheme の状態が変わると useEffectdata-theme 属性を更新し、CSS変数の値が切り替わります。コンポーネント側のクラス名は一切変更する必要がありません。

動作確認は冒頭のCodepenで試せます。

CodePenTailwind CSS Color Theme DemoCSS カスタムプロパティと Tailwind CSS を使ったカラーテーマ切り替えデモです。