ウェブサイトにダークモードを導入しました。
それにあわせて従来のスタイル(ライトモード)も変更しています。

モード切り替えボタン

見出し「モード切り替えボタン」

フッタのボタンからモードを切り替えることができます。

モード切り替えボタン

2023/02/09 追記 デザイン変更しました。

ライトモード・ダークモードの判定は初回訪問時は、メディアクエリの prefers-color-scheme から、OS の設定を参照しています。

ただ、OS の設定のみではなく、モード切り替えボタンからも切り替えられるようにしたかったので、ボタンを選択すると localStorage でモードの状態が保存されるようにしています。

ページを開くと以下の JS が実行され、これらのモードの判定結果を HTML のルート要素に data-color 属性の値として指定しています。

// モードの判定結果を `data-color` の値に指定
const setColorMode = (mode) => document.documentElement.setAttribute('data-color', mode);

// モードの判定
const getColorMode = () => {
  if (localStorage.getItem('color-mode')) {
    return localStorage.getItem('color-mode');
  } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    return 'dark';
  }
  return 'light';
};

setColorMode(getColorMode());

これによって HTML の data-color の値が切り替わります。

<!-- ライトモード -->
<html data-color="light">

<!-- ダークモード -->
<html data-color="dark">

上記のようにモードの判定の結果を data-color に指定しているため、CSS では prefers-color-scheme: dark ではなく、[style*="--color-mode: dark"] を起点としてスタイルを反映しています。

/* ライトモード */
:root {
  --color-primary: #fff;
  /* ... */
}

/* ダークモード */
:root[style*="--color-mode: dark"] {
  --color-primary: #042020;
  /* ... */
}

影響範囲が最小限で済むように、なるべくルート要素の CSS 変数のみで対応できるようにしていますが、それだけでは対応できない部分は個別に調整しました。

反省点としては、CSS の変数名に --color-white--color-black という命名をしていたため、ダークモードのときに把握しづらくなってしまいました。
(見た目ではなく役割で命名すべきでした)

/* ライトモード */
:root {
  /* ... */
  --color-black: #000;
  --color-white: #fff;
  /* ... */
}

/* ダークモード */
:root[style*="--color-mode: dark"] {
  /* ... */
  --color-black: #cfdddd;
  --color-white: #1e1e1e;
  /* ... */
}

これらの課題も含め、また少しずつ手直ししていきます。