🌛

TailwindCSSについて自分なりのチートシートを作る。#3 Dark Mode 編

に公開

初めに

TailwindCSSを使うときに、思うように動かせなかったりすることが多いなと個人的に感じていて、なので一度全体的にまとめておこうと思いこの記事を書いています。

第一弾目は、疑似クラスについてまとめました。
https://zenn.dev/daichi09167/articles/da920ca4edc0eb

第二弾目は、レスポンシブデザインについてまとめました。
https://zenn.dev/daichi09167/articles/805ea42f18d182

本記事では、ダークモードの実装についてまとめていきたいと思います。
Tailwind CSSのダークモードの実装方法は、主に以下の二つで実装可能です。

  • メディアクエリ方式
    • ユーザーのOS設定に基づいて自動的にダークモードを適用する方法です。
  • クラス式
    • ユーザーがトグルボタン等を利用して、ダークモードを適用する方法です。

今回は、React + TypeScript + Vite + TailwindCSSで実装していきます。

メディアクエリ方式について

メディアクエリ式は、dark: を前につけることで実装できます。

index.css
@import "tailwindcss";
App.tsx
interface CardData {
  icon: string;
  title: string;
  description: string;
}

const Home = () => {
  const cardsData: CardData[] = [ // ここで CardData 型を適用
    {
      icon: "📘",
      title: "1番目のカード",
      description: "これはカードの内容です。",
    },
    {
      icon: "📗",
      title: "2番目のカード",
      description: "これはカードの内容です。",
    },
    {
      icon: "⭐",
      title: "3番目のカード",
      description: "これはカードの内容です",
    },
    {
      icon: "💡",
      title: "4番目のカード",
      description: "これはカードの内容です",
    },
    {
      icon: "🚀",
      title: "5番目のカード",
      description: "これはカードの内容です",
    },
  ];

  return (
    <div className="w-screen min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 p-6">
      <h1 className="text-3xl font-bold mb-6 text-center">
        ようこそ、ダークモードへ!
      </h1>

      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 justify-items-center">
        {cardsData.map((card, index) => (
          <div
            key={index}
            className="w-80 bg-white dark:bg-gray-800 rounded-xl shadow-md border border-gray-200 dark:border-gray-700 p-6"
          >
            <h2 className="text-xl font-bold mb-2 flex items-center">
              <span className="mr-2">{card.icon}</span> {card.title}
            </h2>
            <p className="text-gray-700 dark:text-gray-300">
              {card.description}
            </p>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Home;

解説
<div className="w-screen min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 p-6">
今回は上記の部分を例に解説します。

クラス名 意味・効果
w-screen 幅を画面いっぱい(100vw)に広げる。
min-h-screen 高さを画面の高さ(100vh)に最低限設定します。中身が少なくても、画面いっぱいに広がる。
bg-gray-100 背景色を明るいグレーに設定(ライトモード用)。
dark:bg-gray-900 ダークモード時には背景色を暗いグレーに設定。
text-gray-900 テキスト色をほぼ黒に設定(ライトモード用)。
dark:text-gray-100 ダークモード時にはテキスト色を明るいグレーにします。
p-6 全体に 1.5rem のパディングを与えます。

実行結果

  • ライトモード時
  • ダークモード時

クラス式

index.css
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
App.tsx
'use client'

import { useEffect, useState } from 'react'

function Page() {
  const [isDark, setIsDark] = useState(false)

  useEffect(() => {
    const root = document.documentElement
    if (isDark) {
      root.classList.add('dark')
    } else {
      root.classList.remove('dark')
    }
  }, [isDark])

  return (
    <div className="w-screen min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 p-6 transition-colors">
      <button
        onClick={() => setIsDark(!isDark)}
        className="mb-4 px-4 py-2 rounded bg-gray-200 dark:bg-gray-700 dark:text-white"
      >
        {isDark ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
      </button>

      <h1 className="text-2xl font-bold">ようこそ</h1>
      <p className="mt-2">ユーザーがボタンを利用して、ダークモードを適用する方法</p>
    </div>
  )
}

export default Page

解説
一部抜粋しながら解説していきます。

useEffect(() => {
  const root = document.documentElement
  if (isDark) {
    root.classList.add('dark')
  } else {
    root.classList.remove('dark')
  }
}, [isDark])
  • isDark の値が変わるたびに useEffect が発火。
  • document.documentElement は <html> 要素。
  • dark クラスを <html> に追加・削除。
  • 全体的なまとめとして、ユーザーがボタンをクリックすると、isDark の状態が true/false で切り替わり、それに応じて HTML の <html> 要素に dark クラスを付けたり外したりする実装です。

実行結果

  • ライトモード時
  • ダークモード時

Discussion