調和の取れたカラーパレット生成をデザインシステムに向けて

Table of Contents

はじめに

近年、サービス全体の一貫性を確保するためにデザインシステムを構築する例が多い。この時、特に難しいのがカラーパレットの設計で、その理由は以下のような要件があるからだ。

  • カラーパレットは拡張可能であるべき
  • それぞれの配色パターン同士で調和が取れているべき
  • すべての色はアクセシビティに配慮したコントラスト比を確保できるべき

これらの要件を満たすカラーパレットをヒューリスティックに設計することは難しい。したがって、カラーパレットは何らかのアルゴリズムによって体系的に設計する必要があるだろう。

本稿では、筆者が作成したカラーパレット生成ツールで使うために考えた3つのアルゴリズムについて解説する。

用語について

本論に入る前に用語の整理をしておく。作成したツールにおいて扱う用語の定義は以下の通りである。

  • カラーパレット: 生成されたすべてのカラーパターンの一覧。
  • カラーパターン: テーマとトーンの一覧の組み合わせから生成される色の一覧。
  • テーマ: カラーパターン生成に使われる色相(Hue)などのパラメータを表すオブジェクト。
  • トーン: カラーパターンの各色の生成に使われる輝度(Luminance)と鮮やかさ(Colorfulness)のパラメータを表すオブジェクト。
cluster_themes Themes cluster_tones Tones cluster_palette Palette cluster_pattern1 Pattern cluster_pattern2 Pattern cluster_pattern3 Pattern theme1 Red Hue: 40 theme2 Green Hue: 160 theme3 Blue Hue: 280 pattern2 Green 1 Green 2 Green 3 theme2->pattern2 tone1 Tone 1 Luminance: 0.90 Colorfulness: 0.10 tone2 Tone 2 Luminance: 0.50 Colorfulness: 0.50 tone3 Tone 3 Luminance: 0.10 Colorfulness: 0.10 tone2->pattern2 pattern1 Red 1 Red 2 Red 3 pattern3 Blue 1 Blue 2 Blue 3
カラーパレット生成の関係図

輝度からカラーパレットを生成(V1)

まず、カラーパレットを生成するための基本的なパラメータとして色相(Hue)と輝度(Luminance)を考えることができる。ここでは、色相は0〜360°の単位で表される円柱状の座標で、輝度は色をグレースケール化した時の階調を表す。グレースケール化は、WCAGのコントラスト比の算出で使われる相対輝度をガンマ補正して計算する。したがって、パターン間で輝度の階調を揃えることで、結果としてコントラスト比も(完全ではないが)揃えることができる。

基準となる色空間には、HSV色空間を採用する。HSV色空間では色相はHue成分として指定することができるが、輝度を直接指定することはできない。したがってまずは、ある色相において輝度の条件を満たす、彩度(Saturation)と明度(Value)の組み合わせを列挙することから始める。輝度の候補としては以下の値を採用した。

{L} ={\begin{pmatrix}0.95&0.87&0.78&0.68&0.56&0.45&0.36&0.28&0.21&0.15\end{pmatrix}}
0.00.20.40.60.81.0Saturation0.00.20.40.60.81.0ValueRed(0°)Green(120°)Blue(240°)
輝度の条件を満たす彩度と明度の組み合わせ

上の図から、輝度の条件を満たすの曲線(以降、輝度適応曲線と呼ぶ)は色相によって傾きが異なることがわかる。青の傾きが最も大きく曲線で、緑は低輝度においてはほぼ直線になる。ここからそれぞれの適応曲線上の位置Tの色を選んでカラーパレットを生成してみる。位置Tは輝度ごとに以下の値とした。

T={\begin{pmatrix}0.96&0.92&0.88&0.84&0.80&0.76&0.72&0.68&0.64&0.60\end{pmatrix}}

