ゲームのダークテーマとライトテーマの両立
遊びをつくる執筆にClaudeを使用しています。

ゲームにダークテーマとライトテーマの両方を用意しようとすると、「色の変数を2セット持てばいいだけでは?」と思いきや、実際には想定外の箇所で見た目が崩れたり、切り替え直後にちらつきが起きたりします。単に背景と文字色を入れ替えるだけでは済まない理由は、影・半透明・画像・アニメーションといった要素がテーマに依存しているためです。 この記事では、HTMLとCSSだけで動くブラウザゲームを前提に、両テーマを「あとから足す」のではなく「最初から両立する設計」で作るための判断基準と手順を示します。すでに片方だけ実装しているゲームを改修する場合の落とし穴も合わせて扱います。
この記事の目次
なぜ「あとから足す」と壊れやすいのか
テーマ切り替えが後回しになると、色がCSS内に直書き(ハードコード)された状態で積み上がります。background: #1a1a2e のような値がセレクターごとに散らばっているコードは、ダークとライトで2倍の記述が必要になり、修正漏れが発生しやすくなります。
もう一つの問題は「コントラスト比の非対称性」です。ダークテーマで見やすい薄い灰色のテキストは、ライトテーマに切り替えると白背景に溶けてしまいます。両テーマを同時に設計しないと、片方を直すたびにもう片方が崩れるという繰り返しに陥ります。
設計の中心:CSSカスタムプロパティでトークンを定義する
テーマを両立させる最もシンプルな方法は、色をCSSカスタムプロパティ(CSS変数)として定義し、テーマごとにその値だけを差し替える構造にすることです。
まず「デザイントークン」として意味単位で変数を命名します。--color-bg(背景)、--color-surface(カード・パネルなどの浮き上がり面)、--color-text-primary(主要テキスト)、--color-text-muted(補足テキスト)、--color-accent(ボタン・強調)のように、役割で名前を付けます。--dark-blue のような色名での命名は避けてください。テーマを変えるとき名前と実態が食い違い、混乱の原因になります。
:root[data-theme="light"] {
--color-bg: #f5f5f0;
--color-surface: #ffffff;
--color-text-primary: #1a1a1a;
--color-text-muted: #666666;
--color-accent: #2563eb;
}
:root[data-theme="dark"] {
--color-bg: #0f0f1a;
--color-surface: #1e1e2e;
--color-text-primary: #e8e8f0;
--color-text-muted: #9090a8;
--color-accent: #60a5fa;
}<html> タグに data-theme 属性を付け、JavaScriptで値を切り替えるだけでゲーム全体の色が変わります。切り替えのコードは1行です。
document.documentElement.setAttribute('data-theme', 'dark');ハードコード色・画像・影を洗い出す3ステップ
変数化の構造を整えても、既存コードには直書きの色が残っていることが多いです。以下の順で洗い出してください。
ステップ1:CSS内のカラー値を一括検索する
エディタで #(16進数)、rgb(、rgba(、hsl( を検索し、ヒットした箇所をリスト化します。rgba(0,0,0,0.5) のような半透明の影も含めて確認します。
ステップ2:画像・SVGの地色を確認する
PNGやSVGのアイコン・キャラクターが白背景を前提に作られていると、ダークテーマでは白い矩形が浮いて見えます。対処は2つあります。背景透過のPNGを使うか、SVGであればfill色をCSSで上書きする(fill: var(--color-text-primary))方法です。キャンバスに描画したスプライトは、テーマ変数を参照してJavaScript側の描画色も書き換える必要があります。
ステップ3:影と境界線を見直す
box-shadow: 2px 2px 8px rgba(0,0,0,0.3) はライトテーマでは自然に見えますが、ダークテーマでは暗い背景に黒い影が溶けて奥行きが消えます。ダークテーマの影は rgba(0,0,0,0.6) のように濃くするか、rgba(255,255,255,0.05) のような明るめのグロウ(発光感)に替えると立体感が戻ります。影もCSS変数化しておくと切り替えが楽です。
ユーザー設定の保存と初期値の決め方
テーマを選べるゲームは、リロードのたびにテーマがリセットされると体験が悪くなります。localStorage に選択値を保存し、ページ読み込み時に復元するのが標準的な対処です。
// 保存
localStorage.setItem('theme', 'dark');
// 復元(bodyより前に実行するとちらつきを防げる)
const saved = localStorage.getItem('theme');
const system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', saved ?? system);初期値をシステム設定(prefers-color-scheme)から取ることで、ユーザーが明示的に選択しなくてもOSの好みに合ったテーマで起動します。ユーザーが切り替えたらその値を保存し、以降はそちらを優先するという2段階の判定にすると、多くのケースで違和感のない初期表示になります。
切り替えボタンを実装する際は、「アイコンだけ」にせず「ライト / ダーク」のテキストラベルか aria-label を添えてください。アクセシビリティ上、画像だけのボタンはスクリーンリーダーで意味が伝わらなくなります。
コントラスト比を両テーマで確認する最低ライン
テーマを切り替えたとき、文字が読みにくくなる問題は「色を変えた」だけで「コントラストを確認していない」場合に起きます。WCAGガイドライン(ウェブアクセシビリティの国際規格)では、通常テキストのコントラスト比を4.5:1以上とすることを基準としています。ゲームUIはこれを厳密に要求される場面は少ないですが、最低でも3:1を目安にすると視認性が保たれます。
確認の手順はシンプルです。ブラウザの開発者ツール(DevTools)でテキスト要素を選択し、Computedパネルに表示されるコントラスト比を見ます。ChromeとFirefoxは両方ともこの値を自動計算して表示します。
失敗しやすいのは「アクセント色をそのまま両テーマに使う」パターンです。例えば鮮やかな青(#2563eb)はライトテーマの白背景では十分なコントラストを持ちますが、ダークテーマの暗い背景でも同じ青を使うと逆に沈んで見えます。アクセント色もテーマごとに輝度を調整する必要があります。
次の行動
既存ゲームの改修から始める場合は、まずCSS内の色直書き箇所を検索で洗い出し、変数化できる箇所から順に置き換えていくと作業を小分けにできます。新規制作であれば、最初のCSSファイルにトークン定義ブロックを書くことを習慣にしてください。完成したゲームはアソバースにZIPまたはHTMLファイルとして投稿し、実際の端末・ブラウザで両テーマの表示を確認してみましょう。
参考資料
- WCAG(Web Content Accessibility Guidelines)公式ドキュメント
- MDN Web Docs「Using CSS custom properties」
- MDN Web Docs「prefers-color-scheme」

