チュートリアル

レスポンシブカラー:異なる画面への色の適応

2分で読める

レスポンシブデザインはレイアウトの問題を解決しました。フルードグリッド、フレキシブルな画像、ブレークポイントにより、単一のコードベースでスマートフォンサイズの画面から4Kモニターまで対応できるようになりました。しかし、色にはCSSグリッドやメディアクエリが当初設計されていなかった、独自のレスポンシブ問題があります。

同じ16進数値でも、iPhoneのOLED画面、WindowsラップトップのLCD、カラーキャリブレーションされたプロ用モニター、屋外ディスプレイパネルでは異なって見えます。ユーザーは明るい環境と暗い環境の間を行き来し、予測できない文脈でUIを使います。ワイドガモットのディスプレイはsRGBでは表現できない色をレンダリングできます。

レスポンシブカラーデザインとは、これらの違いを理解し、意図的に適応するスタイルを書くことです。偶然に任せてはいけません。


ディスプレイの違いとその重要性

異なるディスプレイ技術の物理的特性

LCD(液晶ディスプレイ) — ほとんどのWindowsラップトップ、廉価モニター、旧型MacBookに搭載。LCDは画面全体をバックライトで照らし、カラーフィルターを使います。ブラックはグレーがかって見えます(バックライトが透過する)。色域は通常sRGBまたはそれに近い範囲です。低彩度域の色はデザインした通りに見える傾向があります。

OLED(有機EL) — iPhone Pro、Samsung Galaxy、Liquid Retina XDRを搭載したMacBook Pro、ハイエンドモニター。各ピクセルが独自の光を発するため、ブラックは真のブラックです(光の出力がゼロ)。色はLCDよりも鮮やかでコントラストが高く見えます。LCDで薄いグレーの背景が、OLEDでははっきりと暗く見えることがあります。

IPS LCD — 中〜高クラスのモニター。基本的なLCDより優れた色精度と広い視野角。sRGBを完全にカバーし、DCI-P3まで拡張することもあります。

Mini-LED — MacBook Pro 14/16インチとPro Display XDRのAppleのXDRディスプレイ。数千のローカル調光ゾーンを持つLCDパネルを使用。非常に高いピーク輝度を維持しながら、OLEDに匹敵するブラックレベルを実現。Display P3の色域。

E-ink — KindleなどのE-reader。グレースケールのみまたは限定的なカラー。ウェブアプリに対して行う色のデザイン決定はここでは無関係ですが、コンテンツの読みやすさ(コントラスト)は依然として重要です。

色域:sRGB、Display P3、Rec. 2020

色域とは、ディスプレイが表現できる色の範囲です。ウェブデザインで遭遇する3つの色域:

sRGB — すべてのモニターがサポートする標準の色域で、CSSが歴史的に前提としてきた色域です。#FF6B6B と書くと、ブラウザはそれをsRGBカラーとして解釈します。ブラウザとOSがその色をディスプレイにマッピングする管理を行います。

Display P3 — sRGBより約25〜30%広い。2016年頃以降のすべてのiPhone、iPad、Macディスプレイ、多くの最新のAndroidフラッグシップ、一部のPCモニターでサポートされています。P3の色域は特に赤と緑の方向に拡張されており、P3ディスプレイで鮮やかに見える彩度の高い赤と緑はsRGBでは全く表現できません。

Rec. 2020 — P3よりもさらに広い。主にビデオ制作とHDRコンテンツに関係します。消費者向けのディスプレイでRec. 2020を完全にカバーするものはほとんどなく、ウェブUIデザインの実用的なターゲットにはまだなっていません。

P3ディスプレイでsRGBカラーを使用した場合に何が起きるか

ブラウザが #FF6B6B をP3ディスプレイでレンダリングすると、OSがsRGBカラーをP3色域にマッピングします。結果は正しく見えます。間違った色は表示されません。得られないのは、P3が持つ拡張された鮮やかさです。彩度の高い赤と緑は、sRGBディスプレイと同じ視覚的な強度で表示されます。

P3のより広い色域を活用するには、P3空間の色を明示的に指定する必要があります。そしてブラウザはsRGBディスプレイのためにグレースフルにフォールバックします。


色適応のためのCSSメディアクエリ

prefers-color-scheme:ダークモード

最も広く使われる色関連のメディアクエリは、ユーザーがOSでダークモードを有効にしているときにパレットを適応させます。

/* ライトモード(デフォルト) */
:root {
  --color-bg: #F8FAFC;
  --color-surface: #FFFFFF;
  --color-text-primary: #1E293B;
  --color-text-muted: #64748B;
  --color-brand: #2563EB;
  --color-border: #E2E8F0;
}

