ダークモード対応画像の実装ガイド - picture 要素と CSS による切り替え
ダークモードと画像の課題 - なぜ画像の対応が必要なのか
ダークモードの普及に伴い、テキストや背景色だけでなく画像もダークモードに適応させる必要性が高まっています。ライトモード向けに作成された画像をダークモードでそのまま表示すると、いくつかの問題が発生します。
発生する問題:
- 眩しさ: 白背景の画像やスクリーンショットが暗い UI の中で浮き上がり、目に負担をかける
- コントラストの崩壊: ライトモード前提で設計されたグラフや図解が、ダークモードでは視認性が低下する
- ブランドの不整合: ロゴやアイコンの色がダークモードの配色と調和しない
- 透過画像の問題: 透過 PNG のテキストや線がダークモードの背景色と同化して見えなくなる
すべての画像をダークモード対応にする必要はありません。写真やイラストはそのままで問題ないケースが多いですが、以下のカテゴリの画像は対応を検討すべきです。
- ロゴ・アイコン (特に単色のもの)
- スクリーンショット (UI のキャプチャ)
- 図解・フローチャート・グラフ
- 白背景前提のイラスト
- 透過背景の画像で、前景色がダーク背景と同化するもの
対応方法は画像の種類によって異なります。SVG はスタイルの動的変更が容易ですが、ラスター画像 (JPEG, PNG, WebP) は別バージョンの用意が必要になる場合があります。
picture 要素による画像切り替え - HTML だけで実現する方法
HTML の <picture> 要素と media 属性を使えば、JavaScript なしでダークモード用の画像を出し分けることができます。ブラウザのメディアクエリ評価に基づいて、適切な画像ソースが自動的に選択されます。
基本的な実装:
<picture> <source srcset="diagram-dark.png" media="(prefers-color-scheme: dark)"> <source srcset="diagram-light.png" media="(prefers-color-scheme: light)"> <img src="diagram-light.png" alt="システム構成図"></picture>
この方法の利点は、CSS や JavaScript に依存せず、HTML のみで完結する点です。ブラウザは表示に必要な画像のみをダウンロードするため、不要な画像の転送が発生しません。
レスポンシブ画像との組み合わせ:
<picture> <source srcset="hero-dark-800.webp 800w, hero-dark-1600.webp 1600w" media="(prefers-color-scheme: dark)" type="image/webp"> <source srcset="hero-light-800.webp 800w, hero-light-1600.webp 1600w" media="(prefers-color-scheme: light)" type="image/webp"> <img src="hero-light-800.jpg" alt="ヒーロー画像" sizes="100vw"></picture>
フォーマット切り替え (WebP/AVIF) とダークモード切り替えを同時に行う場合、<source> 要素の数が増えますが、ブラウザは最初にマッチしたソースのみをダウンロードするため、パフォーマンスへの影響はありません。
注意点: prefers-color-scheme はシステム設定を参照します。サイト独自のダークモードトグル (JavaScript で切り替える方式) には対応できません。サイト独自のトグルに対応するには、CSS 変数や JavaScript による切り替えが必要です。
CSS による画像のダークモード対応 - フィルタと変数の活用
CSS を使えば、同じ画像ファイルをダークモードに適応させることができます。別バージョンの画像を用意する必要がないため、管理コストが低い方法です。
CSS フィルタによる明るさ・コントラスト調整:
@media (prefers-color-scheme: dark) { .screenshot { filter: brightness(0.8) contrast(1.1); } .diagram { filter: invert(1) hue-rotate(180deg); }}
invert(1) hue-rotate(180deg) の組み合わせは、画像の色を反転させつつ色相を 180 度回転させることで、写真の色味を保ちながら明暗を反転させるテクニックです。図解やグラフに効果的ですが、写真には不自然な結果になるため使用を避けてください。
CSS 変数を使った背景画像の切り替え:
:root { --logo-url: url('/images/logo-light.svg'); }@media (prefers-color-scheme: dark) { :root { --logo-url: url('/images/logo-dark.svg'); }}.logo { background-image: var(--logo-url); }
mix-blend-mode の活用: mix-blend-mode: difference や mix-blend-mode: exclusion を使うと、背景色に応じて画像の見え方を自動的に調整できます。ただし、予測しにくい結果になることがあるため、限定的な用途に留めてください。
opacity の調整: ダークモードでは画像の不透明度を 80-90% に下げることで、暗い背景との調和を改善できます。特に写真やイラストに有効で、眩しさを軽減しつつ内容は十分に視認できます。
@media (prefers-color-scheme: dark) { img:not(.no-dim) { opacity: 0.85; } img:not(.no-dim):hover { opacity: 1; }}
SVG のダークモード対応 - currentColor と CSS 変数による動的な色変更
SVG はダークモード対応に最も適した画像フォーマットです。CSS でスタイルを動的に変更できるため、1 つのファイルでライトモードとダークモードの両方に対応できます。
currentColor の活用: SVG の fill や stroke に currentColor を指定すると、親要素の color プロパティを継承します。
<svg viewBox="0 0 24 24"> <path fill="currentColor" d="M12 2L2 7l10 5 10-5-10-5z"/></svg>
CSS でテキスト色をダークモード対応にしていれば、SVG アイコンも自動的に追従します。インライン SVG でもファイル参照でも動作しますが、<img> タグで読み込んだ外部 SVG には CSS が適用されないため、インライン SVG または CSS 背景画像として使用してください。
CSS 変数による多色 SVG の対応:
<svg> <rect fill="var(--svg-bg, #ffffff)" /> <text fill="var(--svg-text, #333333)">Label</text></svg>
:root { --svg-bg: #ffffff; --svg-text: #333333; }@media (prefers-color-scheme: dark) { :root { --svg-bg: #1a1a2e; --svg-text: #e0e0e0; }}
SVG 内の prefers-color-scheme: SVG ファイル内に <style> 要素を埋め込み、メディアクエリを記述することも可能です。これにより、外部 CSS に依存せず SVG 単体でダークモード対応が完結します。ファビコンの SVG 版 (favicon.svg) でこのテクニックが特に有効です。
スクリーンショットと図解のダークモード対応 - 実践的なワークフロー
スクリーンショットや図解は、ダークモード対応で最も手間がかかるカテゴリです。自動化できる部分と手動対応が必要な部分を整理し、効率的なワークフローを構築します。
スクリーンショットの対応方針:
- 方針 A: 2 バージョン用意する: ライトモードとダークモードの両方でスクリーンショットを撮影し、
<picture>要素で切り替える。最も品質が高いが、更新時に 2 倍の作業が必要 - 方針 B: CSS フィルタで対応する: ライトモードのスクリーンショットに
filter: brightness(0.8)を適用。手軽だが、暗くなるだけで真のダークモード対応にはならない - 方針 C: 角丸とシャドウで馴染ませる: スクリーンショットに
border-radiusとbox-shadowを適用し、ダーク背景との境界を柔らかくする。内容は変えないが、視覚的な違和感を軽減
図解・フローチャートの対応: Mermaid、Draw.io、Figma などのツールで図解を作成する場合、ダークモード用のカラーテーマを事前に定義しておくと効率的です。Mermaid はテーマ設定でダークモード用の図を生成できます。
自動化のアプローチ: Playwright や Puppeteer を使えば、ダークモードでのスクリーンショット撮影を自動化できます。page.emulateMediaFeatures でダークモードをエミュレートし、同じページのライト版とダーク版を自動生成するスクリプトを構築できます。CI/CD パイプラインに組み込めば、ドキュメント更新時に自動的に両バージョンが生成されます。
パフォーマンスとアクセシビリティの考慮事項
ダークモード画像の実装では、パフォーマンスとアクセシビリティの両面に配慮する必要があります。
パフォーマンスの最適化:
- 不要な画像の読み込み防止:
<picture>要素を使えば、ブラウザは表示に必要な画像のみをダウンロードします。CSS のbackground-imageで切り替える場合も、使用されないモードの画像はダウンロードされません - 画像サイズの管理: ダークモード用の別バージョンを用意すると、サーバーのストレージ使用量が 2 倍になります。CDN のストレージコストを考慮し、本当に別バージョンが必要な画像を厳選してください
- CSS フィルタの GPU 負荷:
filterプロパティは GPU で処理されますが、大量の画像に適用するとモバイルデバイスでバッテリー消費が増加する可能性があります
アクセシビリティの確保:
- コントラスト比の維持: ダークモードでも WCAG AA 基準 (4.5:1) のコントラスト比を維持してください。CSS フィルタで明るさを下げた結果、テキストを含む画像の可読性が低下していないか確認が必要です
- alt テキストの一貫性: ライトモードとダークモードで異なる画像を表示する場合でも、
altテキストは同じ内容を伝えるものにしてください - アニメーションの制御:
prefers-reduced-motionメディアクエリも併せて考慮し、モード切り替え時のトランジションを無効化できるようにしてください
テスト方法: Chrome DevTools の Rendering パネルで prefers-color-scheme をエミュレートできます。ライトモードとダークモードを素早く切り替えながら、全画像の表示を確認してください。Lighthouse のアクセシビリティ監査も両モードで実行することを推奨します。