Tailwind CSSでダークモード対応をする

Tailwind CSSでダークモード対応をする
公開日時

dark: 付きの色クラスを指定するとダークモード時に指定の色に切り替えてくれる。

ダークモード自体を有効にするには、 tailwind.config.jsdarkModeclassmedia を指定する。

media の場合はOSの設定に基づいて自動でdarkモードに切り替えてくれるので、tailwindcssのみで完結する。

ただし、ユーザは手動でダークモードのON/OFFを切り替えることができない。

class の場合は、jsでhtmlタグのclassに dark を付与/削除する実装が必要になるが、ユーザがダークモードのON/OFFを切り替えることができるようになる。

<html class="dark">

今回はユーザがダークモードのON/OFFを切り替えられるように class を指定することにした。

tailwind.config.jsの設定

// tailwind.config.js
module.exports = {
  purge: ['./src/**/*.{js,ts,jsx,tsx}'],
  darkMode: 'class'
}

Reactでの実装方法

  • ダークモード切り替え用のトグルボタンコンポーネントを作る

Toggling dark mode manually」を参考に現在のモードをlocalStorageに保存するようにする。

また、アイコン表示にはTailwind作者の方が最近リリースしていた「Heroicons」を利用した。

jsx形式でコピーできるので手軽に使える。

トグルボタンは「Toggle switch without JS by dirkolbrich」を参考にさせていただいた。

// components/ToggleDarkMode.tsx
import React, { useEffect, useState } from 'react'

export const ToggleDarkMode = () => {
  const [darkMode, setDarkMode] = useState(false)
  useEffect(() => {
    if (
      localStorage.theme === 'dark' ||
      (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
    ) {
      setDarkMode(true)
      document.querySelector('html')?.classList.add('dark')
    } else {
      setDarkMode(false)
      document.querySelector('html')?.classList.remove('dark')
    }
  }, [darkMode])

  const handleChangeDarkMode = () => {
    if (darkMode) {
      localStorage.theme = 'light'
      setDarkMode(false)
    } else {
      localStorage.theme = 'dark'
      setDarkMode(true)
    }
  }

  return (
    <div className="flex">
      <div className="mr-2 text-xs">
        <svg
          className="w-6 h-6"
          xmlns="http://www.w3.org/2000/svg"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
            d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
          />
        </svg>
      </div>
      <div className="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
        <input
          type="checkbox"
          name="toggle"
          id="toggle"
          checked={darkMode}
          className="toggle-checkbox"
          onChange={handleChangeDarkMode}
        />
        <label htmlFor="toggle" className="toggle-label">
          toggle
        </label>
      </div>
      <div className="text-xs">
        <svg
          className="w-6 h-6"
          xmlns="http://www.w3.org/2000/svg"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
            d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
          />
        </svg>
      </div>
    </div>
  )
}
  • トグルボタン用のカスタムcss classを追加
/* styles/index.css */

@tailwind base;

/* Start purging... */
@tailwind components;
/* Stop purging. */

.toggle-checkbox {
  @apply absolute block w-6 h-6 dark:bg-white border-4 dark:border-gray-400 rounded-full appearance-none cursor-pointer focus:outline-none bg-gray-800 border-gray-100;
}

.toggle-checkbox:checked {
  @apply right-0 border-gray-400;
}

.toggle-label {
  @apply block h-6 align-middle overflow-hidden text-gray-100 bg-gray-100 rounded-full cursor-pointer dark:text-gray-400 dark:bg-gray-400 dark:border-gray-400 border-gray-100 border-2;
}

/* Start purging... *
@tailwind utilities;
/* Stop purging. */
  • 最後にAppコンポーネントでToggleDarkModeコンポーネントを呼び出すようにする
// App.tsx
import React, { useEffect, useState } from 'react'
import { ToggleDarkMode } from './components/ToggleDarkMode'

function App() {
  return (
    <div className="min-h-screen dark:bg-gray-800 dark:text-gray-200">
      <main>
        <div className="flex justify-center">
          <ToggleDarkMode />
        </div>
      </main>
    </div>
  )
}

dark mode dark

これでダークモード対応ができた。

参考


Related #css

Youtube動画で学ぶTailwind CSS

「Designing with Tailwind CSS」シリーズがとても分かりやすかった。

tailblocks

tailwind cssのテンプレート集

Next.js 10 + Tailwind CSS 2.0 でAMP対応(hybrid)を行う

next/ampのhybridモードを使い通常ページとAMPページを生成し、PostCSSでCSSの容量削減を行いinline cssとして埋め込み

「Tailwind CSS Tips, Tricks & Best Practices」を観て学ぶ

動画を観ながら実際に手を動かして13個の練習問題を解いた