新たなプロジェクト「KALEIDO-BOARD(カレイドボード)」を公開しました。

「KALEIDO-BOARD」のイメージ

徐々に混ざり合う色の美しさや変化を楽しみながら、CSS の color-mix() 関数の仕組みを理解することを目的とするウェブアプリです。

2 つのレイヤーと背景レイヤーを持っており、ブレンドモードで合成方法を変更したり、レイヤー内のスウォッチを変形することもできるので、組み合わせによって新たなインスピレーションを得ることができるかもしれません。

「KALEIDO-BOARD」を使用して作成したイメージの例

この記事では、color-mix() 関数の基本的な説明と、「KALEIDO-BOARD」アプリの機能と使い方、認識している課題などを取り上げます。

color-mix() は、その名のとおり 2 つのカラーを混合する関数で、以下のようにカラーの値に対して使用します。

color-mix() の例
:root {
  --color-link: #00f;

  --color1: color-mix(in srgb, blue, green);
  --color2: color-mix(in srgb, blue 20%, green);
  --color3: color-mix(in srgb, blue 20%, green 20%);
  --color4: color-mix(in srgb, var(--color-link) 60%, transparent);
  --color5: color-mix(in oklch, hwb(240deg 0% 0%), hsl(120deg 100% 25.1%));
  --color6: color-mix(in oklch longer hue, blue, blue);
}

color-mix() にカンマ(,)区切りで 3 つの引数を渡しますが、最初が補間に使用する色空間(カラースペース)で、残りの 2 つが混合する色です。

上記のコードで指定している値について、もう少し詳しく見ていきます。

color-mix(in srgb, blue, green)

in srgb の指定は sRGB の色空間を使用するという意味です。ここでは混合する割合を省略しているため、bluegreen50% ずつ均等に混色されます。

color-mix(in srgb, blue 20%, green)

sRGB の色空間を使用しています。blue20% としていますが、green の割合を省略しているため、残りの 80% が割り当てられます。このように、基本的には合計の割合が 100% になります。

color-mix(in srgb, blue 20%, green 20%)

1 つ前の例と比較すると、ここでは blue 20%, green 20% と指定しており、合計は 40% です。このように、合計が 100% 未満の場合には、不足している分に対してアルファ(透明度)を乗算してスケーリングします。

一方で、合計が 100% を超える指定をした場合には、100% を上限としてそれぞれの割合でスケーリングします。

color-mix(in srgb, var(--color-link) 60%, transparent)

同様に sRGB の色空間を使用しています。1 つ目の色には CSS 変数(カスタムプロパティ)の --color-link60% の割合で指定し、2 つ目には transparent を指定しているので 60% の不透明度で表示されます。

このように color-mix() は、CSS 変数に格納しているカラーの不透明度を変更するテクニックとしても活用できます。

color-mix(in oklch, hwb(240deg 0% 0%), hsl(120deg 100% 25.1%))

Oklch の色空間を使用しています。指定している色は hwb()hsl() で、すべて異なる色空間ですが、このような指定も許容されます。

ここで指定している hwb()hsl() は、あくまで点としてのカラーの情報であり、一方で in oklch は面であり、2 つの色を結ぶ線の役割を果たします。

color-mix(in oklch longer hue, blue, blue)

Oklch の色空間を使用していますが、色相補間のアルゴリズムとして longer を指定しているため、同じ blue を指定していますが予想に反した配色になります。この色相補間は線を結ぶ経路を決める方法といえます。

色相補間については後ほど改めて説明します。

color-mix() のサポート状況

この見出しのリンク

color-mix() は、ブラウザのサポート状況の指標として使用される Baseline においても「Baseline 2023(Newly available)」に定められており、すでに主要なブラウザでサポートされています。Safari においては 16.2 からサポートしています。

ただ、W3C の仕様では「CSS Color Module Level 5」に属しており、現時点では草案(Working Draft)段階である点には注意が必要です。

本アプリは以下のセクションで構成されています。

アプリの各セクションの名称を示した図。1. HEADER、2. STAGE、3. COLOR-MIX、4. BACKGROUND、5. LAYERS から構成されている
  1. HEADER
  2. STAGE
  3. COLOR-MIX
  4. BACKGROUND
  5. LAYERS
「HEADER」のスクリーンショット

「sticky」を選択すると、ステージを含めて position: sticky が適用されるため、スクロールに追従するようになります。

「size」ではカラーパレットのサイズを 200px から 600px までの範囲で変更できます。なお、値は 10 刻みで変更されます。

ステージに配置されているカラーパレットには、各ウィジェットで変更した内容が反映されます。

パレット内のスウォッチは 10 x 10 で構成されており、「COLOR-MIX」ウィジェットで指定した 4 つのコーナーの色を起点に、CSS の color-mix() によって 10 段階でブレンドされます。

カラーパレットの図

また、パレット内のスウォッチにホバーすると opacity: 0 になるのですが、このときに「LAYERS」ウィジェットで変更した値もデフォルトに戻しています。そのため、ホバー状態を解除すると再び変更した値が有効になり、独特のアニメーションが生まれます。

