🫑tech.miyabitti

OKLCHで設計する、コントラスト比に基づいた実践グレースケール&同系色パレット

OKLCHの知覚均一な明度軸を活用し、WCAG 2.xコントラスト比とAPCA Lc値の具体的な数字をもとに、見やすいグレースケール・同系色パレットを設計する方法を解説します

目次

はじめに

UIの色を選ぶとき、「なんとなく薄いグレー」「なんとなく濃い文字色」で済ませていませんか?

背景色と文字色の組み合わせには、アクセシビリティの観点から明確な数値基準があります。そして、その基準を満たすパレットを「感覚」ではなく「仕組み」で設計できる色空間がOKLCHです。

この記事では、OKLCHの知覚均一な明度(Lightness)軸を使って、コントラスト比の具体的な数値に裏付けられたグレースケールパレットを設計する方法を解説します。グレースケールだけでなく、ブルーやグリーンなど同系色でコントラストを確保するためのパレット設計についても詳しく取り上げます。

OKLCHの基礎

L・C・Hの3軸

OKLCHは、Björn Ottosson氏が2020年に発表したOKLab色空間の円筒座標表現で、人間の色知覚に基づいた色空間です。以下の3つの軸で色を表現します。

名前範囲説明
LLightness(明度)0〜10が黒、1が白。知覚的に均一なスケール
CChroma(彩度)0〜約0.40が無彩色(グレー)、大きいほど鮮やか
HHue(色相)0〜360色の種類(赤・青・緑など)

CSSでは oklch() 関数で記述します。

/* 明度55%、彩度0.15、色相250°(青) */
color: oklch(0.55 0.15 250);
/* グレースケール: Cを0にするだけ */
background: oklch(0.97 0 0);

なぜHSLではなくOKLCHなのか

HSLにも L(Lightness)がありますが、HSLの明度は知覚的に均一ではありません。たとえば、hsl(60, 100%, 50%)(黄色)とhsl(240, 100%, 50%)(青)は同じ L=50% ですが、黄色のほうが圧倒的に「明るく」見えます。

OKLCHの L は人間の知覚に基づいて設計されているため、L値が同じなら、どの色相でも同じ明るさに見えます。これが「知覚均一」の意味であり、パレット設計において決定的に重要な性質です。

Note

OKLCHの詳細な設計背景については、作者のBjörn Ottosson氏によるブログ記事 A perceptual color space for image processing を参照してください。また、Evil MartiansのAndrey Sitnik氏による解説記事 OKLCH in CSS: why we moved from RGB and HSL も非常に参考になります。

コントラスト比の基礎知識

WCAG 2.xの計算方法

WCAG 2.xでは、2色間のコントラスト比を以下の式で定義しています(Success Criterion 1.4.3 Contrast (Minimum))。

コントラスト比 = (L1 + 0.05) / (L2 + 0.05)

ここで L1 は明るい方の色の相対輝度L2 は暗い方の相対輝度です。相対輝度はsRGB値からガンマ補正を解除(リニア化)した上で、以下の係数で算出されます。

L = 0.2126 × R + 0.7152 × G + 0.0722 × B

結果は 1:1(同一色)から 21:1(純粋な白と黒)の範囲を取ります。

Note

WCAG 2.xのコントラスト比計算はsRGB色空間に基づいています。OKLCHで設計したパレットも、最終的にはsRGBに変換してコントラスト比を検証する必要があります。計算式の詳細は WCAG 2.2 – Relative Luminance の定義 を参照してください。

WCAG 2.xの閾値

