Sass のカラースペース & 広色域 カラー

2024年9月11日 Miriam Suzanne 投稿

広色域カラーが Sass にやってきます!

明確にしておきましょう。oklch(…)color(display-p3 …) のような広色域 CSS カラーフォーマットは、2023年5月からすべての主要ブラウザで利用可能になっています。しかし、それ以前から、これらの新しいカラーフォーマットは Sass で許可されていました。これは Sass の私の好きな機能の1つです。ほとんどの新しいCSSは、「公式」サポートやアップデートを必要とせずにそのまま動作します。Sass が不明なCSSに遭遇すると、そのコードをブラウザに渡します。すべてをプリプロセスする必要は ありません。

多くの場合、それだけで十分です。カスケードレイヤーとコンテナクエリがブラウザで展開されたとき、Sass が行うことは何もありませんでした。しかし、新しいCSSカラーフォーマットは少し異なります。色は Sass のファーストクラスのデータ型であるため、常にそのまま渡したいとは限りません。ブラウザに送る前に、色を操作および管理したい場合がよくあり ます。

カラースペースについてすべてご存知ですか? 新しい Sass の機能にスキップしましょう!

カラーフォーマットのトレードオフカラーフォーマットのトレードオフ パーマリンク

CSSは、これまで sRGB カラーフォーマットに限定されていましたが、これには主に2つの特徴があります 

  • 色の相対的な redgreen、および blue 光の量を制御することにより、数学的に色を表現&操作するための基盤となるRGB カラーモデルを使用します。
  • 1990年代半ば以降のカラーモニターで表示できる色のデフォルト範囲である、sRGB 色域内の色のみを表すことができます 

明確な色域境界明確な色域境界 パーマリンク

CSSで以前から利用可能だったフォーマット(名前付きカラー(例:red)、hexカラー(例:#f00)、およびカラー関数(例:rgb()/rgba()hsl()/hsla()、最近ではhwb())は、すべてsRGBカラーを記述する方法です。名前付きカラーは特殊ですが、他のフォーマットは、色域の色が3D 空間に投影されたかのように「座標」システムを使用します。

sRGB gamut rendered in sRGB space forms a rainbow colored cube sRGB gamut rendered in hsl space forms a rainbow-edged cylinder with black at the bottom and white at the top sRGB gamut rendered in hwb space forms a rainbow-core top surface with a black-to-gray bottom and gray-to-white outside edge
Isaac Muse によるColorAideを使用して生成された画像。

これらの素敵な幾何学的形状を見てください! RGBは虹色の立方体を提供し、HSLHWB(それらの「極」の hue チャネルを使用)は、それらの同じ色を円柱に配置します。明確な境界線により、どの色が色域内または色域外であるかを(数学的に)簡単に知ることができます。 rgb() では、0~255 の値を使用します。その範囲内にあるものはすべて立方体の中にありますが、チャネルが 0 未満または 255 を超えると、sRGB 色域の内側にはもうありません。 hsl()hwb() では、hue 座標は逃避速度に達することなく円を周り続けることができますが、saturationlightnesswhiteness、および blackness チャネルは、0~1 または 0%~100% で明確に移動します。繰り返しますが、その範囲外のものはすべてカラースペースの外側になり ます。

人間の知覚との一致人間の知覚との一致 パーマリンク

しかし、そのシンプルさには限界があります。最も明白なのは、モニターがますます良くなっているということです。最近では、多くのモニターが sRGB を超える色、特に利用可能な明るい緑の範囲を広げて表示できます。使用可能な新しい色で形状を単純に拡張すると、もはやきれいな幾何学を扱うことはできませ ん。

display-p3 gamut rendered in sRGB space adds unequal red and green horns to the sRGB cube display-p3 gamut rendered in hsl space creates a boot-like bulge of green near the base of the hsl cylinder

sRGB フォーマットの鮮明なエッジとクリーンな数学は、表示できる色が正確にわかっていて、それらの色がボックスに完全に収まるように配置したからこそ可能でした。しかし、人間の色の知覚はそれほど明確ではなく、市販されているどのモニターの色域とも完全に一致しません。単純な数学ではなく、人間の知覚に基づいてすべての同じ色を均等に間隔をあけようとすると、エッジが急な全く異なる形状になります。これは、oklch 空間の display-p3 色域です。

display-p3 gamut rendered in oklch space forms a skewed cube with a conic black base

実際の違いは、hsloklch で同じ「明るさ」の色を比較するときに特に顕著です。人間は黄色味を帯びた色合いを青よりも明るく知覚します。同じ範囲に収まるようにスケーリングすることにより、hsl は青よりもはるかに明るい黄色を提供し ます。

on the left a blue and much brighter yellow, on the right our yellow is much darker to match the blue tone

新しい CSS フォーマットにより選択肢が生まれる新しい CSS フォーマットにより選択肢が生まれる パーマリンク

今後は、広色域 カラーで2つの方向性があります。

  • より大きく、より大きな色域を単純な座標に再調整し、クリーンで幾何学的な境界を維持するために色をストレッチするカラーフォー マット。
  • 特定の 色域に関係なく、知覚的に均一な間隔を維持するカラーフォーマット。

一方では、明確な境界線により、利用可能な色の範囲内に簡単に留まることができます。これらの境界線がないと、物理的に不可能な色を誤って要求してしまう可能性があります。一方、これらの色は他の人間に知覚されることを期待しており、読みやすい十分なコントラストを備えて、物事を見た目に一貫性のあるものにする必要があり ます。

CSS Color Module Level 4 は、多くの新しいCSSカラーフォーマットを定義しています。それらのいくつかは、特定のカラースペースへの幾何学的なアクセスを維持しています。より馴染みのある rgb()hsl() 関数と同様に、新しい hwb() 関数は、huewhiteness、および blackness チャネルを使用して、sRGB 色域の色を記述します。これは興味深いフォーマットであり、以前に書いたことがあります 

色域が境界付けられた残りのスペースは、color(<space> <3-channels> / <alpha>) 関数を使用して利用できます。この構文を使用すると、sRGBsrbg-lineardisplay-p3(最新のモニターに共通)、a98-rgbprophoto-rgb、および rec2020 で色を定義できます。これらの各々は、指定された色域を 0~1 または 0%~100% の(立方体の)座標範囲にマッピングします。非常にきれい です。

同じ color() 関数では、「デバイスに依存しない」(そして色域のない) xyz カラースペースにもアクセスできます。これは、さまざまなカラーモデル間の変換の国際的なベースラインとしてよく使用されます。ここでは白色点については詳しく説明しませんが、xyz-d65 (デフォルト) を明示的に指定するか、代わりに xyz-d50 を使用できます。

xyz から外に向かって作業すると、多くの新しい理論的に境界のないカラーフォーマットが得られます。これらは、クリーンな幾何学よりも知覚的に均一な分布を優先しています。これらは、lab()lightnessa、およびb)と lch()lightnesschroma、および hue)、およびそれぞれの新しい「ok」バージョン(oklab() および oklch())を含む独自の関数で利用できます。これらの形式の完全な歴史を知りたい場合は、Eric Portis が素晴らしい 説明を書いています

