—Pngtree—halo moon_5336581.png

Tailwind CSS のダークモードで System, Light, Dark を切り替える

ダークモードの設定では、OS の設定と同期させるか、ライトモードまたはダークモードに手動で切り替えるかの 3 つの選択肢が用意されていることがあります。手動でライトモードとダークモードを切り替える場合に比べて、OS の設定を自動で反映できるメリットがあります。

ダークモードを設定できるサイトやアプリケーションでは、大きくわけて 3 つの方法が考えられます。

  • OS の設定に合わせてダークモードに切り替える
  • ユーザーが手動でライトモードまたはダークモードに切り替える
  • ユーザーが手動でライトモード、ダークモード、OS の設定に合わせるかを切り替える

OS の設定に合わせてダークモードに切り替える場合には、prefers-color-scheme という CSS メディアクエリを使用します。このメディアクエリは、ユーザーの OS の設定がダークモードになっている場合にのみスタイルが適用されます。ユーザーが明示的に設定を切り替えられない不便さはありますが、実装が簡単です。

ユーザーが手動でライトモードまたはダークモードに切り替える場合には、大抵の場合トグルボタンやスイッチの ON/OFF で切り替えられるようになっています。

Vue の公式サイトのヘッダーのスクリーンショット。ダークモードを切り替えるスイッチが ON になっている。

この場合には、初期設定では OS の設定に合わせてダークモードに切り替えるようにしておき、ユーザーが手動で切り替えた場合には LocalStorage などに保存しておき、次回からはその設定を読み込むようにします。

ダークモードの設定では、OS の設定と同期させるか、ライトモードまたはダークモードに手動で切り替えるかの 3 つの選択肢が用意されていることがあります。例えば、Tailwind CSS の公式サイトでは、SystemLightDark の 3 つを切り替えることができます。

Tailwind CSS の公式サイトのヘッダーのスクリーンショット。Light, Dark, System のドロップダウンメニューが表示されていて、System が選択されている。

手動でライトモードとダークモードを切り替える場合に比べて、System はどのようなメリットが有るのでしょうか?それは OS の設定を変更した時に自動で反映してくれることにあるでしょう。

例えば、Mac OS のダークモードの設定では時間帯によって自動で切り替わるように設定できます。このような場合には、ユーザーが手動でライトモードとダークモードを切り替えるよりも、OS の設定に合わせて自動で切り替わる方が便利です。

それでは、Tailwind CSS のダークモードで SystemLightDark の 3 値を切り替える方法を見ていきましょう。

Tailwind CSS の設定

Tailwind CSS でタークモードはデフォルトでは OS の設定に合わせて切り替わるようになっています。今回は手動でライトモードとダークモードを切り替えるように設定を変更していきます。その場合には class ストラテジーを使用することになります。class ストラテジーでは HTML の祖先に dark クラスが付与されている場合にのみダークモードのスタイルが適用されます。

class ストラテジーを使用する場合には tailwind.config.jsdarkModeclass を指定します。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'class',
  // ...
}

ダークモードの初期設定

class ストラテジーを使用する場合には、ページが読み込まれた際にダークモードで表示するかどうかを決定して、dark クラスを付与する必要があります。dark クラスは HTML のルート要素である <html> に付与することになるでしょう。

ダークモードで表示するかの決定は、以下のプロセスによって行われます。

  1. LocalStorage に theme が保存されていないまたは、themesystem の場合 OS の設定に合わせてダークモードで表示するかどうかを決定する
  2. LocalStorage に theme が保存されていて、themedark の場合はダークモードで表示す
  3. それ以外の場合にはライトモードで表示する
// LocalStorage に theme が保存されていない or theme が system の場合
if (!('theme' in localStorage) || localStorage.theme === 'system') {
  // OS の設定を読み取る
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    // OS の設定がダークモードの場合、<html> に dark クラスを付与する
    document.documentElement.classList.add('dark')
  }
  // LocalStorage に設定を保存する
  localStorage.setItem('theme', 'system')
} else if (localStorage.theme === 'dark') { // LocalStorage に theme が保存されていて、theme が dark の場合
  document.documentElement.classList.add('dark')
} else { // それ以外の場合
  document.documentElement.classList.remove('dark')
}

このスクリプトは CSS が読み込まれる前に実行される必要があります。さもなければ、ダークモードで表示する前に一瞬ライトモードで表示されてちらつく感じがします。一般的な React のアプリケーションでは index.html<head> タグ内の <style> タグの前に <script> タグを配置することで、CSS が読み込まれる前に実行されるようになります。

OS のダークモードの設定を読み取るには window.matchMedia を使用します。window.matchMediaメディアクエリーを引数に受け取り、MediaQueryList オブジェクトを返します。

MediaQueryList オブジェクトは matches プロパティを持っており、メディアクエリに合致するかどうかを表す boolean 値を返します。また、change イベントを監視することにより、メディアクエリの状態が変化した際にコールバック関数を実行できます。

window.matchMedia("(prefers-color-scheme: dark)").matchestrue を返す場合には OS の設定がダークモードであることを意味します。

OS の設定が変更された際に自動で反映する

ダークモードの設定として System が選択されている場合には、OS の設定が変更された際に自動でダークモードの設定が反映される必要があるでしょう。これは前述した MediaQueryList オブジェクトの change イベントを監視することにより実現できます。

window.matchMedia('(prefers-color-scheme: dark)') が返す値に add.addEventListener('change', callback) を呼び出すことで、OS の設定が変更された際に callback 関数が実行されるようになります。

// OS の設定が変更された際に実行されるコールバック関数
const mediaQueryLlistener = (e: MediaQueryListEvent) => {
  if (localStorage.theme === 'system') {
    if (e.matches) {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
  }
}
 
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', mediaQueryLlistener)

コールバック関数内では LocalStorage の値が System の場合のみ処理を実行します。明示的にライトモード、ダークモードを選択している場合には、OS の設定が変更されても反映されないようにするためです。

コールバック関数は引数で MediaQueryListEvent を受け取ります。MediaQueryListEvent オブジェクトは matches プロパティを持っており、メディアクエリに合致するかどうかを表す boolean 値を返します。

この値が true の場合には OS の設定がダークモードに変更されたことを意味しますから、<html>dark クラスを付与します。false の場合には反対に <html> から dark クラスを削除します。

これで、実際に OS の設定を変更するたびにダークモードの設定が自動で反映されることが確認できます。

ダークモードの切り替え

ダークモードの切り替えは LocalStorage の theme の値をの変更と <html> タグの dark クラスのトグルで実現できます。SystemLightDark の 3 値を選択して変更しますから、セレクトボックスで切り替えることが考えられます。

React で実装すると、以下のような例になるでしょう。

type Theme = 'system' | 'light' | 'dark'
 
const ThemeSwitcher: React.FC = () => {
  // LocalStorage に保存されている値を初期値として設定する
  const [theme, setTheme] = useState<Theme>(localStorage.theme || 'system')
 
  const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const value = e.target.value as Theme
    setTheme(value)
    localStorage.setItem('theme', value)
    if (value === 'dark') {
      document.documentElement.classList.add('dark')
    } else if (value === 'light') {
      document.documentElement.classList.remove('dark')
    } else {
      // System が選択された場合は OS の設定を見て切り替える
      document.documentElement.classList.toggle('dark', window.matchMedia('(prefers-color-scheme: dark)').matches)
    }
  }
 
  return (
    <select value={theme} onChange={handleChange}>
      <option value="system">System</option>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
    </select>
  )
}

Contributors

> GitHub で修正を提案する
この記事をシェアする
はてなブックマークに追加

関連記事