【CSS】CSSカスタムプロパティとJavaScriptを使ってモード切替(配色変更)を実装

はじめに

便利なCSSカスタムプロパティ。実はJavaScriptから取得/設定ができます。今回はCSSカスタムプロパティとJavaScriptを使ってモード切替(配色変更)を実装する方法を解説します。

CSSカスタムプロパティ

CSSカスタムプロパティとはCSS内で使用できる変数のことです。

Webデザインの配色を考えるとき、たくさんの色を使うことは滅多にありません。大抵は白色+黒色+テーマカラー(1〜2色)に落ち着くことと思います。

テーマカラーはWeb全体のあちこちに配色されることになります。見出しの文字色だったり、ボックスの背景色だったり、表の罫線だったり、というように。

// テーマカラーが #417ece の場合

h1 {
  color: #417ece;
}
.box {
  background-color: #417ece;
}
.cell {
  border-bottom: 1px solid #417ece;
}

このように分散して書いていると、いざテーマカラーを変更したいとなったときに修正が非常に面倒です。一括置換すれば楽ですが、対象が複数ファイルに及ぶとやはり面倒です。

そこで登場するのがCSSカスタムプロパティです。テーマカラーをCSSカスタムプロパティに入れてやることで面倒な修正作業から解放されます。

// テーマカラーが #417ece の場合

// CSSカスタムプロパティ
:root {
  --theme-color: #417ece;
}

h1 {
  color: var(--theme-color);
}
.box {
  background-color: var(--theme-color);
}
.cell {
  border-bottom: 1px solid var(--theme-color);
}

テーマカラーを変更したいときは一番上の--theme-colorを変更するだけで済みます。

ちなみに:rootというのは擬似クラスのひとつで、文書のルート要素、つまりhtmlタグのことを指します。CSSカスタムプロパティは:root内に書くというのが慣習になっています。

rgba、lighten、darkenとの併用

CSSカスタムプロパティとlightendarkenとの併用はできません。また、rgbaは16進数のCSSカスタムプロパティは使えなくて、RGB値のCSSカスタムプロパティであれば使えます。

:root {
  --theme-color: #417ece;

  // RBB値(「rgb(65, 126, 206)」という書き方はNG)
  --theme-color-rgb: 65, 126, 206;
}

.heading {  
  // ❌ lighten、darkenはCSSカスタムプロパティを使えない
  background-color: lighten(var(--theme-color), 50%);
  border-bottom: 1px solid darken(var(--theme-color), 50%);

  // ❌ 16進数のCSSカスタムプロパティは使えない
  color: rgba(var(--theme-color), .5);

  // ⭕ RGB値のCSSカスタムプロパティは使える
  color: rgba(var(--theme-color-rgb), .5);
}

サンプル

簡単なものですがサンプルを作ったので動きを確認してみてください。左上の「モード切替」をクリックするたびにライトモードとダークモードが切り替わります。

実装

では、さっそく実装していきます。まず初めにすべてのコードを記載します。CSSはSASSを使用、JavaScriptはjQueryを使用していますのでご注意ください。

HTML

<span id="mode-toggle">モード切替</span>
<div id="wrap">
  <h1>Sample Text</h1>
</div>

CSS

#mode-toggle {
  cursor: pointer;
  color: #000;
}
#wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
  color: var(--text-color);
  background-color: var(--bg-color);
  transition: color .5s, background-color .5s;
}

:root {
  --mode:light;
  --bg-color: var(--light-bg-color);
  --text-color: var(--light-text-color);

  // Light Mode
  --light-bg-color: #fff;
  --light-text-color: #000;

  // Dark Mode
  --dark-bg-color: #000;
  --dark-text-color: #fff;
}

JavaScript

const modeToggle = () => {
  $('#mode-toggle').on('click', function() {
    const html = document.querySelector('html');
    const currentMode = getComputedStyle(html).getPropertyValue("--mode");

    let mode = null, bgColor = null, textColor = null;

    switch (currentMode) {
      case 'light':
        mode             = 'dark';
        bgColor          = getComputedStyle(html).getPropertyValue("--dark-bg-color");
        textColor        = getComputedStyle(html).getPropertyValue("--dark-text-color");
        break;
      case 'dark':
        mode             = 'light';
        bgColor          = getComputedStyle(html).getPropertyValue("--light-bg-color");
        textColor        = getComputedStyle(html).getPropertyValue("--light-text-color");
        break;
      default:
        break;
    }

    html.style.setProperty('--mode', mode);
    html.style.setProperty('--bg-color', bgColor);
    html.style.setProperty('--text-color', textColor);
  });
}

$(function() {
  modeToggle();
});

解説

HTML

特筆すべきことはありません。

CSS

#wrap {
  color: var(--text-color);
  background-color: var(--bg-color);
}

#wrapの文字色と背景色にCSSカスタムプロパティを使用しています。

:root {
  --mode:light;
  --bg-color: var(--light-bg-color);
  --text-color: var(--light-text-color);

  // Light Mode
  --light-bg-color: #fff;
  --light-text-color: #000;

  // Dark Mode
  --dark-bg-color: #000;
  --dark-text-color: #fff;
}

実際に使用するCSSカスタムプロパティと、ライトモード/ダークモードそれぞれのCSSカスタムプロパティを用意しておきます。そして、最初に使用するモードのCSSカスタムプロパティを、実際に使用するCSSカスタムプロパティに設定します(今回はライトモード)。