/* ダークモード */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0F172A;
    --color-surface: #1E293B;
    --color-text-primary: #F1F5F9;
    --color-text-muted: #94A3B8;
    --color-brand: #60A5FA;
    --color-border: #334155;
  }
}

ダークモードのブランドカラーが #2563EB(深い青)から #60A5FA(明るい青)にシフトしていることに注目してください。これは意図的なものです。ライトモードのブランドブルーは白い背景に対して良好なコントラストを持っています。ダークネイビーの背景では、同様のコントラストを維持するために明るい値が必要です。

コントラストチェッカーを使って、ライトモードとダークモード両方の組み合わせを検証しましょう。

  • ライトモード:#1E293B on #F8FAFC → 4.5:1をクリアすることを確認
  • ダークモード:#F1F5F9 on #0F172A → 4.5:1をクリアすることを確認
  • ダークモードのブランドリンク:#60A5FA on #0F172A → 4.5:1をクリアすることを確認

prefers-contrast:ハイコントラストモード

一部のユーザー、特に弱視の方は、OSでハイコントラストモードを有効にします。prefers-contrast メディアクエリを使って、この設定に対応できます。

@media (prefers-contrast: more) {
  :root {
    --color-text-primary: #000000;
    --color-bg: #FFFFFF;
    --color-brand: #0000CC;
    --color-border: #000000;
  }
}

ハイコントラストモードでは、くすんだ色、装飾的なグラデーション、微妙なティントを、鮮明でハイコントラストな代替に置き換える必要があります。目標は最大限の読みやすさであり、視覚的な洗練ではありません。

prefers-contrast の値には morelessforcedno-preference があります。forced はWindowsのハイコントラストモードに対応しており、システムレベルの色のオーバーライドが適用されます。CSSの色はOSによって完全に上書きされる可能性があります。

color-gamut:ワイドガモットディスプレイへのターゲティング

color-gamut メディアクエリは、ユーザーのディスプレイがsRGBより広い色域をサポートしているかを検出します。

/* デフォルト:sRGBカラー */
:root {
  --color-brand: #FF6B6B;
}

/* P3対応ディスプレイ:より鮮やかなバリアント */
@media (color-gamut: p3) {
  :root {
    --color-brand: color(display-p3 1 0.35 0.35);
  }
}

color(display-p3 1 0.35 0.35) の構文は、display-p3 カラースペースを使ったCSSの color() 関数を使います。値は0〜1の範囲です(0〜100%に相当)。この特定の値は、sRGBで表現できるよりも彩度の高い赤にマッピングされます。

テキスト、背景、ボーダーなど、ほとんどのUI要素にはsRGBの色域で十分です。P3カラーの使用例は主にブランドアクセント、ヒーロー画像、対応ディスプレイで最大限の鮮やかさを求めるマーケティング要素です。

forced-colors:WindowsのハイコントラストM

WindowsのハイコントラストモードはスタイルシートのCSSカラーをシステム定義のパレットに置き換えます。要素は形とレイアウトを保持しますが、色はユーザーが選んだハイコントラストテーマ(通常は黒背景に白テキスト、または白背景に黒テキスト、いくつかのアクセントカラー)でオーバーライドされます。

forced-colorsモードでどの色が表示されるかを制御することはできませんが、インターフェースが機能し続けることは保証できます。

@media (forced-colors: active) {
  /* システムキーワードカラーを使用する */
  .button {
    background-color: ButtonFace;
    color: ButtonText;
    border: 2px solid ButtonBorder;
  }

  /* 色だけに依存していた要素にボーダーを復元する */
  .status-indicator {
    border: 2px solid currentColor;
  }
}

ButtonFaceButtonTextCanvas などのCSSキーワードカラーは、ユーザーが選んだハイコントラストのシステムカラーに自動的にマッピングされます。これらを使うことで、コンポーネントが壊れた状態ではなく意図的なものとして見えるようになります。


CSSカスタムプロパティによる動的カラー

レスポンシブカラーのためのトークンアーキテクチャ

CSSカスタムプロパティ(変数)は、レスポンシブカラーシステムの基盤です。重要な原則は、コンポーネントが生の値ではなくセマンティックトークンを参照するということです。メディアクエリがトークンの値を変更すれば、コンポーネントは更新する必要がありません。

