CSS の HSL で色を指定しよう!(そして、さらに優れた方法について)
CSS で色を指定する際、通常は HEX や RGB を使って定義することが多い:
.gray {
color: #fefefe;
}
.red {
background-color: rgb(125, 125, 125);
}
こうした書き方は長年使われてきており、僕がWeb開発を学び始めた頃に身につけた方法でもある。しかし、この書き方にはいくつかの問題が存在する。
16進数(HEX)のフォーマットは、人間にとっては読みにくい。例えば #27cc95 と言われても、数字を見ただけでどんな色なのか僕には想像がつかない。16進数は機械によるパースは容易だが、人間にとって光の三原色の配合量で色を捉えるのは直感的ではないのだ。
また、デザインシステムでは通常、同一系統の色に対して明度ごとにカラーパレットを作成し、デザイナーが配色しやすいように整える。しかし、単に色の明度を変化させるだけでも、HEX や RGB では数値が大きく異なってしまうことがある。
#BEDBFEと#1E40AFでは、前者の方が明るいが、HEX や RGB の表記からそれを読み取るのは極めて難しい。
CSS とブラウザが大幅に進化した現在では、色の定義がはるかに理解しやすいフォーマット――HSL を使って色を記述できるようになった。
HSL とは何か
多くのWeb開発者が色を指定する際に自然と HSL を使っていることに気づき、その仕組みを理解したとき、僕はようやく HSL による色表現がどれほど直感的であるかを実感した。
HSL は、人間が理解しやすい方法で色を表現する形式であり、H(Hue: 色相)、S(Saturation: 彩度)、L(Lightness: 輝度/明度)に分かれている。写真を趣味にしている人なら、現像・レタッチソフトでよく目にする言葉かもしれない。
色相環はこのような形をしている:
真上を 0 度とし、赤、橙、黄、緑、青、藍、紫へと移り変わっていく。
彩度は色の鮮やかさを指し、値が低いほどくすんだ色になり、極端に低いとグレーがかって見える。彩度が高ければ鮮やかに見える。輝度は色の明るさであり、値が低いほど暗い色になる。
これが理解できると、Tailwind CSS や Material Design などでよく見られるカラー命名規則が、明度を基準にしていることが見えてくる。例えば color-indigo-100 や color-indigo-200 などだ。ただし、ここでの命名は数字が小さいほど明るい。
実際の現場では、明度だけでなく他の微調整も加えるのが一般的だ。
CSS では、HSL は以下のように記述できる:
.my-color {
color: hsl(220deg, 30%, 20%);
}
実際の色を目にする前であっても、僕には以下の情報が読み取れる:
- 色相が 220 度なので、おおよそ青系統の色である
- 彩度が低めで、やや暗い(あるいはかなり暗めの)青である
実際の色は以下のようになる:
知覚される明るさは色相によって変化する
HSL は非常に直感的に扱えるが、HSL 上の輝度が同じであっても、人間の目にはより明るく感じられる色が存在する。最もわかりやすい例が黄色だ。
hsl(236deg,100%,50%)
hsl(61deg, 100%, 50%)
同じ輝度 50% であっても、黄色の方が明らかに明るく見える。これは色の波長がそれぞれ異なるためで、例えば同じ輝度 60% でも、画面上では眩しすぎて目に刺さるように感じられることがある。
これはラベル
これはラベル
ここで白い文字を乗せてラベル UI として使用した場合、青い背景のコントラスト比は許容範囲内だが、黄色い背景のコントラスト比は目も当てられない状態になる。
デザインシステムでは、同一の属性を持つUI要素に対して同じ輝度を適用し、統一感と調和のとれたビジュアルを作ることがよくある。しかし、上述した制限があるため、実際には単に同じ輝度を使い回すだけでは済まず、デザイナーが別途細かく色を微調整しなければならない。
これは HSL の大きな欠点であり、これについては後ほど解決策を取り上げる。それでもなお、HSL は色を扱う上で極めて有用なツールであることに変わりはない。ぜひ身につけて自分のデザインに取り入れてほしい。
デザインツールとしての HSL
ここから先はデザイナーにとっては常識かもしれないが、開発者にとってはそうとは限らない内容だ。
単色でない背景における文字色の調整
単色(純色)ではない背景の上で文字を綺麗に見せるのは難しいが、彩度と輝度を調整することで全体をより調和させることができる。例えば僕のブログのトップページでは、背景色に hsl(230deg, 62%, 83%) を使用し、文字色には hsl(230deg, 37%, 24%) を採用している。彩度と輝度を抑えつつ、単なる純粋な黒を使うのではなく背景色をわずかに混ぜ込むことで、より調和のとれた見た目に仕上がる。
色相の調整を通じた明度のコントロール
上述したアプローチは、HSL で色を表現しているからこそ、各色の役割やコントラストを直感的に理解できる。HSL の扱いに慣れてくると、UI を設計する際にも自然と以下のように考えられるようになる:
- ラベルの背景色は目立ちすぎる必要がないため、淡く、彩度が比較的低い色を採用しよう
- 文字色は背景と調和するように、明度と彩度を調整しよう
こうすることで、単なる「なんとなくの感覚」によるデザインから脱却し、目的を持って狙い通りの効果を作り出すことができる。
HSL は sRGB カラースペースにしか対応していない
sRGB は標準的なカラースペース規格であり、あらゆるディスプレイが sRGB をサポートしている。sRGB は長年使われており極めて普及しているが、Apple が推進する P3 などのカラースペースと比較すると、Apple 製ディスプレイ(MacBook や Studio Display など)が表現できるより広い色域を活かしきれない。
sRGB と P3 の違い
- sRGB:最も広く普及しているカラースペースであり、大半のディスプレイやWebアプリケーションの色域をカバーしている。色域は比較的小さく、日常的な一般的な用途に適している。
- P3:より広いカラースペースであり、sRGB よりも約 50% 広い色域を持つ(特に緑と赤の領域)。ハイエンドディスプレイ(Apple の Retina ディスプレイなど)やプロ向けの映像処理で広く用いられている。
HSL の次に来るもの – oklch
前述のとおり、HSL には知覚される明るさ(知覚明度)の問題がある。数値上の輝度が同じであっても、人間の目は特定の色に対して明るさをより敏感に感じるため、HSL で定義された輝度が同じだからといってそのまま適用できないという問題が生じる。
さらに、HSL はすべての色に対して一律の彩度値を提供する。これは HSL がモデルを簡略化するために、色相・彩度・輝度を完全にフラットなものとして扱っているからだ。しかし、ディスプレイであれ人間の目であれ、表現可能な彩度の範囲は色相ごとに異なっている。
この知覚明度の問題を解決するために、CSS Color Module Level 4 では新たな色の定義方法として oklch() が導入された。P3 などのより広い色域をサポートできるだけでなく、ブラウザによるフォールバック機能も備わっている。指定した色が現在のカラースペースで再現できない場合でも、表現可能な最も近い色に自動的にマッピングしてくれる。
次回の記事では、OKLCH の使い方と実際の効果について詳しく解説しよう!
まとめ
Web開発において、従来の HEX や RGB による色指定は広く普及しているものの、可読性の低さや直感性に欠けるという課題があり、P3 のような広色域カラースペースにおける表現力も制限されている。
HSL(色相・彩度・輝度)はより直感的な色の定義方法を提供し、開発者が色を容易に理解・調整できるようにしてくれる。HSL は3つの次元で色を表現するため、調整がより人間的になり、特にデザインシステムにおけるカラーパレットの明度設計を美しく表現するのに役立つ。
しかし HSL にも限界はある。色相ごとに人間が感じる知覚明度が異なるため、同じ輝度の値であっても目には異なる明るさとして映ってしまうのだ。
この課題を解決するために導入されたのが CSS Color 4 の OKLCH カラーモデルである。人間の目による明度の知覚をより正確に反映するだけでなく、P3 などの広色域をサポートし、最も近い色を自動でマッピングしてWebにおける色の表現力を一段階引き上げてくれる。
HSL と OKLCH を活用することで、開発者はWebの色をより精密にコントロールし、デザインの一貫性と視覚的なクオリティを高めつつ、次世代のカラー標準にも備えることができるのだ!
関連記事
- Three.js で僕の部屋を表現する 僕が React Three Fiber を使って実際の自分の部屋をブラウザ上に再現し、実世界のオブジェクトを目録に見立て、空間の記憶を通してここ数年の生活と仕事について語った話。
- フロントエンドで画像を扱う際に注意すべきこと Jake Archibaldの記事を起点に、現代のレスポンシブ画像の書き方を整理する。なぜwidth/heightを付ける必要があるのか、CSSのaspect-ratioはいつ使うべきか、AVIFとWebPの選び方、そしてpicture/source/srcsetを使ったモバイル向け画像の切り替えについて。
- CSS field-sizing — たった1行のCSSでフォーム要素を自動リサイズする かつてtextareaの自動高さ調整は、JavaScriptでscrollHeightを監視するしかなかった。しかしCSSのfield-sizing: contentなら、わずか1行で代替でき、textarea、input、selectに対応している。本記事では従来のやり方のペインポイントと、field-sizingの使い方をまとめる。
- リンクの下線をもっと見栄え良くする:text-underline-offset デフォルトでは下線と文字が近すぎて、このスタイルを好まないデザイナーもいるし、僕自身もあまり綺麗ではないと感じていた。