ダークモード対応は、一見「色を反転すればいい」だけの作業に見えます。しかし実際に取り組むと、影が見えなくなる、画像が浮く、彩度の高い色が目に刺さる、と想定外の崩れが次々に出てきます。ダークモードは配色の差し替えではなく、もうひとつのテーマを設計する作業です。
この記事では、CSSカスタムプロパティを使った破綻しにくい設計方法と、単純な色反転で壊れるポイントへの対処をまとめます。
設計の基本:色を「役割名」で参照する
ダークモード対応の成否は、実装を始める前のCSS設計でほぼ決まります。コンポーネントのCSSに #fff や #333 が直接書かれている状態では、全箇所にダーク用の上書きを書くことになり、確実に漏れが出ます。
対策は、色を「役割名」のカスタムプロパティに集約し、テーマ側でその値だけを切り替える構造にすることです。
:root {
--color-bg: #ffffff;
--color-surface: #f5f6f8; /* カードなど一段上の面 */
--color-text: #1f2937;
--color-border: #e5e7eb;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #121212;
--color-surface: #1e1e1e;
--color-text: #e5e7eb;
--color-border: #374151;
}
}
.card {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
}
コンポーネント側はテーマの存在を知らなくてよい、という状態がゴールです。切り替え対象がカスタムプロパティの定義ブロックひとつに閉じていれば、テーマ追加も保守も容易になります。
prefers-color-schemeと手動切り替えの併用
prefers-color-scheme はOSの設定に追従するメディアクエリで、まずはこれに対応するのが基本です。ただし実務では「OSはダークだがこのアプリはライトで使いたい」という要望が必ず出るため、手動切り替えとの併用が定番です。
- ユーザー選択は「ライト/ダーク/OSに従う」の3択にする
- 選択結果は
html要素の属性(例:data-theme="dark")で表現し、CSSはメディアクエリと属性セレクタの両方で同じ変数を切り替える - 選択値はlocalStorageに保存し、ページ読み込みの早い段階(headのインラインスクリプト)で属性を適用して、一瞬ライトで表示されるちらつき(FOUC)を防ぐ
また、color-scheme: light dark; をルートに指定しておくと、スクロールバーやフォーム部品などブラウザ標準UIの配色もテーマに追従します。忘れやすいポイントです。
単純な色反転で壊れるもの
影:暗い背景では機能しない
ライトモードで階層を表現していた box-shadow は、暗い背景ではほとんど見えません。ダークモードでは影の代わりに、面の明るさで高さを表現するのが定石です。手前にある要素ほど背景をわずかに明るくします(例: 背景#121212、カード#1e1e1e、ダイアログ#2a2a2a)。前述のように面の色を変数化しておけば、この切り替えも変数定義だけで済みます。
画像とロゴ:白背景前提の素材が浮く
白背景に置く前提で作られたロゴや図版は、暗い背景の上で四角く浮いてしまいます。対処は素材の性質ごとに分けます。
- ロゴやアイコンはダーク用素材を用意し、
picture要素とmedia="(prefers-color-scheme: dark)"で出し分ける。SVGならcurrentColorを使えば1素材で済む - 写真はそのままでよいが、まぶしく感じる場合は
filter: brightness(0.9)程度で軽く落とす - スクリーンショットなど白背景の図版には、薄いボーダーを付けて境界を示す
彩度:ライト用の色は暗背景で刺さる
ライトモード用の鮮やかなアクセントカラーを暗い背景にそのまま置くと、ハレーションを起こしたように目に刺さります。ダークモードでは彩度を下げ、明度を上げたバリエーションを使うのが基本です。また「真っ白の文字」も強すぎるため、本文は#e0e0e0前後に抑えます。逆に純黒(#000000)の背景も避け、#121212程度のグレーを使うと目の負担が減ります。コントラスト比4.5:1の基準はダークモードでも同じなので、ダーク側のパレットも忘れずに検証してください。
まとめ
ダークモード対応の要点は、色を役割名のカスタムプロパティに集約してテーマ側で一括切り替えする設計、prefers-color-scheme+手動切り替え(data-theme属性とlocalStorage)の併用、そして影は面の明るさへ・画像は出し分け・彩度は抑えるという「反転では済まない3点」への対処です。設計を先に整えれば、ダークモードは怖くありません。むしろ変数化を強制してくれる分、CSSの健全性が上がる良い機会になります。
