Skip to content

CSSメディアクエリを使用したダークモードの実装方法

PCやスマートフォンなどのデバイスの外観の設定でダークモード(またはダークテーマ)を有効にすると、ユーザーインターフェースや対応されているWebサイトが暗い色で表示されます。

このダークモードはCSSのカスタムプロパティ(Custom Properties)メディアクエリ(media queries)を使用することで簡単に実装することができます。

デバイスの設定に応じてWebサイトのテーマを切り替える方法に加えて、Google FontsのようにWebサイト独自でライトモード/ダークモードの切り替えを可能にするボタンの設置方法を紹介します。

デバイスの設定に応じてWebサイトのテーマを切り替える

CSSのカスタムプロパティ(Custom Properties)とSCSSの @mixin を使用してライトモード/ダークモードそれぞれの場合のカラーコードを設定します。

@mixin light-theme {
  --color-primary: #bae8e8;
  --color-secondary: #e3f6f5;
  --color-text: #2d334a;
  --color-surface: #e3f6f5;
  --color-stroke: ##272343;
  --color-bg: #fffffe;
}

@mixin dark-theme {
  --color-primary: #bae8e8;
  --color-secondary: #72757e;
  --color-text: #fffffe;
  --color-surface: #121212;
  --color-stroke: #010101;
  --color-bg: #242629;
}

デバイスの設定に応じたCSSの切り替えには メディアクエリ(media queries)prefers-color-scheme を使用します。 ( prefers-color-scheme のブラウザ対応状況はこちらです。)

@media (prefers-color-scheme: dark) {
  /* ダークモード(ダークテーマ)を有効にしている場合に反映されます */
}

@media (prefers-color-scheme: light) {
  /* ライトモードを有効にしている場合または設定がデフォルトの場合に反映されます */
}

先ほど作成した @mixin@include で呼び出します。

@media (prefers-color-scheme: dark) {
  :root {
    @include dark-theme;
  }
}

@media (prefers-color-scheme: light) {
  :root {
    @include light-theme;
  }
}

デモ

デバイスの外観の設定が反映されるようになりました。

デモ(CodePen)

Webサイト独自のダークモード切り替えボタンを設置する

各テーマのCSSの設定はデバイスの設定に応じてWebサイトのテーマを切り替えると同じですが、 @include での呼び出し場所が変わります。 body タグの class 名によってライトテーマ/ダークテーマが切り替わるようにします。

.light {
  @include light-theme;
}

.dark {
  @include dark-theme;
}

htmlに切り替え用のボタンを設置します。
JavaScriptで切り替えの処理を行うためにボタンに id="toggleTheme" を付与しています。

<button id="toggleTheme"><span>Dark</span> Mode</button>

JavaScriptで切り替えの処理を記述していきます。
デフォルトではライトテーマが設定されている( <body class="light"></body> )ことを前提としています。

切り替えボタンとテキストを変数として宣言します。

const btn = document.getElementById("toggleTheme");
const btnText = document.querySelector("#toggleTheme span");
let theme;

クリックしたときに実行する処理を toggleTheme として宣言します。
ボタンをクリックするたびに宣言した処理を実行したいので、 addEventListenerclick イベントを指定します。

const toggleTheme = () => {
  // ここにクリックした時の処理を記述する
};

btn.addEventListener("click", () => {
  toggleTheme();
});

toggleTheme の処理は、実行される度にbodyタグのclass名が light <-> dark となるようにします。
class名は element.classList.replace('現在のclass名', '変更後のclass名') で付け替えます。

bodyタグのclass名を element.classList.contains('class名') で取得し、現在のclass名に応じて付け替えを行います。
ボタン名は element.innerHTML で書き換えます。

const toggleTheme = () => {
  if (document.body.classList.contains("light")) {
    document.body.classList.replace("light", "dark");
    btnText.innerHTML = "Light";
  } else {
    document.body.classList.replace("dark", "light");
    btnText.innerHTML = "Dark";
  }
};

ボタンをクリックする度にテーマが切り替わるようになりましたが、ブラウザを更新したりサイトを離れてしまうとデフォルトテーマに戻ってしまいます。
localStorage を使用してユーザーが選択したテーマをWebサイトに覚えさせておくようにします。

適用するテーマを保存するための変数を宣言します。

let theme;

localStorage に保存されているテーマを localStorage.getItem('名前') で取得し currentTheme として宣言します。
保存されているテーマがない場合はデフォルトテーマ(light)が現在のテーマとして宣言されるようにします。

const currentTheme = localStorage.getItem("theme") || "light";

Webサイトが読み込まれた際の最初の見え方(表示テーマ)を、 currentTheme に応じてbodyタグのclass名およびボタン名を切り替えて設定します。

if (currentTheme === "dark") {
  document.body.classList.add("dark");
  btnText.innerHTML = "Light";
} else if (currentTheme === "light") {
  document.body.classList.add("light");
  btnText.innerHTML = "Dark";
}

ボタンがクリックされる度に最初に宣言した theme の値が light <-> dark となるようにし、 localStoragelocalStorage.setItem('名前', 値) で保存します。

const toggleTheme = () => {
  if (document.body.classList.contains("light")) {
    document.body.classList.replace("light", "dark");
    theme = "dark";
    btnText.innerHTML = "Light";
  } else {
    document.body.classList.replace("dark", "light");
    theme = "light";
    btnText.innerHTML = "Dark";
  }
  localStorage.setItem("theme", theme);
};

デモ

デモ(CodePen)

デバイスの設定とWebサイト独自の切り替えの両方に対応する

デバイスはダークモード(ダークテーマ)を有効にしていても、Webサイトはライトテーマで見たいという場合もあるかもしれません。
紹介した2つの方法を組み合わせることで、Webサイトを最初に訪問した際はデバイスの設定に準じたテーマで表示され、ボタンによってユーザーが任意でテーマを切り替えることができるようになります。

デバイスの外観の設定をJavaScriptで取得するには window.matchMedia('(prefers-color-scheme: dark)') を使用します。
デバイスの設定がダークモード(ダークテーマ)になっている場合は true を返します。
この値を prefersDarkTheme として宣言します。

Webサイト訪問時に使用するテーマを判定する箇所に追記します。

if (currentTheme === "dark" || prefersDarkTheme.matches) {
  document.body.classList.add("dark");
  btnText.innerHTML = "Light";
} else if (currentTheme === "light" || !prefersDarkTheme.matches) {
  document.body.classList.add("light");
  btnText.innerHTML = "Dark";
}

デモ

デモ(CodePen)

Reference 参照