基準コントラスト比対象
AA(通常テキスト)4.5
以上
本文テキスト(18pt未満)
AA(大きなテキスト)3
以上
見出し(18pt以上 or 14pt太字以上)
AAA(通常テキスト)7
以上
高コントラスト要件
UIコンポーネント3
以上
アイコン、ボーダー等(SC 1.4.11

WCAG 2.xの限界

WCAG 2.xのコントラスト比計算には、実務上いくつかの問題が知られています。

  • ポラリティ(極性)を考慮しない — 白背景に暗い文字と、黒背景に明るい文字では、同じコントラスト比でも読みやすさが異なる
  • フォントサイズ・太さとの関係が粗い — 「通常テキスト」と「大きなテキスト」の2段階しかない
  • ダークモードでの過剰判定 — 暗い背景に中間色のテキストを組み合わせたとき、実際には十分読めるのに基準を満たさないケースがある

こうした課題を解決するために提案されたのが、次に紹介するAPCAです。

APCAとは — 次世代のコントラスト評価

概要

APCA(Advanced Perceptual Contrast Algorithm)は、人間の視覚特性をより正確にモデル化したコントラスト評価手法です。Andrew Somers氏(Myndex)が開発し、GitHubリポジトリで公開されています。WCAG 2.xの「コントラスト比」の代わりに、Lc値(Lightness Contrast)という指標を使います。

APCAの大きな特徴は以下の3点です。

  1. ポラリティを区別する — 暗い背景×明るい文字と、明るい背景×暗い文字で計算式が異なる
  2. フォントサイズ・太さと連動する — 大きなフォントほど低いLcで十分と判定される
  3. 知覚ベースの計算 — OKLCHの L と同様に、人間の明るさ知覚に基づいている

Lc値の推奨閾値

以下は APCA Readability Criterion で示されている推奨値です。

用途最小Lc値フォント目安
本文テキスト(推奨)Lc 9016px / 400
本文テキスト(最小)Lc 7518px / 400
非本文テキストLc 60キャプション、ラベル
見出しLc 4536px / 400 や 24px / 700
プレースホルダーLc 30補助的テキスト
非テキスト要素Lc 15装飾的ボーダー等

Important

2026年8月時点で、APCAはWCAG 3.0の正式な要件ではありません。初期ドラフトに含まれていましたが、ワーキンググループでの合意に至らず除外されています(Adrian Roselli – “Using APCA as a WCAG 3 Failure”)。法的なアクセシビリティ要件を満たすには、引き続きWCAG 2.xの基準(4.5:1 / 3:1)に準拠する必要があります。 APCAは実務上のベストプラクティスとして、WCAG 2.xと併用するのが現実的です。

OKLCHで設計するグレースケールパレット

ここからが本題です。OKLCHの L 軸を使って、コントラスト比が検証された実用的なグレースケールパレットを設計します。

パレット定義

グレースケールでは C = 0(無彩色)なので、L の値だけでパレット全体が決まります。以下は、Tailwind CSS v4のカラーパレットを参考にした11段階のパレットです。

グレースケールパレット(C = 0)

50oklch(0.97 0 0)
100oklch(0.93 0 0)
200oklch(0.87 0 0)
300oklch(0.78 0 0)
400oklch(0.68 0 0)
500oklch(0.55 0 0)
600oklch(0.45 0 0)
700oklch(0.37 0 0)
800oklch(0.27 0 0)
900oklch(0.21 0 0)
950oklch(0.14 0 0)

コントラスト比の検証

このパレットから代表的な背景色×文字色の組み合わせを取り、WCAG 2.xの基準で検証した結果が以下です。

ライトモード

組み合わせコントラスト比AA通常AA大AAA
背景50 × テキスト90016.24
背景50 × テキスト80013.82
背景50 × テキスト7009.56
背景50 × テキスト6006.82
背景50 × テキスト5004.45
背景50 × テキスト4002.64
カード100 × テキスト90014.42
カード100 × テキスト7008.49
背景200 × テキスト90011.96

ダークモード

組み合わせコントラスト比AA通常AA大AAA
背景950 × テキスト5018.25
背景950 × テキスト10016.20
背景950 × テキスト20013.43
背景950 × テキスト3009.95
背景950 × テキスト4006.91
カード900 × テキスト5016.24
カード900 × テキスト10014.42
カード800 × テキスト10012.26

実際に見て比較する

テーブルの数字だけではわかりにくいので、実際のレンダリングで確認しましょう。

テキストのコントラスト比較

❌ コントラスト不足 — 2.64:1

色彩はデザインの基盤です。適切なコントラスト比を確保することで、すべてのユーザーにとって読みやすいUIを実現できます。

背景: Gray-50 (L=0.97) / 文字: Gray-400 (L=0.68)

✅ AA準拠 — 16.24:1

色彩はデザインの基盤です。適切なコントラスト比を確保することで、すべてのユーザーにとって読みやすいUIを実現できます。

背景: Gray-50 (L=0.97) / 文字: Gray-900 (L=0.21)

❌ コントラスト不足 — 4.45:1 — AA未達

Gray-500(L=0.55)はAA通常をわずかに下回る4.45:1。1ステップ暗い600(L=0.45)にするだけで6.82:1まで改善されます。

背景: Gray-50 (L=0.97) / 文字: Gray-500 (L=0.55)

✅ AA準拠 — 6.82:1 — AA達成

Gray-500(L=0.55)はAA通常をわずかに下回る4.45:1。1ステップ暗い600(L=0.45)にするだけで6.82:1まで改善されます。

背景: Gray-50 (L=0.97) / 文字: Gray-600 (L=0.45)

ダークモードの比較

❌ コントラスト不足 — 2.82:1

色彩はデザインの基盤です。適切なコントラスト比を確保することで、すべてのユーザーにとって読みやすいUIを実現できます。

背景: Gray-950 (L=0.14) / 文字: Gray-600 (L=0.45)

✅ AA準拠 — 16.20:1

色彩はデザインの基盤です。適切なコントラスト比を確保することで、すべてのユーザーにとって読みやすいUIを実現できます。

背景: Gray-950 (L=0.14) / 文字: Gray-100 (L=0.93)

カード型UIでの比較

❌ Gray-300 × Gray-200 — 読めない

記事タイトル

この記事の説明文です。コントラスト比が不十分だと、テキストが背景に溶け込んでしまいます。

#CSS2026年8月21日
✅ Gray-900 × Gray-600 — はっきり読める

記事タイトル

この記事の説明文です。十分なコントラスト比が確保されていると、テキストがはっきり読めます。

#CSS2026年8月21日

テーブルから読み取れるポイント

  1. 本文テキストにはL差0.7以上を確保する — 背景50(L=0.97)× テキスト900(L=0.21)のL差は0.76で、コントラスト比は16.24
    。余裕をもってAAAを達成
  2. 500番がAA通常の境界線 — 背景50 × 500(L=0.55)は4.45
    本文テキストに使うなら600以上(L=0.45以下)が安全
  3. 400番以下はテキストに使わない — 背景50 × 400は2.64
    。AA大(3
    )すら満たさないため、装飾やプレースホルダー限定
  4. ダークモードでは400番が有用 — 背景950 × 400は6.91
    。ダークモードの補助テキストとして十分機能する

CSS Custom Propertiesでの実装

:root {
/* グレースケールパレット */
--gray-50: oklch(0.97 0 0);
--gray-100: oklch(0.93 0 0);
--gray-200: oklch(0.87 0 0);
--gray-300: oklch(0.78 0 0);
--gray-400: oklch(0.68 0 0);
--gray-500: oklch(0.55 0 0);
--gray-600: oklch(0.45 0 0);
--gray-700: oklch(0.37 0 0);
--gray-800: oklch(0.27 0 0);
--gray-900: oklch(0.21 0 0);
--gray-950: oklch(0.14 0 0);
/* セマンティックトークン(ライトモード) */
--color-bg: var(--gray-50);
--color-bg-card: var(--gray-100);
--color-border: var(--gray-200);
--color-text-muted: var(--gray-500);
--color-text: var(--gray-900);
--color-text-heading: var(--gray-800);
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: var(--gray-950);
--color-bg-card: var(--gray-900);
--color-border: var(--gray-800);
--color-text-muted: var(--gray-400);
--color-text: var(--gray-100);
--color-text-heading: var(--gray-50);
}
}