--modeは現在のモードを保持しておくためのCSSカスタムプロパティです。モードが切り替わるたびに値が変更されます。このとき、以下のことに気をつけてください(理由は後述)。

// ❌ プロパティ名と値の間にスペースは不要
--mode: light;

// ❌ 値をシングルクォーテーションやダブルクォーテーションで括らない
--mode:'light';
--mode:"light";

// ⭕ 必ず以下のように記述
--mode:light;

JavaScript

const modeToggle = () => {
  $('#mode-toggle').on('click', function() {

#mode-toggleがクリックされたときのイベントを記述しています。

    const html = document.querySelector('html');
    const currentMode = getComputedStyle(html).getPropertyValue("--mode");

1行目でhtml要素を取得し、2行目で--modeというCSSカスタムプロパティの値を取得しています。CSSカスタムプロパティの書き方によって取得される値が微妙に異なるので注意してください(後続の分岐処理に影響します)。

  • ❌ プロパティ名と値の間にスペースがある
    • CSSカスタムプロパティ:--mode: light;
    • 取得される値:「 light」 ※先頭にスペースがある
  • ❌ 値をシングルクォーテーションやダブルクォーテーションで括る
    • CSSカスタムプロパティ:--mode:'light'; / --mode:"light";
    • 取得される値:'light' / "light"
  • ⭕ 正しく記述
    • CSSカスタムプロパティ:--mode:light;
    • 取得される値:light
    let mode = null, bgColor = null, textColor = null;

    switch (currentMode) {
      case 'light':
        mode             = 'dark';
        bgColor          = getComputedStyle(html).getPropertyValue("--dark-bg-color");
        textColor        = getComputedStyle(html).getPropertyValue("--dark-text-color");
        break;
      case 'dark':
        mode             = 'light';
        bgColor          = getComputedStyle(html).getPropertyValue("--light-bg-color");
        textColor        = getComputedStyle(html).getPropertyValue("--light-text-color");
        break;
      default:
        break;
    }

取得したモードの値によって処理を分岐しています。現在がライトモードならダークモードの値を取得し、現在がダークモードならライトモードの値を取得します。

    html.style.setProperty('--mode', mode);
    html.style.setProperty('--bg-color', bgColor);
    html.style.setProperty('--text-color', textColor);

取得した値を設定しています。

まとめ

CSSカスタムプロパティとJavaScriptを使ってモード切替(配色変更)を実装することができました。意外と簡単に実装できたのではないでしょうか。

今回は実装しませんでしたが、Local Storageにモードの状態を保存しておくことで、次回アクセス時にもモードを保持しておくことが可能になります。

Local Storageについては、以下のページでも触れているので参考にしてください。

関連記事

【CSS+JS】メニューアイコンの一種、ベントーメニューの実装方法(アニメーション付き)
# はじめに メニューアイコンの中ではハンバーガーメニューが有名だと思いますが、その他にもいろいろな種類があって、それぞれに名前もつけられています。 <a class="gallery" data-group="gallery" href= [...]
2021年10月18日 14:26
【CSS+JS】メインコンテンツの裏から現れるフッターの実装方法
# はじめに オシャレなサイトなどでたまに見かける「メインコンテンツの裏から現れるフッター」の実装方法について説明します。 # サンプル <iframe height="392" style="width: 100%;" scrollin [...]
2021年10月18日 13:26
【CSS+JS】背景画像の視差効果(パララックス)を実装する方法
# はじめに JavaScriptプラグインを使わずに、背景画像の視差効果(パララックス)をVanilla JS(ピュアなJavaScript)だけで実装する方法について説明します。 # サンプル まず、背景画像の視差効果(パララックス) [...]
2021年10月17日 12:08
【CSS+JS】ウィンドウ内全体にファイルをドラッグ&ドロップしてアップロード
# はじめに ユーザーが選択したファイルをアップロードする必要がある場合、ファイル選択フィールドを設置することはもちろんですが、ファイルをドラッグ&ドロップしてアップロードできるようにもなっていると使い勝手のいいサービスだと言えます。しかし、ドラ [...]
2021年6月2日 15:15
【CSS+JS】モーダルウィンドウを表示しその中にYouTube動画を動的に埋め込む
# はじめに 動画のサムネイル画像をクリックしてモーダルウィンドウを表示し、その中にYouTube動画を動的に埋め込む方法について説明します。なお、本記事ではYouTube動画を動的に埋め込むまでを範囲とし、埋め込んだ動画の再生制御などは範囲外と [...]
2021年5月20日 23:23
【CSS+JS】テキストを1文字ずつ表示するアニメーションの実装方法
# はじめに モダンなサイトでよく見かける「テキストを1文字ずつ表示するアニメーション」の実装方法を解説します。 # 実装 ## サンプル 繰り返し動作を確認したい場合は右下の「Rerun」ボタンをクリックしてください。 <i [...]
2021年5月20日 13:26
【CSS】CSSだけでMarkdownのコードにファイル名をつける
# はじめに Markdownのコードに、そのコードがどのファイルのものなのかを示すためにファイル名が書いてあるとわかりやすいです。以下はQiitaの記事でコードを書いたときの一例です。 <img data-src="https://i.i [...]
2021年4月6日 14:39
【CSS】カーソルを乗せると流れるようなアニメーションのハンバーガーメニューを作る
# はじめに 今回はハンバーガーメニューにカーソルを乗せると川のように流れるアニメーションを作りたいと思います。 今回の記事は以下の記事をベースにしていますので、まだご覧になっていない方は先にこちらをご覧ください。 <div clas [...]
2020年8月31日 8:54