表の各項右の数値は左から順に黒(#000)と白(#FFF)に対するWCAGのコントラスト比、輝度、後述する視覚上の鮮やかさを表すColorfulnessを示す。

Red 11:18.94/L95/C6
Green 11:18.94/L95/C18
Blue 11:18.96/L95/C5
Red 21:15.53/L87/C16
Green 21:15.57/L87/C63
Blue 21:15.64/L87/C13
Red 31:12.43/L78/C25
Green 31:12.32/L78/C80
Blue 31:12.50/L78/C22
Red 41:9.40/L68/C36
Green 41:9.47/L68/C67
Blue 41:9.47/L68/C29
Red 51:6.53/L56/C51
Green 51:6.52/L56/C53
Blue 51:6.53/L56/C36
Red 64.73:1/L45/C65
Green 64.76:1/L45/C40
Blue 64.73:1/L45/C39
Red 76.64:1/L36/C49
Green 76.61:1/L36/C31
Blue 76.69:1/L36/C41
Red 89.17:1/L28/C35
Green 89.33:1/L28/C22
Blue 89.19:1/L28/C47
Red 912.21:1/L21/C24
Green 912.24:1/L21/C16
Blue 912.15:1/L21/C31
Red 1015.05:1/L15/C16
Green 1015.19:1/L15/C10
Blue 1015.15:1/L15/C20
輝度の条件を満たす曲線から任意の位置Tを選択したカラーパレット(V1)

それぞれの色相のカラーパターンは、輝度の階調が一致するものを選んだので、想定通りコンスラスト比もある程度は揃っているのがわかる。一方で、緑のパターンの低輝度の部分は特に、他と比べて鮮かに際立ってって見える。これは緑の場合の輝度適応曲線の傾きが小さいため、高彩度の色が選ばれてしまうからだ。このことはカラーパレットをHSV空間の彩度と明度にマッピングした以下の図から読み取れる。

0.00.20.40.60.81.0Saturation0.00.20.40.60.81.0ValueRed(0°)Green(120°)Blue(240°)
カラーパターン(V1)のHSV色空間における彩度と明度

では、色相によって視覚上の鮮かさが異なってしまう問題は、どのように解決すればいいだろうか?まず考えたのは輝度適応曲線の傾きによって、位置Tを補正することだ。結論から言うとこの方法は採用しなかった。なぜなら、そもそも補正値をどのような値にすべきか、さらに低輝度の場合と高輝度の場合で補正の方向を変化させなければならない(傾きが小さい色の彩度を抑える方向で補正すると、今度は逆に高輝度で傾きの色の視覚的な鮮かさが相対的に大きくなってしまう)等、パラメータの調整が難しかったからだ。

そこで考えたのが、視覚上の鮮やかさを数値化して輝度と同様に揃える方法だ。

輝度と視覚上の鮮やかさからカラーパレットを生成(V2)

視覚上の鮮やかさ(以降Colorfulness)とは本稿で独自に定義する数値で、HSV色空間のSaturationとは異なる。具体的には、HWB色空間におけるWhiteness成分とBlackness成分を足した値を1から引いたものをColorfulnessとしている。Colorfulnessは0〜1までの数値を取り、1に近付くにつれより視覚的に鮮やかに見えるようになる。計算式は以下の通りとなる。

{\begin{aligned} W&=(1-S)V\\B&=(1-V)\\C&=1-(W+B) \end{aligned}}

Colorfulnessの例として、HSV色空間でSaturationを1で固定、Valueを1〜0.1まで変化させた場合、その値は以下の表のようになる。表の各項の左側がHSVの各成分で、右側がColorfulnessになっている。ここから、確かにColorfulnessが視覚上の鮮やかさと一致しているのがわかる。

H0 S100 V100100
H120 S100 V100100
H240 S100 V100100
H0 S100 V9090
H120 S100 V9090
H240 S100 V9090
H0 S100 V8080
H120 S100 V8080
H240 S100 V8080
H0 S100 V7070
H120 S100 V7070
H240 S100 V7070
H0 S100 V6060
H120 S100 V6060
H240 S100 V6060
H0 S100 V5050
H120 S100 V5050
H240 S100 V5050
H0 S100 V4040
H120 S100 V4040
H240 S100 V4040
H0 S100 V3030
H120 S100 V3030
H240 S100 V3030
H0 S100 V2020
H120 S100 V2020
H240 S100 V2020
H0 S100 V1010
H120 S100 V1010
H240 S100 V1010
HSV色空間において彩度が同一で明度のみが異なる場合のColorfulness

では、実際に輝度とColorfulnessを使ってカラーパレットを生成することを考える。それには、先程の輝度適応曲線はそのまま使って、目標とするColorfulnessに最も近い値が得られる位置Tを選択すればいい。Colorfulnessの候補としては以下の値を採用した。

C={\begin{pmatrix}0.08&0.16&0.26&0.38&0.50&0.50&0.38&0.26&0.16&0.08\end{pmatrix}}

これらColorfulnessの候補から生成したカラーパレットが以下の表だ。

Red 11:18.94/L95/C6
Green 11:18.75/L95/C8
Blue 11:18.96/L95/C5
Red 21:15.53/L87/C16
Green 21:15.49/L87/C16
Blue 21:15.63/L87/C14
Red 31:12.47/L78/C26
Green 31:12.32/L78/C26
Blue 31:12.43/L78/C24
Red 41:9.46/L68/C38
Green 41:9.38/L68/C38
Blue 41:9.26/L68/C36
Red 51:6.50/L56/C50
Green 51:6.55/L56/C50
Blue 51:6.42/L56/C50
Red 64.73:1/L45/C50
Green 64.83:1/L45/C49
Blue 64.79:1/L45/C49
Red 76.78:1/L36/C38
Green 76.72:1/L36/C38
Blue 76.67:1/L36/C39
Red 89.25:1/L28/C26
Green 89.11:1/L28/C26
Blue 89.27:1/L28/C26
Red 912.25:1/L21/C16
Green 912.27:1/L21/C16
Blue 912.23:1/L21/C16
Red 1015.04:1/L15/C8
Green 1015.01:1/L15/C8
Blue 1015.18:1/L15/C8
輝度適応曲線からColorfulnessが任意の値に近い位置を選択したカラーパレット(V2)

このカラーパレットは最初のものとは違って、輝度だけではなくColorfulnessの値も揃っているのがわかる。その結果、視覚的に調和が取れた理想的なものすることができた。

このカラーパレットを前節と同じくHSV色空間の彩度と明度にマッピングすると以下の図になる。これを見ると輝度適応曲線の傾きを考慮して、上手く補正されているのがわかる。

0.00.20.40.60.81.0Saturation0.00.20.40.60.81.0ValueRed(0°)Green(120°)Blue(240°)
カラーパターン(V2)のHSV色空間における彩度と明度

Lch色空間を利用してカラーパレットを生成(V3)

前節の方法で既に完成系となるカラーパレットを作ることはできたが、別解としてLch色空間を使う方法もある。

Lch色空間を使うことで輝度はLuminance成分(前節で使っていたグレースケール階調とはわずかに異なる)を、色相はHue成分をそのまま使用できる。したがって、LuminanceとHueは定数になるので、カラーパターンの色を選択をColorfulnessが任意の値を取るChromaを見付けるということに集約できる(Colorfulnessを得るためにHWB色空間への変換が必要となるのでこの点は面倒だが)。さらに、Lch色空間を使うことで色相をより人間の視覚に近似した形で選択できるようになる(HSV色空間のHue成分は緑の範囲が広く見える等、人間の視覚的に均一ではない)。

この方法で作成したカラーパレットは以下のようになる。なお、Hue成分はHSV色空間の場合と近似するように調整してある。前節の結果と大きな変化はないが、HSV色空間で見た時に、同じパターンの色同士のHue成分がわずかに異なるという違いがある。

Red 11:18.52/L95/C8
Green 11:18.52/L95/C8
Blue 11:18.52/L95/C8
Red 21:15.00/L87/C16
Green 21:15.00/L87/C16
Blue 21:15.00/L87/C16
Red 31:11.64/L78/C26
Green 31:11.64/L78/C26
Blue 31:11.64/L78/C26
Red 41:8.59/L68/C38
Green 41:8.59/L68/C38
Blue 41:8.59/L68/C38
Red 51:5.78/L56/C50
Green 51:5.78/L56/C50
Blue 51:5.78/L56/C50
Red 65.37:1/L45/C50
Green 65.37:1/L45/C50
Blue 65.37:1/L45/C50
Red 77.50:1/L36/C38
Green 77.50:1/L36/C38
Blue 77.50:1/L36/C38
Red 810.04:1/L28/C26
Green 810.04:1/L28/C26
Blue 810.04:1/L28/C26
Red 912.73:1/L21/C16
Green 912.73:1/L21/C16
Blue 912.73:1/L21/C16
Red 1015.20:1/L15/C8
Green 1015.20:1/L15/C8
Blue 1015.20:1/L15/C8
Lch色空間を利用したカラーパレット(V3)
0.00.20.40.60.81.0Saturation0.00.20.40.60.81.0ValueRed(0°)Green(120°)Blue(240°)
カラーパターン(V3)のHSV色空間における彩度と明度

おわりに

ここまで、カラーパレットを生成するアルゴリズムを3つ解説したが、今回作成したツールでは最後に紹介したLch色空間を使う方法を採用している。

なお本ツールでは、本稿で説明した色相、輝度、Colorfulness以外にも、テーマごとにColorfulnessのオフセットとスケールを設定できる。つまり、テーマによって視覚上の鮮かさの調整が可能だ。さらに、色空間のパラメータとして、3原色(Primaries)の色度(Chromaticity)、光源(Illuminant)の白色点(WhitePoint)を選ぶことができる。これらを変えることで生成される色は大きく変化する。

最後に、今回紹介した3つのアルゴリズムはどれもシンプルで実装が容易だった。これらが、読者が新たなツールを作る時の参考にもなれば幸いである。

You may also like...

  • Vimのfoldexprで新たに折り畳みを定義する方法とその活用法

    Vimで十分に活用されていない組み込み機能の一つは折り畳み(Folding)だと思います。私は以前から折り畳みをいくつかの方法で活用しており、Vimのお気に入りの機能の一つになっています。この記事では、私がどのように折り畳みを設定しているのか、さらには折り畳みを活用するためのプラグインについてみなさんに共有します。

  • A keyboard-oriented system tray for X11 tiling window managers

    Typically, tiling window managers like XMonad do not have a system tray. If you want a system tray, you can use a standalone implementation like stalonetray or a status bar implementation with built-in system tray support, such as polybar.

  • Why you should use wcwidth() to calculate a character width

    Character width depends on the font, and Unicode Consortium does not provide explicit width definitions for all characters. There are characters that have ambiguous widths other than those defined as "Ambiguous (A)" in EastAsianWidth.txt. For example, "☀ (U+2600)" is defined as "Neutral (N)" in EastAsianWidth.txt, but its width may be full-width in a CJK font or an emoji font. This means that a non-East Asian character may be also the ambiguous-width character. Character width tables in default locales is problematic for both CJK and non-CJK users. You can create a custom locale to define a better character width table. wcwidth() respects defined by the locale. All TUI applications should consistently use wcwidth() to calculate the width of the character without an embedded character width table. If there is a mismatch in character width between applications, the screen will be broken.

  • XMonadでカスタマイズ可能なマルチカラムレイアウトを

    XMonadで3列以上のレイアウトを提供するモジュールとして、xmonad-contribのThreeColumnsとMultiColumnsが存在する。ThreeColumnsは3列固定のレイアウトで、MultiColumnsは列数が動的に増減するレイアウトだ。しかし、いずれのレイアウトも筆者の要求を満たすものではなく、新しいレイアウトを独自に作成するに至った。