Tailwind CSSでダークモード対応をする
dark:
付きの色クラスを指定するとダークモード時に指定の色に切り替えてくれる。
ダークモード自体を有効にするには、 tailwind.config.js
の darkMode
に class
か media
を指定する。
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>
)
}
これでダークモード対応ができた。