TL;DR 最優先の新しいフォーマットTL;DR 最優先の新しいフォーマット パーマリンク

カラーエキスパートにとっては、これらすべての柔軟性があるのは素晴らしいことです。私たちのようなその他大勢の人にとっては、いくつかの優れた フォーマットがあります。

  • color(display-p3 …) は、多くの最新ディスプレイで利用可能なより広い色域の色へのアクセスを提供し、色域の明確なセットを維持します 境界。
  • oklch(…) は、hsl(…) の新しい代替手段であり、操作する上で最も直感的で知覚的に均一なスペースです。chromasaturation に非常によく似ています。ただし、ここにはガードレールがほとんどなく、どの画面でも表示できない色域の外に出てしまう可能性があります。座標系はまだ円柱を記述していますが、人間の知覚とディスプレイテクノロジーのエッジはそのスペースにきれいにマッピングされませ ん。
  • トランジションとグラデーションの場合、色相の間を直接移動する場合(カラーホイールを一周するのではなく)、oklab(…) は優れた線形オプションです。通常、2つの色域内の色間のトランジションまたはグラデーションは色域内に留まりますが、彩度または明度の極端な状況を扱う場合は、常にそれに頼ることはできませ ん。

Sass での CSS カラー関数Sass での CSS カラー関数 パーマリンク

Sass は、すべての新しいCSSフォーマットを受け入れ、操作、混合、変換、および検査できるファーストクラスのとして扱います。これらの関数はすべてグローバルに 利用可能です。

  • lab()oklab()lch()、および oklch()
  • sRGBsrgb-lineardisplay-p3a98-rgbprophoto-rgbrec2020xyzxyz-d65、および xyz-d50 カラースペースを使用する color()
  • hwb()(Sass には以前 color.hwb() 関数がありましたが、現在はグローバル関数を優先して非推奨になりまし た)。

Sass カラー関数は、CSS 関数と同じ構文を使用します。これは、特定の色がさまざまなスペースで表現できることを意味します。たとえば、これらはすべて同じ色 です。

