🌗

WebサイトにJavaScriptでダークモード切り替えるボタンを実装する

2021/08/21に公開

JavaScript でダークモードを実装したい

ダークモードに切り替えるボタンを作成しようと思い、記事や動画などをあさりましたが、理想とする実装方法が見つかりませんでした。
根気よくあさっていたら良さそうな記事を見つけました。
海外の Abbey Perini さんが紹介している実装方法がめちゃくちゃ参考になりました。

https://dev.to/abbeyperini/toggle-dark-mode-in-react-28c9

以下の項目に該当する人におすすめです。

  1. ユーザーの OS のテーマを判定して、その内容をもとにサイトのテーマを適応したい人
  2. CSS の変数(カスタムプロパティ)を使ってテーマを管理したい人
  3. React や Vue でも使いたい人
  4. JQuery を使わず実装したい人

実行したい処理

👇 は実装するダークモードのイメージです。

if(/* 初回アクセスの場合 */) {
  // ユーザーのOSテーマを元にサイトのテーマを適応
} else {
  // セッションストレージの情報を元にテーマを適応
}

ライトモードとダークモードの CSS をかく

ライトモードとダークモードように CSS のクラスを用意します。ここでは、is-theme-lightis-theme-darkとしておきます。

メンテナンスしやすいようにカラーは変数で管理します。

:root,
.is-theme-light {
  --base-color: white;
  --primary-color: skyblue;
  --secondary-color: mistyrose;
  --font-color: black;
}
.is-theme-dark {
  --base-color: black;
  --primary-color: lightskyblue;
  --secondary-color: lavenderblush;
  --font-color: whitesmoke;
}
body {
  color: var(--font-color);
  background var(--base-color);
}

テーマを適応する

<html>タグに CSS のクラスを追加する処理を書きます。<html>タグは、documentElement を使用してを表現します。

次に、セッションストレージにテーマを保存する機能を作成します。sessionStoragesetItem()メソッドを使います。setItem()第一引数の key を、第二引数は key に渡す値を渡します。key の名前はthemeとしておきます。

これらをまとめて一つの関数にします。

const applyTheme = (themeName) => {
  sessionStorage.setItem('theme', themeName);
  document.documentElement.className = themeName;
};

applyTheme()に渡された引数(クラス名)が、<html>タグのクラスに追加され、sessionStorageの keythemeの値に渡されるようになります。

初回セッションかどうかを判定する

sessionStorageに keystatusを作成して、滞在時に key 値を渡す処理を書きます。

初期値に空文字を設定しておきます。

const applyVisited = (status) => {
  status = '';
  sessionStorage.setItem('status', status);
};

ユーザーの OS テーマを元にサイトのテーマを適応する

これは、初回アクセス時に実行する処理です。

matchMedia()メソッドを使ってユーザーの OS テーマを判別する処理を書いていきます。matchMedia()の引数に(prefers-color-scheme: dark)を渡す事で、ユーザーの OS テーマがダークモードであるかどうかを判別できます。

const prefersColorSchemeDark = matchMedia(
  '(prefers-color-scheme: dark)'
).matches;

条件分岐と先ほど作った関数applyTheme()を使って、初回アクセス時に実行する処理を書きます。OS テーマがダークモードの場合はis-theme-darkを、そうでない場合はis-theme-lightapplyTheme()の引数に渡します。

const initialTheme = () => {
  const prefersColorSchemeDark = matchMedia(
    '(prefers-color-scheme: dark)'
  ).matches;
  if (prefersColorSchemeDark) {
    applyTheme('is-theme-dark');
  } else {
    applyTheme('is-theme-light');
  }
};

セッションストレージからテーマの情報を取得する

これは、初回アクセス以外、ページ遷移や再読み込みが発生した時に実行します。getItem()メソッドを使ってセッションストレージに keythemeに保存されている情報を取得します。

const storageTheme = sessionStorage.getItem('theme');

条件分岐とapplyTheme()を使って適応するテーマの処理をおこないます。

export const getStorageTheme = () => {
  const storageTheme = sessionStorage.getItem('theme');
  if (storageTheme === 'is-theme-dark') {
    applyTheme('is-theme-dark');
  } else if (storageTheme === 'is-theme-light') {
    applyTheme('is-theme-light');
  }
};

状況に応じてテーマを適応する

準備が整いましたので、冒頭で述べた 👇 の処理を書いていきます。

if(/* 初回アクセスの場合 */) {
  // ユーザーのOSテーマを元にサイトのテーマを適応
} else {
  // セッションストレージの情報を元にテーマを適応
}

実際にかく処理は少々異なります。

if(/* 初回アクセスじゃない場合 */) {
  // 初回アクセス以外、ページ遷移や再読み込みが発生した時に実行する処
  // セッションストレージの情報を元にテーマを適応
} else {
  // 初回アクセス時に実行する処理
  // ユーザーのOSテーマを元にサイトのテーマを適応
}

まとめると 👇 のようになります。

const discriminationTheme = () => {
  const getStrageVisited = sessionStorage.getItem('status');
  if (getStrageVisited) {
    getStorageTheme();
  } else {
    initialTheme();
    applyVisited('visted');
  }
};

セッションストレージの keystatusの情報を取得して、情報があればgetStorageTheme()を実行、そうでなければ、initialTheme()applyVisited()を実行します。applyVisited()には引数としてvisitedを渡します。

ボタンクリックでテーマを切り替える

最後にボタンをクリックした時にテーマを切り替える処理を書いていきます。

const switchTheme = () => {
  const storageTheme = sessionStorage.getItem('theme');
  if (storageTheme !== 'is-theme-dark') {
    applyTheme('is-theme-dark');
  } else {
    applyTheme('is-theme-light');
  }
};

全ての処理をまとめると 👇 のようになります。

theme.js
const applyVisited = (status) => {
  status = "";
  sessionStorage.setItem("status", status);
};
const applyTheme = (themeName) => {
  sessionStorage.setItem("theme", themeName);
  document.documentElement.className = themeName;
};
const initialTheme = () => {
  const prefersColorSchemeDark = matchMedia(
    "(prefers-color-scheme: dark)"
  ).matches;
  if (prefersColorSchemeDark) {
    applyTheme("is-theme-dark");
  } else {
    applyTheme("is-theme-light");
  }
};
const getStorageTheme = () => {
  const storageTheme = sessionStorage.getItem("theme");
  if (storageTheme === "is-theme-dark") {
    applyTheme("is-theme-dark");
  } else if (storageTheme === "is-theme-light") {
    applyTheme("is-theme-light");
  }
};
const switchTheme = () => {
  const storageTheme = sessionStorage.getItem("theme");
  if (storageTheme !== "is-theme-dark") {
    applyTheme("is-theme-dark");
  } else {
    applyTheme("is-theme-light");
  }
};
const discriminationTheme = () => {
  const getStrageVisited = sessionStorage.getItem("status");
  if (getStrageVisited) {
    getStorageTheme();
  } else {
    initialTheme();
    applyVisited("visted");
  }
};

theme.js として保存して、HTML ファイルに読み込みましょう。

<button>タグのonclick属性にswitchTheme()を渡せば、テーマ切り替えボタンの完成です。

<script src="theme.js"></script>
<button onclick="switchTheme()"></button>

React で使うには

React で使うには、switchThemediscriminationThemeexportして、switchThemeonClick属性に、discriminationThemeは App.js などのファイル内のuseEffectの中に記述していただければ OK です!
ただ、React ではuseStateに置き換えらそうなコードがいくつかあるので、今度 React バージョンも公開できればと思います。

GitHubで編集を提案

Discussion