色空間とワイドガモット(CSS Color 4とディスプレイP3)
なぜP3対応画面でだけ色が鮮やかになるのかを原理から説明でき、oklchやcolor()でsRGBの外側まで安全に指定できるようになる。色域・色空間・知覚均等とHDR表示の扱いを内部動作から解き明かします。
- 1.色域(ガモット)は再現できる色の範囲、色空間はその数値の解釈規則。sRGBは標準的な狭い三角形、Display-P3はその約1.25倍広い三角形、Rec2020はさらに広い。同じrgb値でも色空間が違えば別の色を指す。
- 2.color(display-p3 …)はP3の原色を基準に色を指定し、対応画面ではsRGBの外側の鮮やかな色を出せる。oklch()はOKLab由来の知覚均等な色空間で、明度を保ったまま色相を回すなど直感どおりの操作ができる。
- 3.@media (color-gamut: p3)で画面の表示能力を分岐でき、color-gamut: srgbの環境にはsRGB値、p3の環境には広色域値を出し分ける。HDRはガモットではなく輝度範囲の話で、dynamic-rangeメディア特性で別に扱う。
なぜ同じ#ff0000でも画面ごとに赤さが違うのか
color: #ff0000 と書いたとき、ブラウザが画面に出す「赤」は1つに決まりません。同じRGBの数値でも、画面が再現できる色の範囲が違えば、出てくる赤の鮮やかさが変わるからです。ここを原理から理解する鍵が、色域(ガモット)と色空間という別物の2概念です。色域は「再現できる色の集合の広さ」、色空間は「数値をどの色として解釈するかの規則」を指します。両者を混同すると、color(display-p3 1 0 0) がなぜ普通の赤より鮮やかなのか、oklch() がなぜ明度を保てるのかが説明できません。本稿では sRGB / Display-P3 / Rec2020 の色域差から、CSS Color 4 の color() と oklch()、color-gamut メディア特性、そして HDR との切り分けまでを内部動作の順に整理します。色の数値が最終的にどう確定するかは CSS値の計算過程 と地続きの話です。
色域と色空間 ― 三角形と座標系
人が見える色全体は、CIE xy色度図という馬蹄形の領域で表せます。ディスプレイは赤・緑・青の3原色を混ぜて色を作るため、再現できる色は3原色を頂点とする三角形の内側に限られます。この三角形が**色域(ガモット)**です。原色がより純粋(彩度が高い)なほど三角形は大きくなり、より多くの色を出せます。
| 色域 | 三角形の広さ(目安) | 原色の位置 | 主な用途 |
|---|---|---|---|
| sRGB | 基準(最も狭い) | 標準的なRGB原色 | 従来のWeb・一般的な画面 |
| Display-P3 | sRGB比 約1.25倍の面積 | より純粋な赤・緑 | 近年のスマホ・ノートPC・有機EL |
| Rec2020 | P3よりさらに広い | ほぼ単色光に近い原色 | HDR動画・最新の広色域表示 |
ここで重要なのは、色空間は「三角形の頂点位置 + 数値の解釈規則」をセットで定めるという点です。rgb(255 0 0) は「赤原色を最大、緑青を0」という指示ですが、その赤原色が sRGB の頂点なのか P3 の頂点なのかで、画面に出る色は物理的に別物になります。つまり同じ 1 0 0 でも、色空間が変われば指している色が変わります。さらに sRGB は数値と光量が比例しないガンマ補正(おおむね2.2乗相当の非線形変換)を含み、128 は物理光量の半分ではありません。色空間とはこの「原色位置・白色点・非線形変換(トランスファ関数)」の総体を指します。
色管理(カラーマネジメント)のない環境では、sRGB前提の 255,0,0 を P3画面が「P3の赤」として表示してしまい、本来より過飽和な赤になります。ブラウザは画像やCSS色にカラープロファイルを適用してこれを補正しますが、プロファイル無指定の画像や未対応経路では崩れます。だからこそ「どの色空間の値か」を明示することが広色域時代の前提になります。
sRGBの外へ ― color()関数とdisplay-p3
従来のCSSの rgb() / #rrggbb は sRGB色空間に固定されており、定義上 sRGB の三角形の外側の色は表現できません。P3画面が出せる「sRGBより鮮やかな赤」を指定する手段がそもそも無かったわけです。これを解いたのが CSS Color 4 の color() 関数で、色空間を明示してその座標系で色を指定できます。
.brand {
/* sRGBの最大赤。P3画面でもsRGBの三角形内に留まる */
color: #ff0000;
/* P3の赤原色。sRGBの外側で、対応画面ではより鮮やかに出る */
color: color(display-p3 1 0 0);
}
color(display-p3 1 0 0) は「P3色空間の赤を最大」という意味で、これは sRGB の三角形の外にある色です。後に書いた宣言が優先されるため、P3非対応のブラウザは color() 行を解釈できず、直前の #ff0000 にフォールバックします。この「無効な宣言は無視され、直前の有効値が残る」挙動はカスケードの基本で、詳しくは CSS値の計算過程 の通りです。
色がガモットの外に出た場合の扱いも仕様化されています。指定値が画面の色域に収まらないとき、ブラウザはガモットマッピング(gamut mapping)でその色域内の最も近い色へ寄せます。CSS Color 4 はこの写像を OKLCh 空間上で彩度(chroma)を落とす方式で規定しており、明度と色相をできるだけ保ったまま再現可能な色に丸めます。color(display-p3 …) で範囲外を指定しても、画面の能力に応じて破綻なく表示されるのはこのためです。
oklch() ― 知覚均等な色空間
color() がガモットの拡張だとすれば、oklch() は操作のしやすさを解く関数です。従来の hsl() は計算が単純な反面、同じ明度パラメータでも色相によって人の感じる明るさが大きくぶれます(黄色は明るく、青は暗く見える)。これは HSL が sRGB を機械的に変換しただけで、人の知覚に沿っていないためです。
oklch() は OKLab という知覚均等を狙って設計された色空間に基づきます。3つの軸はそれぞれ独立した意味を持ちます。
| 軸 | 意味 | 範囲の目安 |
|---|---|---|
| L(lightness) | 知覚的な明度 | 0%(黒)〜100%(白) |
| C(chroma) | 鮮やかさ(彩度に相当) | 0(無彩色)〜実質0.4程度まで |
| H(hue) | 色相(角度) | 0〜360deg |
:root {
/* Lを固定し色相だけ回すと、明るさの揃ったパレットになる */
--primary: oklch(0.65 0.2 250); /* 青 */
--accent: oklch(0.65 0.2 20); /* 赤。明度は青と同じに感じる */
/* Cを上げるとP3でしか出せない鮮やかさに踏み込める */
--vivid: oklch(0.65 0.37 150);
}
OKLab の利点は実務に直結します。第一に、L を固定して H だけ変えれば知覚上の明るさが揃ったパレットを作れ、コントラスト比の管理が安定します。第二に、2色間の補間(グラデーションやアニメーション)が知覚的に滑らかになり、hsl で起きがちな途中のくすみ(グレーを通る現象)を避けられます。第三に、oklch() は C を大きく取ればsRGBの外まで自然に指定できるため、color(display-p3 …) を直接書かずとも広色域へ踏み込めます。範囲外なら前述のガモットマッピングで丸められます。
#ff0000、color(display-p3 …)、oklch(…) はどれも、ブラウザ内部では色域に依存しない共通の色(実装上は拡張範囲の浮動小数RGB)へ変換され、最後に出力先の画面の色空間へマッピングされます。だからこそ異なる記法どうしの補間や color-mix() が破綻なく定義できます。記法はあくまで入力で、最終色は出力先の能力が決めます。
color-gamutメディア特性で出し分ける
画面が広色域に対応しているかは @media (color-gamut: …) で問い合わせできます。srgb / p3 / rec2020 の3段階があり、指定した色域を画面がおおむねカバーしていれば真になります(P3画面は通常 color-gamut: srgb も p3 も真になります)。これを使えば、対応環境にだけ広色域値を渡す出し分けが書けます。
.hero {
/* 既定はsRGB。全環境で確実に表示される */
background: #1763ff;
}
@media (color-gamut: p3) {
.hero {
/* P3画面でだけ、sRGBの外まで踏み込んだ鮮やかな青に */
background: color(display-p3 0.09 0.39 1);
}
}
ただし color() 自体がフォールバックを持つため、多くの場合このメディアクエリは必須ではありません。color(display-p3 …) を直接書けば、非対応ブラウザは直前のsRGB宣言に自動で落ちます。color-gamut が要るのは、対応状況によってレイアウトや画像素材ごと切り替えるなど、色値の単純フォールバックでは足りないケースです。
color-gamut: p3 が真でも、画面の個体差・キャリブレーション・周囲光までは保証されません。色の正確さが要件なら、メディアクエリ任せにせず、出力にカラープロファイルを埋め込んで色管理に委ねるべきです。color-gamut は「広色域値を出してよい画面か」の目安であって、色の一致を約束するものではありません。
ワイドガモットとHDRは別物
最後に最も誤解されやすい切り分けです。ワイドガモット(広色域)と HDR(ハイダイナミックレンジ)は別の軸で、混同するとメディアクエリを取り違えます。
| 観点 | ワイドガモット | HDR |
|---|---|---|
| 広げる対象 | 色域(色の鮮やかさの範囲) | 輝度(明暗のダイナミックレンジ) |
| 代表規格 | Display-P3 / Rec2020 | Rec2020 + PQ/HLGなどの輝度伝達関数 |
| CSSの問い合わせ | @media (color-gamut: p3) | @media (dynamic-range: high) |
| 効果 | より純粋で鮮やかな色が出る | ハイライトがより明るく出る |
色域は xy色度図上の「三角形の広さ」、HDR は z軸方向の「輝度の天井の高さ」に当たり、直交します。CSS では HDR 能力を @media (dynamic-range: high) で別に問い合わせます。Rec2020 が両方の文脈で出てくるため混同されますが、Rec2020 は色域の規格であり、HDR はそこに PQ/HLG のような輝度のトランスファ関数を組み合わせて初めて成立します。なお色域や輝度の指定が最終的に画素値となって画面へ届くまでの段は ピクセルパイプライン、画像側の色の扱いは 画像のデコードとレンダリング で補完してください。
「HDR対応だから鮮やかな色を出す」と考えて @media (dynamic-range: high) で color(display-p3 …) を出し分けるのは誤りです。HDR対応でも色域はsRGB止まりの画面があり得ますし、その逆もあります。色の鮮やかさは必ず color-gamut で、明るさの範囲は dynamic-range で分岐してください。
まとめ
色域(ガモット)は再現できる色の三角形の広さ、色空間は数値をどの色として解釈するかの規則で、別概念です。rgb() / #rrggbb は sRGB に固定され、Display-P3 や Rec2020 の鮮やかな色を出せません。CSS Color 4 の color(display-p3 …) は色空間を明示して sRGB の外側を指定でき、非対応ブラウザは直前のsRGB宣言へ自動フォールバックします。範囲外の色は OKLCh 上で彩度を落とすガモットマッピングで安全に丸められます。oklch() は知覚均等な OKLab に基づき、L を保ったまま H を回す・滑らかに補間する・C を上げて広色域へ踏み込む、といった直感どおりの操作ができます。対応画面の判定は @media (color-gamut: p3) ですが、color() 自体がフォールバックを持つため必須ではありません。最後に広色域と HDR は別軸で、色域は color-gamut、輝度範囲は dynamic-range で分岐します。色値の確定過程は CSS値の計算過程、画面到達までは ピクセルパイプライン を参照してください。
Web/フロントエンド Article
色空間とワイドガモット(CSS Color 4とディスプレイP3)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
色空間
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 6
導入後に効く点
color(display-p3 …)はP3の原色を基準に色を指定し、対応画面ではsRGBの外側の鮮やかな色を出せる。oklch()はOKLab由来の知覚均等な色空間で、明度を保ったまま色相を回すなど直感どおりの操作ができる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 6
判断チェックリスト
- 自社の用途が「色空間 / CSS Color 4」に近いか確認する。
- 強みである「色域(ガモット)は再現できる色の範囲、色空間はその数値の解釈規則。sRGBは標準的な狭い三角形、Display-P3はその約1.25倍広い三角形、Rec2020はさらに広い。同じrgb値でも色空間が違えば別の色を指す。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。