プレイグラウンド

SCSS 構文

@debug MediumVioletRed;
@debug #C71585;
@debug hsl(322.2 80.91% 43.14%);
@debug oklch(55.34% 0.2217 349.7);
@debug color(display-p3 0.716 0.1763 0.5105);
プレイグラウンド

Sass 構文

@debug MediumVioletRed
@debug #C71585
@debug hsl(322.2 80.91% 43.14%)
@debug oklch(55.34% 0.2217 349.7)
@debug color(display-p3 0.716 0.1763 0.5105)

Sass の色はスペースを保持しますSass の色はスペースを保持します パーマリンク

従来、CSSとSassはどちらも、異なるカラースペースを相互に交換可能なものとして扱っていました。すべてのカラー形式が同じ基盤モデルを使用して同じ色域を記述している場合、hsl()構文を使用して色を指定すると、パーサーはデータ損失のリスクなしに、それをrgb()に積極的に変換できます。これは、現代のカラースペースではもはや当てはまりません。

一般的に、特定のスペースで定義された色は、そのスペースに残り、そのスペースで出力されます。スペースは、color()に渡される名前付きスペースのいずれか、または関数名(たとえば、lab()関数を使用して定義された色に対するlab)によって定義されます。

ただし、rgbhsl、およびhwbスペースは「レガシースペース」と見なされ、下位互換性のために特別な処理を受けることがよくあります。レガシーカラーは、利用可能な最も下位互換性のある形式で出力されます。これは、CSS自身の下位互換性の動作と一致します。16進数表記またはCSSのカラー名を使用して定義された色も、レガシーなrgbカラースペースの一部と見なされます。

Sassは、これらのカラースペースを検査および操作するためのさまざまなツールを提供しています。

  • color.space($color)を使用して、色のスペースを検査できます。
  • color.is-legacy($color)を使用して、色がレガシースペースにあるかどうかを尋ねることができます。
  • color.to-space($color, $space)を使用して、あるスペースから別のスペースに色を変換できます。

これらの関数はすべて、組み込みのSass Color Moduleによって提供されます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$brand: MediumVioletRed;

// results: rgb, true
@debug color.space($brand);
@debug color.is-legacy($brand);

// result: oklch(55.34% 0.2217 349.7)
@debug color.to-space($brand, 'oklch');

// results: oklch, false
@debug color.space($brand);
@debug color.is-legacy($brand);
プレイグラウンド

Sass 構文

@use 'sass:color'
$brand: MediumVioletRed

// results: rgb, true
@debug color.space($brand)
@debug color.is-legacy($brand)

// result: oklch(55.34% 0.2217 349.7)
@debug color.to-space($brand, 'oklch')

// results: oklch, false
@debug color.space($brand)
@debug color.is-legacy($brand)

スペース間で色を変換すると、それらの色はもはや等しいとは見なされません。ただし、color.same()関数を使用すると、それらが「同じ」色としてレンダリングされるかどうかを尋ねることができます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$orange-rgb: #ff5f00;
$orange-oklch: oklch(68.72% 20.966858279% 41.4189852913deg);

// result: false
@debug $orange-rgb == $orange-oklch;

// result: true
@debug color.same($orange-rgb, $orange-oklch);
プレイグラウンド

Sass 構文

@use 'sass:color'
$orange-rgb: #ff5f00
$orange-oklch: oklch(68.72% 20.966858279% 41.4189852913deg)

// result: false
@debug $orange-rgb == $orange-oklch

// result: true
@debug color.same($orange-rgb, $orange-oklch)

color.channel()を使用して、色の個々のチャンネルを検査できます。デフォルトでは、色のスペースで利用可能なチャンネルのみをサポートしますが、$spaceパラメーターを渡して、指定されたスペースに変換した後のチャンネルの値を返すことができます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$brand: hsl(0 100% 25.1%);

// result: 25.1%
@debug color.channel($brand, "lightness");

// result: 37.67%
@debug color.channel($brand, "lightness", $space: oklch);
プレイグラウンド

Sass 構文

@use 'sass:color'
$brand: hsl(0 100% 25.1%)

// result: 25.1%
@debug color.channel($brand, "lightness")

// result: 37.67%
@debug color.channel($brand, "lightness", $space: oklch)

CSSは、「無効」および「欠落」したカラーチャンネルという概念も導入しました。たとえば、彩度が0%hslカラーは常にグレースケールになります。その場合、hueチャンネルは無効と見なすことができます。その値を変更しても、結果の色に影響はありません。Sassでは、color.is-powerless()関数を使用して、チャンネルが無効かどうかを尋ねることができます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$gray: hsl(0 0% 60%);