ポイントは、プリミティブトークン(--gray-XXX)とセマンティックトークン(--color-text 等)を分離することです。ダークモードではセマンティックトークンの参照先を入れ替えるだけで、コントラスト比を維持したまま配色を反転できます。

グレースケールを超えて — 同系色パレットの設計法

ここまではグレースケール(C=0)の話でした。実際のUIでは、ブランドカラーに基づいた同系色パレットが必要になります。

原則:Lの差でコントラストを確保する

同系色パレットでも、コントラスト比を決定するのは明度の差です。色相(H)を固定し、彩度(C)を持たせたまま、L 値の段階を設計すれば、グレースケールと同じロジックでコントラストを確保できます。

これがOKLCHの最大の強みです。色相を自由に変えても、L値の差を同じに保てば、ほぼ同じコントラスト比が得られます。

彩度カーブの設計

彩度(C)を全ステップで一定にすると、明度の高い色(50番台)と低い色(900番台)が不自然に鮮やかになります。人間の目は、極端に明るい色や暗い色の彩度を知覚しにくいためです。

推奨される彩度カーブは山型で、中間の明度で最大になり、両端に向かって小さくなるのが自然です。Huetoneなどのツールを使うと視覚的に確認しながら設計できます。

C
0.15 | ●
0.12 | ● ●
0.09 | ● ●
0.06 | ● ●
0.04 | ● ●
0.02 |● ●
0.01 ● ●
┗━━━━━━━━━━━━━━━━━━━━━━━
50 100 200 300 400 500 600 700 800 900 950

ブルー系パレットの具体例

色相 H = 250(青)でパレットを組んだ場合の具体例です。

ブルー系パレット(H = 250)

50oklch(0.97 0.01 250)
100oklch(0.93 0.02 250)
200oklch(0.87 0.04 250)
300oklch(0.78 0.08 250)
400oklch(0.68 0.12 250)
500oklch(0.55 0.15 250)
600oklch(0.45 0.14 250)
700oklch(0.37 0.12 250)
800oklch(0.27 0.09 250)
900oklch(0.21 0.06 250)
950oklch(0.14 0.04 250)

同系色でもコントラスト比はほぼ同じ

このブルー系パレットのコントラスト比を検証すると、グレースケールとほぼ同じ数値が得られます。

組み合わせコントラスト比AA通常AA大
Blue-50 × Blue-90016.26
Blue-50 × Blue-7009.49
Blue-950 × Blue-10016.23
Blue-950 × Blue-20013.48