この仕組みによって、レイヤーの重なり方によっては波紋のような効果を出すことができます。

ホバー効果のデモ

「COLOR-MIX」では、カラーパレットの 4 つのコーナーのカラーを変更できます。この 4 つのコーナーに向かって、color-mix() を使用してほぼ均等の割合で混色されます。

「COLOR-MIX」のスクリーンショット

また、A、B、C、D それぞれに配置されているボタンで、表示のオン・オフを切り替えることができます。オフにすると近いコーナーの色が使用されるので、3 色、2 色、1 色(!)のブレンドを確認することができます。

テキストフィールドの値には制限はないので、transparent で透明を指定することもできますし、oklch(88% 0.2 185) のように sRGB の色域を超えた指定も可能です。

ただ、使用しているカラーピッカーのライブラリの制約上、ポップアップで表示されるカラーの選択は「Hex」「RGB」「HSL」に限定されます。テキストフィールドにそれ以外の色空間(カラースペース)の指定をすると NaN が表示されます(JS エラーにはなりません)。

記事の冒頭で説明した、color-mix() で色の補間方法として使用する色空間を選択します。本アプリでは 8 つの色空間のキーワードから選択できますが、仕様上はさらに多くのキーワードが存在します。

これらの色空間は「矩形(rectangular)」タイプと「極座標(polar)」タイプに分かれており、後者の場合は色相補間のアルゴリズム(<hue-interpolation-method>)を選択できます。

現時点では hslhwblchoklch の 4 つの色空間が極座標タイプです。

選択できる色相補間の方法は 4 つで、デフォルトは shorter です。色相環に対して選択した 2 つのカラーによって描かれた弧に対して、以下のアルゴリズムを選択します。

  • shorter: 短いほうの弧を使用する
  • longer: 長いほうの弧を使用する
  • increasing: 時計回りの弧を使用する
  • decreasing: 反時計回りの弧を使用する

実際のコードでは shorter huelonger hue のように、後ろに hue キーワードを付与しますが、説明をシンプルにするために省略しています。

以下は、HSL の色相環を擬似的に表現したデモで、「color 1」と「color 2」を color-mix() で均等(50%)に混色したときに、色相がどのように補間されるかを視覚化しています。

Live Demo

このデモは、お使いのブラウザでは対応していません 🙇‍♂️

1
2
45 deg
135 deg
<hue-interpolation-method>

中心のドットと、そこから伸びている罫線の先にある中間のドットには、初期の状態では以下と同等のコードが適用されています。

{
  background-color: 
    color-mix(
      in hsl shorter hue,
      hsl(45deg 100% 60%),
      hsl(135deg 100% 60%)
    );
}

上記のデモでは、「color 1」もしくは「color 2」のスライダーを動かすと色相の位置が変化します。それにともない、中心のドットおよび中間のドットのカラーも変化します。

<hue-interpolation-method> のリストから、色相補間の方法を選択することができますが、アルゴリズムの種類によって 180 度位置が変わることが確認できます。

「BACKGROUND」では、背景画像と背景色を指定できます。

「BACKGROUND」のスクリーンショット

ドロップエリアに画像ファイルを直接ドラッグ & ドロップするか、「UPLOAD」ボタンから画像ファイルをアップロードすることができます。

アップロードした画像は背景画像として指定され、その上にはレイヤーが重なっています。そのため、レイヤーのブレンドモードによっては背景画像は下に隠れたままになります。

なお、画像ファイルは URL.createObjectURL() メソッドによって Blob を作成して一時的に表示しているだけなので、外部には送信していません。

画像アップロードの下のセレクトメニューでは、アップロードした背景画像と背景色との合成方法(background-blend-mode)を変更できます。

アップロードした画像の表示サイズを選択できます。

基本的には background-size プロパティに指定する値と同じですが、zero を選択したときには background-size: 0 が指定されます。これは画像を削除せずに一時的に画像を非表示にしたいときに使用できます。

ステージの背景色を変更できます。また、「BG」ラベルが付いているボタンで表示のオン・オフを切り替えることができます。

「LAYERS」では、各レイヤーをブレンドモードで合成方法を変更したり、レイヤー内のスウォッチを変形することができます。

「LAYERS」のスクリーンショット

「BACKGROUND IMAGE」の上には 2 つのレイヤーが重なっています。

「STAGE」エリアには、背景画像の上に「LAYER-1」「LAYER-2」が重なっていることを表す図

「LAYER-1」の上に「LAYER-2」が重なっていますが、それぞれブレンドモードの変更と形状の変形(transform)ができます。また、レイヤー名の隣に配置されているスイッチで、レイヤーの表示・非表示を切り替えることができます。

「LAYER-1」と「LAYER-2」のカラー指定を個別に変更することはできません。

本アプリでは、色と色が隣接している部分で縁辺対比という現象が発生しやすくなっているため、この現象について簡単に説明します。

以下は hsl(0 0% 10%) から hsl(0 0% 90%) まで、10 段階で color-mix() で混色して明度のみに変化を加えているデモです。

Live Demo

このデモは、お使いのブラウザでは対応していません 🙇‍♂️

100 %
0 %

