ダークUIで気をつけること——コントラスト・彩度・グレインの決め方
背景を黒くしただけのダークUIは、まぶしいのに読みにくいという状態になりがちです。私が実際に使っている、明度差の測り方・彩度を落とす順番・グレインでバンディングを隠す手順を書きます。
ダークUIで失敗するときの原因は、ほぼ3つに絞れます。明度差が足りない(あるいは強すぎる)、色が鮮やかすぎる、そしてグラデーションに縞(バンディング)が出る。私はこの3つを「コントラスト・彩度・グレイン」の順で潰すようにしています。ライトテーマの色を単純に反転させるのではなく、それぞれ別の基準で決め直す作業だと考えたほうが早く収まります。
コントラストは数字と目視の二段構えで見る
まず数字。WCAG 2.2 の達成基準 1.4.3 は本文テキストに 4.5:1、大きな文字に 3:1 を求めています(大きな文字の定義は「18ポイント以上、または太字で14ポイント以上」)。ボタンの枠線やアイコンなど文字以外の要素は、基準 1.4.11 で隣接色に対して 3:1 です。出典は仕様書そのものを見るのが確実です(WCAG 2.2 / 1.4.3 Contrast (Minimum)、1.4.11 Non-text Contrast)。
ここで注意したいのは、WCAG 2 の比率は相対輝度から計算する対称な式なので、前景色と背景色を入れ替えても同じ数字になるという点です。つまり「ライトで4.5:1だったから、反転しても大丈夫」とは言えても、見た目の読みやすさが同じとは限りません。明るい文字が暗い背景の上でにじんで太く見える現象については以前から指摘があり、知覚に沿った計算方法の議論も続いているとされています。私は数字をクリアしたうえで、必ず実機の画面で目視するという順番にしています。
実務では、次の3点を先に固定してしまうと迷いが減ります。
| 役割 | 私の出発点 |
|---|---|
| 背景(最下層) | 真っ黒ではなく #121212〜#16181a あたり |
| 本文テキスト | 純白ではなく #e6e6e6 前後(背景に対して12:1程度) |
| 補助テキスト | 4.5:1 を下回らない範囲まで落とす |
真っ黒と純白を避けるのは、コントラストが高すぎると長文でちらつきを感じやすいためです。加えて、背景が完全な黒だと影が置けなくなり、カード同士の重なりを表現しづらくなります。
彩度は落とす。鮮やかな色は面積を小さく
ライトテーマで使っていたブランドカラーを、そのままダークの背景に置くと、まず文字として読めません。Material Design のダークテーマのガイドラインでも、彩度の高い色は暗い面に対して 4.5:1 を満たしにくく、視覚的な振動(optical vibration)を起こして目が疲れるため、彩度を落として明度を上げたトーンを使うよう説明されています(Dark theme - Material Design)。
私の手順はこうです。ブランドカラーは明度を上げて彩度を下げた別トーンをダーク用に1つ作る。そして元の鮮やかな色は、テキストではなく「小さい面積のアクセント」に回します。具体的には、バッジ・グラフの線・フォーカスリング・アクティブなタブの下線など。面積が小さければ振動は気になりませんし、そこだけ彩度が高いことで視線の誘導が効きます。
グレインは「バンディング隠し」として入れる
ダークUIで質感の話になると装飾のように思われますが、私がグレイン(微細なノイズ)を入れる理由はほとんど実用です。暗い色どうしのグラデーションは、8bitの階調では段差が縞になって見えることがあります。ヒーロー画像の上に黒の透過グラデーションを重ねる、という定番の処理でよく起きます。
対処は2つ。ひとつはグラデーションの距離を長くして1段あたりの変化を小さくすること。もうひとつが、上に薄いノイズを重ねて段差の境界を散らすことです。私は不透明度を2〜5%程度に抑えた小さなノイズ画像(あるいはSVGの feTurbulence)をタイル状に敷いて、pointer-events: none で操作を邪魔しないようにしています。強く入れると文字の読みやすさを落とすので、縞が見えなくなったところで止めるのが目安です。
実装は color-scheme を起点に一元化する
色の管理が二重になると必ず片方が古くなります。私は :root { color-scheme: light dark; } を宣言したうえで、カスタムプロパティの値を light-dark() で1行に書く方法を使っています。light-dark() は color-scheme に light dark が設定されていることを前提に、現在のカラースキームに応じた値を返す関数で、Baseline では 2024年5月に「Newly available」になったとされています(light-dark() - MDN)。古い環境も対象なら、従来どおり prefers-color-scheme のメディアクエリで変数を上書きする形でかまいません。
:root {
color-scheme: light dark;
--bg: light-dark(#fbfbf9, #121212);
--text: light-dark(#1c1c1c, #e6e6e6);
--text-sub: light-dark(#5a5a5a, #a8a8a8);
}
明日から試せる一手
いま手元にあるダークUIを開いて、補助テキストと枠線の2つだけをコントラストチェッカーに入れてみてください。私の経験では、崩れているのはほぼこの2つです。本文は最初から気をつけるので通っていて、キャプション・プレースホルダー・カードの境界線が基準を割っている。数字を確認したら、その画面をスマホの輝度を下げた状態で見て、縞が出ていないかも一緒に確認する。ここまでで15分ほどです。