グレースケールの同等ペア(Gray-50 × Gray-900 = 16.24

)と比較すると、L値を揃えている限り、色相や彩度を変えてもコントラスト比はほぼ変わりません。これがOKLCHの知覚均一性の実証です。

ブルー系での実際のレンダリング

❌ コントラスト不足 — 2.64:1

同系色でも、L値の差を十分に確保すれば読みやすいテキストを実現できます。色相を統一したブランドカラーパレットでも、アクセシビリティは両立可能です。

背景: Blue-50 (L=0.97) / 文字: Blue-400 (L=0.68)

✅ AA準拠 — 16.26:1

同系色でも、L値の差を十分に確保すれば読みやすいテキストを実現できます。色相を統一したブランドカラーパレットでも、アクセシビリティは両立可能です。

背景: Blue-50 (L=0.97) / 文字: Blue-900 (L=0.21)

同系色パレット設計のチェックリスト

  1. L値の段階はグレースケールと同じにする — パレットの骨格はLで決まる。グレースケールで検証済みのL値をそのまま流用できる
  2. 彩度は山型カーブにする — 中間明度(400〜600)で最大、両端で最小
  3. ガモットの上限に注意する — OKLCHはP3やRec.2020などの広色域を表現できるが、sRGBの範囲外になる色は、一般的なモニターで クランプ(丸め込み) される。特に高彩度かつ高明度の色(鮮やかな黄色やシアン等)は注意が必要
  4. 最終確認はsRGB変換後のコントラスト比で行う — OKLCHで設計しても、WCAG 2.xの基準はsRGBベース。ツールで変換・検証を行うこと

実践テクニック

color-mix() による動的調整

CSS color-mix() を使えば、パレット外の中間色を動的に生成できます。

/* gray-100とgray-200の中間色 */
border-color: color-mix(in oklch, var(--gray-100), var(--gray-200));
/* 白を40%混ぜて明るくする */
background: color-mix(in oklch, var(--blue-500), white 40%);

in oklch を指定することで、OKLCHの知覚均一な空間で補間が行われます。in srgb と比較すると、中間色が「泥っぽく」ならない利点があります。

相対カラー構文

CSS相対カラー構文(Relative Color Syntax)を使えば、既存の色の L だけを変更するといった操作も可能です。

/* ベースカラーの明度だけを0.2に変更 */
--dark-variant: oklch(from var(--brand-color) 0.2 c h);
/* 明度を20%下げる */
--darker: oklch(from var(--brand-color) calc(l - 0.2) c h);

ダークモードの設計パターン

グレースケールパレットの場合、ダークモードではLの対応を反転させるだけで切り替えが可能です。

ライトモード用途ダークモード
50 (L=0.97)背景950 (L=0.14)
100 (L=0.93)カード背景900 (L=0.21)
200 (L=0.87)ボーダー800 (L=0.27)
900 (L=0.21)テキスト100 (L=0.93)

ただし、完全な反転ではなくコントラスト比を基準に調整するのが重要です。ダークモードではまぶしさを抑えるため、テキストに純白(L=1.0)ではなく、少しLを下げた50番(L=0.97)や100番(L=0.93)を使うのが一般的です。

フォールバック

OKLCHは主要なモダンブラウザでサポートされていますが、古いブラウザ向けにフォールバックを提供する場合は @supports を活用します。

:root {
/* フォールバック(sRGB) */
--gray-900: #181818;
/* OKLCH対応ブラウザ向け */
@supports (color: oklch(0 0 0)) {
--gray-900: oklch(0.21 0 0);
}
}

まとめ

OKLCHの L 軸が知覚均一であるという性質は、パレット設計を「感覚」から「仕組み」に変えてくれます。

押さえておくべきポイントをまとめます。

  • グレースケールは C = 0 でLだけの設計 — シンプルで、コントラスト比の予測が容易
  • 本文テキストにはLの差を0.7以上確保 — WCAG 2.x AA(4.5
    )を十分に超え、AAA(7
    )も視野に入る
  • 500番(L=0.55)がAA通常の境界線 — 明るい背景(L≧0.93)に対して、本文テキストには600番以上(L≦0.45)を使う
  • 同系色でもL値を揃えればコントラスト比はほぼ同じ — OKLCHの知覚均一性の恩恵で、色相を変えてもパレットの骨格を使い回せる
  • 彩度は山型カーブにする — 中間明度で最大、両端で最小が自然
  • WCAG 2.xで検証し、APCAで微調整する — 法的準拠はWCAG 2.x、実際の読みやすさはAPCAで確認するのが現実的

参考リンク

色選びに迷ったとき、OKLCH Lightness値とコントラスト比のテーブルが、確信を持った判断の助けになれば幸いです。