// result: true, because saturation is 0
@debug color.is-powerless($gray, "hue");

// result: false
@debug color.is-powerless($gray, "lightness");
プレイグラウンド

Sass 構文

@use 'sass:color'
$gray: hsl(0 0% 60%)

// result: true, because saturation is 0
@debug color.is-powerless($gray, "hue")

// result: false
@debug color.is-powerless($gray, "lightness")

さらに進んで、CSSでは、チャンネルを「欠落」または不明として明示的にマークすることもできます。これは、grayのような色をoklchのようなカラースペースに変換する場合に自動的に発生する可能性があります。hueに関する情報がありません。noneキーワードを使用して、欠落したチャンネルを持つ色を明示的に作成し、color.is-missing()関数を使用して、カラーチャンネルが欠落しているかどうかを検査することもできます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$brand: hsl(none 100% 25.1%);

// result: false
@debug color.is-missing($brand, "lightness");

// result: true
@debug color.is-missing($brand, "hue");
プレイグラウンド

Sass 構文

@use 'sass:color'
$brand: hsl(none 100% 25.1%)

// result: false
@debug color.is-missing($brand, "lightness")

// result: true
@debug color.is-missing($brand, "hue")

CSSと同様に、Sassは欠落したチャンネルが意味を持つ場所で保持しますが、チャンネル値が必要な場合は、それらを0の値として扱います。

Sassカラーの操作Sassカラーの操作 パーマリンク

既存のcolor.scale()color.adjust()、およびcolor.change()関数は、期待どおりに動作し続けます。デフォルトでは、すべてのカラー操作は色によって提供されるスペースで実行されます。ただし、変換に明示的なカラースペースを指定することもできます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$brand: hsl(0 100% 25.1%);

// result: hsl(0 100% 43.8%)
@debug color.scale($brand, $lightness: 25%);

// result: hsl(5.76 56% 45.4%)
@debug color.scale($brand, $lightness: 25%, $space: oklch);
プレイグラウンド

Sass 構文

@use 'sass:color'
$brand: hsl(0 100% 25.1%)

// result: hsl(0 100% 43.8%)
@debug color.scale($brand, $lightness: 25%)

// result: hsl(5.76 56% 45.4%)
@debug color.scale($brand, $lightness: 25%, $space: oklch)

調整が異なるスペースで実行された場合でも、返される色は元のカラースペースで返されることに注意してください。そうすることで、ブラウザーがそれらの形式をサポートすることを必ずしも当てにせずに、oklchのようなより高度なカラースペースが役立つ場合に使い始めることができます。

既存のcolor.mix()関数も、両方の色がレガシーカラースペースにある場合は、既存の動作を維持します。レガシーミキシングは常にrgbスペースで行われます。新しい$methodパラメーターを使用して他のミキシング手法を選択できます。これは、補間メソッドを記述するためのCSS仕様に一致するように設計されています。これは、CSSグラデーション、フィルター、アニメーション、およびトランジション、さらには新しいCSScolor-mix()関数で使用されています。

レガシーカラーの場合、メソッドはオプションです。ただし、非レガシーカラーの場合、メソッドが必要です。ほとんどの場合、メソッドは単純にカラースペース名にすることができます。ただし、「極座標の色相」チャンネル(hslhwblch、またはoklchなど)を持つカラースペースを使用する場合は、カラーホイールの周りを移動する方向を指定することもできます。shorter huelonger hueincreasing hue、またはdecreasing hue

プレイグラウンド

SCSS 構文

@use 'sass:color';

// result: #660099
@debug color.mix(red, blue, 40%);

// result: rgb(176.2950613593, -28.8924497904, 159.1757183525)
@debug color.mix(red, blue, 40%, $method: lab);

// result: rgb(-129.55249236, 149.0291922672, 77.9649510422)
@debug color.mix(red, blue, 40%, $method: oklch longer hue);
プレイグラウンド

Sass 構文

@use 'sass:color'

// result: #660099
@debug color.mix(red, blue, 40%)

// result: rgb(176.2950613593, -28.8924497904, 159.1757183525)
@debug color.mix(red, blue, 40%, $method: lab)

// result: rgb(-129.55249236, 149.0291922672, 77.9649510422)
@debug color.mix(red, blue, 40%, $method: oklch longer hue)

この場合、ミックスの最初の色は「原点」の色と見なされます。上記の他の関数と同様に、ミキシングに異なるスペースを使用できますが、結果は常にその原点カラースペースで返されます。

色域境界の操作色域境界の操作 パーマリンク

