tanaka101

ダークモード

CSS変数を切り替えるだけでダークモードを実現する仕組みを学びます。

ダークモードの正体は「変数の切り替え」

前回のレッスンで、CSS変数を使って色を1箇所で管理する方法を学びました。

globals.css
:root {
  --background: #ffffff;
  --text: #171717;
}
body {
  background-color: var(--background);
  color: var(--text);
}

ダークモードの仕組みはとてもシンプルです。この変数の値だけを別のセットに切り替えるだけで実現できます。

新しいCSSの機能を覚える必要はありません。前回学んだCSS変数と、レッスン2で学んだカスケードの組み合わせです。

:root と .dark で2セットの色を定義する

ライトモード用の色は :root に、ダークモード用の色は .dark に定義します。

globals.css
/* ライトモード(デフォルト) */
:root {
  --background: #ffffff;
  --text: #171717;
}
 
/* ダークモード */
.dark {
  --background: #171717;
  --text: #fafafa;
}

ポイントは、変数名は同じで値だけが違うことです。

変数名ライトモード(:root)ダークモード(.dark)
--background#ffffff(白)#171717(濃いグレー)
--text#171717(濃いグレー)#fafafa(明るいグレー)

body のベーススタイルは var(--background)var(--text) を参照しているだけなので、変数の値が変われば見た目も自動的に変わります。

HTMLに .dark クラスをつけるだけで切り替わる

ダークモードへの切り替えは、HTMLの <html> 要素に .dark クラスを付けるだけです。

ライトモード
<html>
  ...
</html>
ダークモード
<html class="dark">
  ...
</html>

実際に切り替わる様子を確認してみましょう。

ダークモードの切り替え

ボタンを押すと、背景色と文字色が切り替わります。CSSは一切変更していません。.dark クラスの付け外しだけで、変数の値が切り替わっているのです。

なぜ .dark が :root を上書きできるのか

ここでレッスン2 『カスケードと詳細度』で学んだ詳細度を思い出してください。

:root は疑似クラスセレクタで、.dark はclassです。どちらも詳細度は同じ(class相当)ですが、.dark:root後に書かれているため、カスケードの「出現順」ルールで .dark の値が優先されます。

詳細度出現順
:rootclass相当先に書かれている
.darkclass相当後に書かれている → こちらが勝つ

レッスン2で学んだ「同じ詳細度なら後に書いた方が勝つ」というルールがここで活きています。

.dark クラスが <html> 要素についていないとき、.dark のルールはどの要素にもマッチしないため、:root の値がそのまま使われます。

色を増やしてみよう

実際のサイトでは背景色と文字色だけでなく、ボーダーの色やアクセントカラーなども管理します。

複数の色をダークモード対応にする

変数が4つに増えましたが、やっていることは同じです。:root.dark で2セットの値を定義して、コンポーネント側では var() で参照するだけ。色を何個追加しても、この仕組みは変わりません。

このサイトでもダークモードにCSS変数を使っています。

ダークモード 色相 などで検索すると検証してくれてる人がたくさんいるので参考にするとよいです。

構成の概要を振り返ろう

4番目まで理解できましたね。

globals.css(構成の概要)
/* 1. CSSリセット */
*, *::before, *::after { box-sizing: border-box; margin: 0; }
 
/* 2. ベーススタイル */
body { font-family: system-ui, sans-serif; line-height: 1.6; }
 
/* 3. デザイントークン(CSS変数) */
:root { --background: #ffffff; --text: #171717; }
 
/* 4. ダークモードの色定義 */
.dark { --background: #171717; --text: #ffffff; }
 
/* 5. レスポンシブなサイズ定義 */
h1 { font-size: clamp(1.5rem, 1.25rem + 1vw, 1.875rem); }
globals.css(ここまで理解できた部分)
/* 1. CSSリセット ✅ */
*, *::before, *::after { box-sizing: border-box; margin: 0; }
 
/* 2. ベーススタイル ✅ */
body { font-family: system-ui, sans-serif; line-height: 1.6; }
 
/* 3. デザイントークン(CSS変数) ✅ */
:root { --background: #ffffff; --text: #171717; }
 
/* 4. ダークモードの色定義 ✅ */
.dark { --background: #171717; --text: #ffffff; }

残りは 5. レスポンシブなサイズ定義 です。

次のステップ

ダークモードの仕組みを学びました。次のレッスンでは、よりモダンな手法として、画面幅に応じてサイズを滑らかに変化させる clamp() を学びます。