/* セマンティックトークン — これらがコンポーネントで使用するもの */
:root {
  /* テキスト */
  --text-primary: #1E293B;
  --text-secondary: #64748B;
  --text-inverse: #F1F5F9;

  /* サーフェス */
  --surface-base: #F8FAFC;
  --surface-raised: #FFFFFF;
  --surface-overlay: rgba(0, 0, 0, 0.04);

  /* ブランド */
  --brand-primary: #2563EB;
  --brand-primary-hover: #1D4ED8;
  --brand-primary-text: #FFFFFF;

  /* フィードバック */
  --color-error: #DC2626;
  --color-error-bg: #FEF2F2;
  --color-success: #16A34A;
  --color-success-bg: #F0FDF4;
}

@media (prefers-color-scheme: dark) {
  :root {
    --text-primary: #F1F5F9;
    --text-secondary: #94A3B8;
    --text-inverse: #1E293B;

    --surface-base: #0F172A;
    --surface-raised: #1E293B;
    --surface-overlay: rgba(255, 255, 255, 0.05);

    --brand-primary: #60A5FA;
    --brand-primary-hover: #93C5FD;
    --brand-primary-text: #0F172A;

    --color-error: #F87171;
    --color-error-bg: #1F0E0E;
    --color-success: #4ADE80;
    --color-success-bg: #0D1F12;
  }
}

コンポーネントはトークンを使います:

.card {
  background-color: var(--surface-raised);
  color: var(--text-primary);
  border: 1px solid var(--color-border, #E2E8F0);
}

.button-primary {
  background-color: var(--brand-primary);
  color: var(--brand-primary-text);
}

ユーザーがダークモードに切り替えると、トークンの値が変わり、すべてのコンポーネントが自動的に更新されます。

ページリロードなしのインラインテーマ切替

カスタムプロパティはリアルタイムで更新されます。完全なページリフレッシュなしにテーマトグルを実装できます。

function toggleTheme() {
  const root = document.documentElement;
  const isDark = root.classList.toggle('dark-theme');

  // prefers-color-schemeの値をオーバーライドする
  if (isDark) {
    root.style.setProperty('--surface-base', '#0F172A');
    root.style.setProperty('--text-primary', '#F1F5F9');
    // ... 全ダークトークン
  } else {
    root.style.removeProperty('--surface-base');
    root.style.removeProperty('--text-primary');
    // オーバーライドを削除するとメディアクエリの値が復元される
  }

  localStorage.setItem('theme', isDark ? 'dark' : 'light');
}

ユーザーの設定を localStorage に保存し、最初の描画前にページ読み込み時に適用することで、誤ったテーマのフラッシュを防げます。

動的なティントのための color-mix() の使用

CSS color-mix()(2023年時点ですべての最新ブラウザでサポート)を使うと、すべてのバリアントを事前計算することなく、単一のベースカラートークンからティントやシェードを作成できます。

:root {
  --brand-base: #2563EB;
}

.badge {
  /* ブランドカラーをホワイトと混ぜてライトなティントを作る */
  background-color: color-mix(in srgb, var(--brand-base) 15%, white);
  color: var(--brand-base);
}

.badge-dark {
  /* ブラックと混ぜてダークなバリアントを作る */
  background-color: color-mix(in srgb, var(--brand-base) 85%, black);
  color: white;
}

これは特にダークモードで有用です。白い背景に対するブランドカラーのライトなティントが、暗い背景に対するダークなティントになる必要があります。color-mix() を使えば、その関係をトークンで一度定義するだけで、自動的に適応されます。


画面適応のためのカラースペース変換

レスポンシブカラーに16進数コードだけでは不十分な理由

16進数コードはsRGBカラーを表します。カラースペース(sRGB、Display P3、OKLCH)をまたいで作業する場合、表現の間で変換する必要があります。カラーコンバーターはHEX、RGB、HSL、CMYK、OKLCHをサポートしており、以下のような場合に便利です。

  • ダークモードでどのように見えるかを理解するために、色のHSL明度値を見つける
  • フォールバックのために、P3カラーを最も近いsRGBの近似値に変換する
  • 明度軸が真に知覚的に均一なOKLCHで作業する

知覚的に均一なダークモードパレットのためのOKLCH

OKLCH(Oklab明度、彩度、色相)は、レスポンシブカラーデザインにますます価値を持つようになっています。その明度軸が知覚的に均一だからです。Lの等しい数値ステップは視覚的に等しい明るさのステップを生み出します。HSLとは異なり、同じ明度変化でも異なる色相ではドラマティックに異なる結果が生じます。

これはダークモードにとって重要です。ライトモードの色のダークモード相当版を作る必要があるとき、OKLCHは正しい知覚的な明るさを持つ値を見つけやすくします。

例えば、ライトモードのミュートテキスト #64748B はOKLCHで約 oklch(53% 0.04 254) です。Lの値53%は知覚的な明度を示します。oklch(13% 0.02 254)(L=13%、ダーク背景)のサーフェスに対して、同等のコントラストを維持するために oklch(65% 0.04 254) のテキストを配置することが考えられます。これはライトモードの相当値よりわずかに明るいものです。

カラーコンバーターを使ってパレットカラーのOKLCH値を見つけ、ダークモードの対応版をデザインする際のガイドとしてそれらのL値を使いましょう。


デバイスをまたいだテスト

何をテストすべきか、その理由

ライトモードとダークモード両方のコントラスト — すべてのテキスト/背景トークンペアについて、両モードでコントラストチェッカーを実行してください。ライトモードでパスした色がダークモードでは調整が必要なことがよくあります。反転するだけで十分だと思い込まないでください。

エミュレーションだけでなく実際のデバイス — ブラウザのエミュレーションツールはデバイスのサイズをシミュレートできますが、ディスプレイの色特性を正確にシミュレートすることはできません。iPhone OLEDは、ダークモードと彩度の観点から重要な方法でWindowsラップトップのLCDとは異なる色をレンダリングします。少なくとも1台のOLEDモバイルデバイスと1台のLCDディスプレイでテストしてください。

OLEDとLCDのダーク背景 — OLEDでは、ダークネイビーの #0F172A は真のブラックレベルで非常に暗く見えます。粗悪なLCDでは、明らかに明るく見えて、視覚的な階層が変わることがあります。

キャリブレーション済みと未キャリブレーションのモニター — あなたの開発モニターはカラーキャリブレーションされているかもしれませんが、ユーザーのモニターはほぼ確実にされていません。色は未キャリブレーションのディスプレイではより暖かく、より冷たく、より彩度が高く、または低く見えます。CSSでこれを修正することはできませんが、正確な色相精度に依存する非常に微妙な色の区別をデザインすべきでない理由を教えてくれます。

ハイコントラストモード — Windowsでハイコントラストブラックとハイコントラストホワイトの両方を有効にしてテストしてください。OSがカラーオーバーライドを適用した後、すべてのインタラクティブ要素が機能し、すべてのテキストが読みやすいことを確認してください。

最低限のデバイスマトリクス

デバイス ディスプレイタイプ テストすべき理由
iPhone 14 Pro以降 OLED ダークモードの鮮やかさ、ブラックレベル
Androidフラッグシップ(Samsung Galaxy) OLED iOSとの色レンダリングの違い
MacBook(2019年以降) P3 LCD ワイドガモット、macOSカラー管理
Windowsラップトップ(標準的) sRGB LCD ほとんどのユーザーが持つベースライン
Windowsデスクトップ(モニター) sRGBまたはワイドガモットIPS カラー精度の参照

まとめ

  • 異なるディスプレイ技術(LCD、OLED、IPS、Mini-LED)は同じ16進数値でも、ブラックレベル、彩度、鮮やかさが異なってレンダリングされます。ブラウザのエミュレーションだけでなく、複数の実物のデバイスでテストしてください。
  • prefers-color-scheme はダークモード適応の主要なメディアクエリです。CSSカスタムプロパティ(トークン)を抽象化レイヤーとして使用することで、テーマが変わってもコンポーネントを更新する必要がなくなります。
  • color-gamut: p3color(display-p3 ...) のCSS関数を使ってワイドガモットディスプレイにより鮮やかな色をターゲットにでき、標準ディスプレイのsRGBへの自動フォールバックも機能します。
  • prefers-contrast: moreforced-colors: active はそれぞれハイコントラストモードとWindowsのハイコントラストモードに対応します。これらの設定が必要なユーザーは、それなしではあなたのプロダクトを使えません。
  • OKLCH は知覚的に均一なパレットをデザインするための最も実用的なカラースペースです。その明度軸はすべての色相にわたって真に均一なため、ライトモードパレットのダークモード相当版を計算するときに有用です。
  • ライトモードとダークモード両方でコントラストを検証してくださいコントラストチェッカーを使って。ライトモードでパスした色がダークな文脈に移すと失敗することがよくあります。
  • カラーコンバーターを使って、ダークモードの対応版をデザインしたり、16進数、HSL、OKLCH表現の間で変換したりするときにカラースペースをまたいで作業しましょう。
  • セマンティックトークンアーキテクチャ、つまりコンポーネントで #1E293B ではなく --text-primary を参照することが、あらゆるレスポンシブカラーシステムのスケーラブルな基盤です。トークンが変わるのであって、コンポーネントではありません。

関連カラー

関連ブランド

関連ツール