特定のディスプレイの色域外に出た場合はどうなるでしょうか?ブラウザーはまだ詳細について議論していますが、誰もが何かを表示する必要があることに同意しています。

  • 現在、ブラウザーはすべての色をredgreen、およびblueチャンネルに変換して表示します。これらのチャンネルのいずれかが特定の画面に対して高すぎるか低すぎる場合、許可された最大値または最小値でクランプされます。これはしばしば「チャンネルクリッピング」と呼ばれます。これにより、計算は単純になりますが、一部のチャンネルが他のチャンネルよりも多くクリップされている場合、huelightnessの両方に奇妙な影響を与える可能性があります。
  • CSS仕様では、lightnessを維持することが最優先事項である必要があり、色が色域に入るまでchromaを減らすアルゴリズムが提供されています。これは読みやすいテキストを維持するのに優れていますが、ブラウザーにとってはより多くの作業になり、色が突然その活気を失うと驚く可能性があります。
  • chromaを減らして、色をrec2020色域内に入れるという妥協案について進展がありました。そしてそこからクリッピングします。

ブラウザーの動作はまだ信頼性が低く、一部のカラースペース(oklch)は利用可能な色域から簡単に飛び出してしまう可能性があるため、Sassで色域管理を行うと役立つ場合があります。

color.is-in-gamut()を使用して、特定の色が特定の色域内にあるかどうかをテストできます。他のカラー関数と同様に、これは色が定義されているスペースをデフォルトとしますが、$spaceパラメーターを指定して別の色域に対してテストできます。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$extra-pink: color(display-p3 0.951 0.457 0.7569);

// result: true, for display-p3 gamut
@debug color.is-in-gamut($extra-pink);

// result: false, for srgb gamut
@debug color.is-in-gamut($extra-pink, $space: srgb);
プレイグラウンド

Sass 構文

@use 'sass:color'
$extra-pink: color(display-p3 0.951 0.457 0.7569)

// result: true, for display-p3 gamut
@debug color.is-in-gamut($extra-pink)

// result: false, for srgb gamut
@debug color.is-in-gamut($extra-pink, $space: srgb)

また、color.to-gamut()関数を使用して、特定の色域内になるように色を明示的に移動することもできます。いくつかのオプションがあり、CSSが長期的に使用するデフォルトが不明確であるため、この関数には現在、明示的な$methodパラメーターが必要です。現在のオプションは、clip(現在ブラウザーによって適用されているもの)またはlocal-minde(現在指定されているもの)です。

プレイグラウンド

SCSS 構文

@use 'sass:color';
$extra-pink: oklch(90% 90% 0deg);

// result: oklch(68.3601568298% 0.290089749 338.3604392249deg)
@debug color.to-gamut($extra-pink, srgb, clip);

// result: oklch(88.7173946522% 0.0667320674 355.3282956627deg)
@debug color.to-gamut($extra-pink, srgb, local-minde);
プレイグラウンド

Sass 構文

@use 'sass:color'
$extra-pink: oklch(90% 90% 0deg)

// result: oklch(68.3601568298% 0.290089749 338.3604392249deg)
@debug color.to-gamut($extra-pink, srgb, clip)

// result: oklch(88.7173946522% 0.0667320674 355.3282956627deg)
@debug color.to-gamut($extra-pink, srgb, local-minde)

すべてのレガシーおよびRGBスタイルのスペースは、色の境界付き色域を表します。色を色域にマッピングすることは損失の多いプロセスであるため、通常はブラウザーに任せるか、慎重に行う必要があります。そのため、色域外のチャンネル値は、色域が制限されたカラースペースに変換する場合でも、Sassによって維持されます。

レガシーブラウザーでは、srgb色域の色が必要です。ただし、最新のディスプレイのほとんどは、より広いdisplay-p3色域をサポートしています。

非推奨の関数非推奨の関数 パーマリンク

多くの既存の関数はレガシーカラーに対してのみ意味があるため、color.channel()color.adjust()などのカラースペース対応関数を優先して非推奨になっています。最終的にはこれらはSassから完全に削除されますが、同じ機能はすべて更新された関数で引き続き利用できます。

  • color.red()
  • color.green()
  • color.blue()
  • color.hue()
  • color.saturation()
  • color.lightness()
  • color.whiteness()
  • color.blackness()
  • adjust-hue()
  • saturate()
  • desaturate()
  • transparentize()/fade-out()
  • opacify()/fade-in()
  • lighten()/darken()

これらのレガシー関数をカラースペース対応の関数に自動的に変換するためのマイグレーターを追加しました。

$ sass-migrator color --migrate-deps <path/to/style.scss>