[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
見出し画像

ユーザー選択色を基準にしたカラースケール生成の課題と解決策

2024年4月、カラーミーショップではショッピングカートで「デザイン設定」という機能が利用できるようになりました。

新しいショッピングカートが「デザイン設定」に対応しました | お知らせ・最新情報 カラーミーショップ 無料で本格的なネットショップ作成サービス

この機能は、ショップオーナーがカート画面の各パーツに対して、色等をいくつか自由に設定することができる機能です。
ショップオーナーが色をカスタマイズすることで、ブランドのテーマカラーをカート画面にも反映し、統一感のあるショッピング体験を提供できます。

本記事では、裏話としてこの機能を開発する過程で直面した課題とその解決策について、色空間やSassの話を交えて説明したいと思います。

カラースケールを生成する

今回提供した色をカスタマイズできる機能は、ユーザーが選んだ色に応じてカラースケールを作成し、それぞれのパーツに割り当てるという仕組みになっています。例えばボタンの背景色を決めたら、まずはその背景色をベースの色として、カラースケールを生成します。そして、ボタンの陰部分の色やホバー時の色等がカラースケールから指定されるようになっています。

カラースケールを生成するにあたり、参考になると考えたのが、デザインシステムにおけるカラーパレットの生成プロセスです。

"Design Systems デジタルプロダクトのためのデザインシステム実践ガイド"には、以下の様に記載されています。

同じ色に2つ以上のバリエーションがある場合は、基準値を指定して、基準より20%明るい色、基準より20%暗い色などのように、追加の色調を指定するとよいでしょう。

Design Systems デジタルプロダクトのためのデザインシステム実践ガイド

この機能のカラースケールも、上記に倣い基準値から一定のルールで階調を調整してバリエーションを生成することにしました。しかしながら、デザインシステムのカラーパレット生成プロセスと違う箇所は、その基準値を設定するのは私たちではなく、ユーザーであるという点です。

ユーザーが色を選択できることの難しさ

はじめ、自分はHSL色空間における輝度(L)を変化させることでカラースケールを生成すれば良いかなと考えていました。
HSL色空間とは、色の種類(H)、鮮やかさ(S)、輝度(L)を直感的に把握できるようにした色空間のため、カラースケールの階調を表現するのに、この輝度(L)を変化させることが理にかなっていると思ったからです。
しかしながら、これには問題がありました。ユーザーが極端に暗い(もしくは明るい)色を基準色として設定した場合、カラースケールが望ましいものにならなかったのです。

例えば、ユーザーが暗い緑色を選んだら、その色を基準としたカラースケールが必要となります。その時に意図しない色の強いスケールが表示されるようになってしまいました。

図1は、HSL色空間において、暗い緑(rgb(0, 34, 19))から白(rgb(255, 255, 255))までのグラデーションを示した図です。一見すると良さそうに見えますが、ユーザーが選んだ色が暗い緑だったことを考えると、中間に示される色味の強い緑はまるで違う色の様にも見えます。

図1: HSL色空間において、暗い緑から白までのグラデーションを直線的に示した図。中間の色は暗い緑と比較すると色が強く見えてしまう。
図1: HSL色空間において、暗い緑から白までのグラデーションを直線的に示した図。
中間の色は暗い緑と比較すると色が強く見えてしまう。

この現象はHSL色空間を双円錐上にしたもの(HSL conic)で確認するとよりわかりやすくなります。HSL conicは、明るさ100%の白と明るさ0%の黒をそれぞれ一点にすることで、輝度変化と彩度変化の影響を視覚的にわかりやすくした色空間です。

図2: 図1のグラデーションを、双円錐状にしたHSL色空間(HSL conic)において確認した図。
図2: 図1のグラデーションを、双円錐状にしたHSL色空間(HSL conic)において確認した図。

この色空間上で先ほどのグラデーションを並べると、明らかに中間の地点で色の強さが突出していることがわかります。

そこで、このHSL conic色空間上で直線的にグラデーションになるように調整してみます。

図3: HSL conic色空間において、暗い緑から白までのグラデーションを直線的に配置した図。中間に色の強さが突出しているような箇所は見受けられない。
図3: HSL conic色空間において、暗い緑から白までのグラデーションを直線的に配置した図。
中間に色の強さが突出しているような箇所は見受けられない。

すると、中間の色も極端に色が強く出ることはなくなりました。

ちなみにこれをHSL色空間で改めて見てみると以下の様にグッと彩度を落とす様な曲線を描きます。

図4: 図3のグラデーションを円筒状のHSL色空間に配置した図。彩度を急激に落とす様な曲線を描く。
図4: 図3のグラデーションを円筒状のHSL色空間に配置した図。
彩度を急激に落とす様な曲線を描く。

ということで、図3に近しいグラデーションを生成できるようにカラースケールは作成されるべきということがわかりました。
上記の様なカラースケールを作成するには、sRGB色空間において白や黒を目的の色に混ぜるような生成方法が近いです。先ほどのHSL conic色空間上で直線的に配置したグラデーションをsRGB色空間において表現した図が以下です。こちらも変わらず直線的に表現されるため、白を混ぜていけば、HSL conicと似たスケールを得られることがわかります。

図5: 図3のグラデーションをsRGB色空間に配置した図。sRGB色空間でも直線的に配置されている。
図5: 図3のグラデーションをsRGB色空間に配置した図。
sRGB色空間でも直線的に配置されている。

また、このsRGB色空間で白黒を混ぜる手法は、今までのショッピングカートのカラースケールの生成方法でもあるので、親和性が高いという面もあり、この生成方法を採用しました。

カラースケールを実装に落とし込む

上記の様な検討を経て、ユーザーが選んだ色に応じてカラースケールを作成する方針が決まったので、次はこれを実装に落とし込みます。

今までのショッピングカートのカラースケールの生成方法では、もともとSassのmix関数でカラースケールを実現していました。

Sass: sass:color

しかし、ここでもユーザーが基準色を決めるという要件が課題を生みました。

SassはコンパイルしてCSSを生成する都合上、このmix関数を使用する時点で基準色が決まってないといけません。しかしその基準色はユーザーが決めるため、コンパイル後に定まることになってしまいます。これではmix関数は使えません。

調査したところ、CSSのcolor-mix関数が代替できそうでした。

color-mix() - CSS: カスケーディングスタイルシート | MDN

CSSの関数はブラウザが要素を表示する前段階で、スタイルを計算する際に実行されます。そのためユーザーが色を決めた後に計算することができます。

しかしながら今度は、そのCSS関数が比較的新し目がゆえに、対応していないブラウザが多くなってしまう懸念が有りました。

調べると、このカートでは数%がcolor-mix関数に対応していないことがわかりました。カート画面はその性質上、不特定多数がアクセスする画面です。たとえ数%でも看過できません。

そのため、フォールバック対応をすることになりました。

before

.element {
  box-shadow: $color-primary-600 0 2px 0;
  color: $color-primary-100;
  background-color: $color-primary-500;
}

今までは、自分たちが定義したカラースケールのSass変数をそのまま利用する形でした。これをユーザーが選んだ基準色をベースとしたカラースケールのCSS変数を適用するように切り替えていきます。

after

まずは、カラースケールの生成です。
ユーザーが選択した色がCSS変数に入っているとして、大元となるSass変数を定義します(入っていない場合、var関数の二番目の引数である代替値が利用されます)。
これを基準値として、スケールを作成していきます。スケールは上述のとおり、sRGB色空間において白黒を混ぜる方式でCSSのcolor-mix関数で宣言していきます。

$color-primary: var(--custom-color-primary, #2169f3);

$color-primary-100: color-mix(in srgb, $color-primary 30%, #fff);
$color-primary-200: color-mix(in srgb, $color-primary 50%, #fff);
$color-primary-300: color-mix(in srgb, $color-primary 70%, #fff);
$color-primary-400: color-mix(in srgb, $color-primary 85%, #fff);
$color-primary-500: $color-primary;
$color-primary-600: color-mix(in srgb, $color-primary 90%, #000);
$color-primary-700: color-mix(in srgb, $color-primary 80%, #000);
$color-primary-800: color-mix(in srgb, $color-primary 70%, #000);
$color-primary-900: color-mix(in srgb, $color-primary 65%, #000);

次にフォールバック用の色定義です。
後ろに指定したものが優先的に適用されるように、リストを作成します。

// フォールバック用の色定義
// 後に指定したものが優先的に適用される。

$list-color-primary-100: (var(--custom-color-primary-100, #bcd2fb), $color-primary-100);
$list-color-primary-200: (var(--custom-color-primary-200, #90b4f9), $color-primary-200);
$list-color-primary-300: (var(--custom-color-primary-300, #6496f7), $color-primary-300);
$list-color-primary-400: (var(--custom-color-primary-400, #4280f5), $color-primary-400);
$list-color-primary-500: (var(--custom-color-primary-500, #2169f3), $color-primary-500);
$list-color-primary-600: (var(--custom-color-primary-600, #1e5fdb), $color-primary-600);
$list-color-primary-700: (var(--custom-color-primary-700, #1a54c2), $color-primary-700);
$list-color-primary-800: (var(--custom-color-primary-800, #174aaa), $color-primary-800);
$list-color-primary-900: (var(--custom-color-primary-900, #15449e), $color-primary-900);

次にこのlistを用いて実際にフォールバック用の記述を行うmixinを作成します。

// フォールバック対応用
// listの数に応じて、宣言を複数並べるmixin

@mixin apply-fallback-colors($property, $list, $option: '') {
  @each $value in $list {
    #{$property}: $value + $option;
  }
}

これで下準備はOKです。
実際にこの仕組みを使用したいもろもろの要素の変更内容は以下のようになります。

.element {
  @include apply-fallback-colors(box-shadow, $list-color-primary-600, "0 2px 0");
  @include apply-fallback-colors(color, $list-color-primary-100);
  @include apply-fallback-colors(background-color, $list-color-primary-500);
}

ポイントは「対応後も行数が変わっていない」「変更する文字列が、既存の文字列を利用した正規表現の置換で完結できる」という点です。
この仕組みを使用したい要素の数は膨大でしたが、実際の変更作業は正規表現で一気に置換できたため、大きく工数を削減できたと思います。

上記をコンパイルすると以下のようになります。

.element {
  box-shadow: var(--custom-color-primary-600, #1e5fdb) 0 2px 0;
  box-shadow: color-mix(in srgb, var(--custom-color-primary) 90%, #000) 0 2px 0;
  color: var(--custom-color-primary-0, #fff);
  color: color-mix(in srgb, var(--custom-color-primary) 0%, #fff);
  background-color: var(--custom-color-primary-500, #2169f3);
  background-color: var(--custom-color-primary)
}

同じ宣言が複数並ぶ時は、下の宣言が優先されます。
もしも下の宣言に関してブラウザが解釈できない(color-mix関数が対応していない等)場合、上の宣言が適用されます。

こうして、ユーザーの色を基準色としてカラースケールを生成し、それをフォールバック対応しつつ実現することができました。

残る課題

上記のような対応をしてきましたが、まだ課題は残っています。主に残っている課題は2つあると考えています。

アクセシビリティ

ユーザーが自由に色を選ぶことが出来るということは、極端に明るい色や暗い色を選択することが出来るということになります。
となると、それらの色を基準色とした時に、文字色と背景色のコントラスト比が保てなくなるケースが出てきます。
対応策として、基準色を選ぶのに一定の制限(色相のみを選んでもらうようにする等)を行ったほうが良いかもしれません。
しかし、これだとユーザーが本当に設定したい色ではないものを選択することにもなりかねないため、他に解決策がないか考えたいところです。

最適な色空間

現在は既存のカラースケールと対応した形でsRGB色空間での混色を行っていましたが、より人間の知覚特性に合わせた色空間に変更した方が良いかもしれません。
最近では、OKLCH等の色空間がCSSでも使用出来るようになっているため、これをベースとして書き換える等の対応を行えると、スケール間の変化度合いが人間の知覚に近づき、かつ、異なる色相間における明度・彩度にばらつきがあるように感じる現象も緩和されるのではと思っています。
(ただ、自分の色空間についての理解が浅いので、改めて勉強してから取り組みたい課題と思っています。。)

まとめ

  • ユーザーが決めた色からカラースケールを作成するにあたり、HSL色空間における輝度で調整をしようとすると、意図していない色の強いスケールが生じる可能性がある。

    • sRGB色空間において、白や黒を混ぜるようにカラースケールを作成することで対応した。

  • Sassのmix関数では、コンパイル時点で確定していない色を扱えないため、CSSのcolor-mix関数を活用した。また、ブラウザ対応のためのフォールバックを実装した。

  • 残る課題としてアクセシビリティや最適な色空間を再選定することがあげられる。

本記事で共有した取り組みが、類似の課題に直面する方の参考となれば嬉しいです!


この記事は『GMOペパボデザイナー Advent Calendar 2024』12/8の記事です。他の方の記事、面白いと思うのでぜひぜひ読みに行ってください!


いいなと思ったら応援しよう!