各色が隣接している部分に注目すると、暗い領域はより暗く、明るい領域はより明るく見えることが確認できます。

もし現象が確認できない場合には、「size」のスライダーを操作してサイズを変更してみたり、画面から少し離れて試してみてください。

また、「gap」のスライダーを操作すると各色の間に余白(距離)ができますが、そのときにはこの現象は見受けられません。

このように色と色の縁(ふち)の対比によって起こる現象を縁辺対比といい、網膜の側抑制(そくよくせい)というメカニズムによって発生していると考えられています。

「COLOR-MIX」「BACKGROUND」「LAYERS」の 3 つのウィジェットは、タイトル領域をドラッグすることで位置を移動することができます。

右上の閉じるボタンか、元の位置に表示される「RESET」ボタンを選択することで位置をリセットすることができます。

また、移動後のウィジェットには resize: horizontal が適用されるので、水平方向に対して一定量のリサイズが可能になります。

ウィジェットの移動の図。ドラッグ可能な領域、リサイズハンドル、位置をリセットするボタンが示されている。また、ウィンドウ右上のボタンからも同様にリセット可能である

この機能を利用して、ワークスペースを自由にアレンジすることができます。

ウィジェットを移動・リサイズしてワークスペースをアレンジ

この移動できるウィジェットの機能は、「CSS カスケード再入門 #1 基礎編」というブログ記事で作成したものを流用していますが、この記事で実装されている位置をリセットするときのアニメーションは、相互に影響しない独立したレイアウトでなければ難しいため、本アプリでは無効にしています。

本アプリには、現時点では実装できていない機能や問題点がいくつか存在します。以下に主要な課題をピックアップします。

変更した設定を localStorage に保存するか、JSON 形式でエクスポートできるといった編集内容を保存する機能を用意したかったのですが、まだ実装できていません。

開発工数がかかるので単に後回しにしたのがおもな理由ですが、別の理由としてアップロードした画像の保存方法をどうするかが決まっていません。

画像ファイルを dataURL に変換して保存することはできますが、localStorage の上限はオリジンごとに 5MiB までなので1、IndexedDB API を使用した保存方法を模索するか、いっそのこと画像は保存の対象外とする方向のいずれかで検討したいです。

開発当初、作成したイメージをダウンロードする機能の実装も検討していました。保存方法の選択肢としては以下が考えられます。

  • A. JS ライブラリ「html2canvas」を使用
  • B. Region Capture や Element Capture などの API を使用
  • C. SVG ファイルとして保存

「A. html2canvas」は、ブレンドモードに対応していないといった制約により、目的を達成できないため選択肢から外れました。

「B. Region Capture や Element Capture」は、実際に試していないのでそもそも実現可能なのかがわかりませんが、現時点では実験的な機能であり Chromium 系のブラウザに限定されるのと、実行時にユーザの同意も必要になるので一旦見送りました。

「C. SVG ファイルとして保存」が、もっとも現実的な方法であり、途中まで実装を試してみたのですが、画像を含める方法と、複数レイヤーにブレンドモードを適用する方法を見つけることができずに挫折しました。

今後、効果的な方法が見つかれば機能を実装するかもしれませんが、もし作成したイメージを保存したいときには、お手数ですがブラウザのスクリーンショット機能などを使用してください。

カラーピッカーには、過去のプロジェクト「RECTGRAPH」「RECTBEATS」でも使用している、「Coloris」というライブラリを使用しています。

基本的にはシンプルで使い勝手がいいライブラリなのですが、Oklch のような sRGB の色域を超えたカラーを選択できないため、表現力を制限してしまっている点が惜しいと感じています。

モバイルデバイスでの使用

見出し「モバイルデバイスでの使用」

スマートフォンのようなスクリーンサイズが限られているタッチデバイスでは、ユーザビリティ上の問題があります。

まず、ホバーした要素を一時的に非表示にすることで波紋が生まれる効果ですが、タッチデバイスでは反応こそするものの、タップした要素のみが変わるため連続的な変化による効果を楽しむことができません。

また、スワイプでスクロールした際に不意にウィジェットのタイトルをドラッグしてしまい、ウィジェットごと移動する点なども使い勝手が悪いと感じています。


細かい点を挙げれば尽きませんが、ひとまず以上が認識しているおもな課題です。

本プロジェクトは color-mix() 関数を学ぶために作成したデモに対して、あれもこれもと手を加えていくうちにできました。

ウィジェットの操作によって、色空間(カラースペース)や色相補間の特性が直感的に把握できるとともに、階層的に並んだカラーが選択した内容によって予想外に変化するのも醍醐味の 1 つです。

また、ブレンドモードの組み合わせによっても興味深い変化が生まれるので、レイヤーの合成に対する理解も深まるかもしれません。

上記で挙げたように課題も多く残っているので、今後も少しずつ改修していければと考えています。

本アプリやブログ記事の作成にあたり、以下のウェブページを参考にしました。

色相補間のデモで使用している、CSS の三角関数(Trigonometric Functions)については、以下の記事を参考にしました。

脚注

  1. ブラウザーのストレージ制限と削除基準 | MDN