---
title: "OKLCHで設計する、コントラスト比に基づいた実践グレースケール＆同系色パレット"
description: "OKLCHの知覚均一な明度軸を活用し、WCAG 2.xコントラスト比とAPCA Lc値の具体的な数字をもとに、見やすいグレースケール・同系色パレットを設計する方法を解説します"
publishedAt: "2026-08-21T00:00:00.000Z"
tags: ["CSS","アクセシビリティ","デザイン"]
---
import ColorPalette from "@/components/article/ColorPalette.astro";
import ContrastCompare from "@/components/article/ContrastCompare.astro";
import ContrastCardDemo from "@/components/article/ContrastCardDemo.astro";
import PerceptualCompare from "@/components/article/PerceptualCompare.astro";

## コントラスト比を数値で担保するパレット設計

UIのカラーパレットを組む際、背景色と文字色のコントラスト比は目視の感覚だけに頼らず、アクセシビリティ基準に沿った数値で担保しておくと後々のデザイン破綻を防げます。しかし、従来のRGBやHSLでは数値と人間の知覚が一致せず、色相を変えるたびにコントラスト比を再計算して微調整を繰り返すことになりがちでした。

人間の色知覚に最適化された色空間である**OKLCH**を利用すると、知覚的に均一な明度（Lightness）軸を基準にして、再現性の高いグレースケールや同系色のカラーパレットを論理的に組み立てられます。

本稿では、OKLCHの明度軸をベースに、WCAG 2.xのコントラスト比やAPCAの基準を満たすグレースケールパレット、および同系色（ブルー系など）パレットの具体的な構築手順をまとめます。

## OKLCHがパレット設計に適している理由

### L・C・Hの3軸

[OKLCH](https://oklch.com/)は、Björn Ottosson氏が2020年に発表した[OKLab色空間](https://bottosson.github.io/posts/oklab/)の円筒座標表現で、人間の色知覚に基づいた色空間です。以下の3つの軸で色を表現します。

| 軸 | 名前 | 範囲 | 説明 |
|---|---|---|---|
| **L** | Lightness（明度） | 0〜1 | 0が黒、1が白。**知覚的に均一**なスケール |
| **C** | Chroma（彩度） | 0〜約0.4 | 0が無彩色（グレー）、大きいほど鮮やか |
| **H** | Hue（色相） | 0〜360 | 色の種類（赤・青・緑など） |

CSSでは [`oklch()`](https://developer.mozilla.org/ja/docs/Web/CSS/color_value/oklch) 関数で記述します。

```css
/* 明度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値が同一であれば、色相（H）が変わってもほぼ同一の明るさとして知覚されます**。

以下のデモで、同じ明度指定（HSL L=50% と OKLCH L=0.65）における色相ごとの見え方と、彩度を落としたモノクロ階調の違いを確認できます。

<PerceptualCompare />

モノクロ化した際、HSLは色相によって白から黒に近いグレーまでばらつきが生じるのに対し、OKLCHはすべての色相で均等な階調に揃っていることが分かります。この性質があるため、パレットの明度ステップを一度決めてしまえば、他の色相にもそのまま流用しやすくなります。

> [!NOTE]
> OKLCHの設計背景や詳細な色空間の仕組みについては、考案者のBjörn Ottosson氏による解説 [A perceptual color space for image processing](https://bottosson.github.io/posts/oklab/) や、Evil Martiansの [OKLCH in CSS: why we moved from RGB and HSL](https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl) で詳しく解説されています。

## コントラスト比の計算と評価基準

### WCAG 2.xの計算方法

[WCAG 2.x](https://www.w3.org/TR/WCAG22/)では、2色間の**コントラスト比**を以下の式で定義しています（[Success Criterion 1.4.3 Contrast (Minimum)](https://www.w3.org/TR/WCAG22/#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 の定義](https://www.w3.org/TR/WCAG22/#dfn-relative-luminance) を参照してください。

### WCAG 2.xの閾値

| 基準 | コントラスト比 | 対象 |
|---|---|---|
| **AA（通常テキスト）** | **4.5:1** 以上 | 本文テキスト（18pt未満） |
| **AA（大きなテキスト）** | **3:1** 以上 | 見出し（18pt以上 or 14pt太字以上） |
| **AAA（通常テキスト）** | **7:1** 以上 | 高コントラスト要件 |
| **UIコンポーネント** | **3:1** 以上 | アイコン、ボーダー等（[SC 1.4.11](https://www.w3.org/TR/WCAG22/#non-text-contrast)） |

### WCAG 2.xの課題

WCAG 2.xの計算式は広く普及している一方で、人間の実際の視覚特性との乖離がいくつか指摘されています。

- **極性（ポラリティ）が考慮されない**: 白背景に暗い文字を置く場合と、黒背景に明るい文字を置く場合では、同じ計算値でも読みやすさの体感に差が出ます。
- **フォントサイズやウェイトへの追従が粗い**: 基準が「通常テキスト（4.5:1）」と「大きなテキスト（3:1）」の2区分しかなく、細いフォントや極太フォントの違いが反映されません。
- **ダークモードでの過剰判定**: 暗い背景に中間色のテキストを配した際、実際には問題なく読めるコントラストであっても計算上は不合格判定になりやすい傾向があります。

こうした知覚上の課題を改善するために考案されたのが、次期アルゴリズム候補として開発されているAPCAです。

## APCA（次世代のコントラスト評価）

### 視覚特性に即したLc値の算出

**APCA**（Advanced Perceptual Contrast Algorithm）は、人間の空間周波数特性やコントラスト感度関数を考慮したコントラスト評価手法です。Andrew Somers氏（Myndex）が開発を進めており（[GitHubリポジトリ](https://github.com/Myndex/SAPC-APCA)）、WCAG 2.xの単純比率に代わって**Lc値**（Lightness Contrast）という指標を用います。

APCAには以下のような特徴があります。

- 明暗の極性（ライトモード／ダークモード）に応じて計算式を切り替える
- テキストのフォントサイズやウェイト（太さ）と連動して必要なLc値を算出する
- OKLCHと同様に、人間の明るさ知覚に基づいた非線形変換を行う

### Lc値の推奨閾値

[APCA Readability Criterion](https://readtech.org/ARC/) で示されている主な推奨値は以下の通りです。

| 用途 | 最小Lc値 | フォント目安 |
|---|---|---|
| **本文テキスト（推奨）** | Lc 90 | 16px / 400 |
| **本文テキスト（最小）** | Lc 75 | 18px / 400 |
| **非本文テキスト** | Lc 60 | キャプション、ラベル |
| **見出し** | Lc 45 | 36px / 400 や 24px / 700 |
| **プレースホルダー** | Lc 30 | 補助的テキスト |
| **非テキスト要素** | Lc 15 | 装飾的ボーダー等 |

> [!IMPORTANT]
> 2026年時点で、APCAは**WCAG 3.0の正式仕様として採択されていません**。初期ドラフトに含まれていたものの、ワーキンググループでの合意形成に至らず見送られています（[Adrian Roselli – "Using APCA as a WCAG 3 Failure"](https://adrianroselli.com/2022/01/using-apca-as-a-wcag-3-failure.html)）。したがって法的なアクセシビリティ対応が求められるプロジェクトでは、引き続きWCAG 2.x（4.5:1 / 3:1）の数値を優先しつつ、視覚的な微調整の指標としてAPCAを参照するのが安全です。

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

OKLCHの `L` 軸を使って、コントラスト比の検証に基づいた実用的なグレースケールパレットを設計します。

### パレット定義

グレースケールでは `C = 0`（無彩色）なので、**`L` の値だけでパレット全体が決まります**。以下は、[Tailwind CSS v4のカラーパレット](https://tailwindcss.com/docs/colors)を参考にした11段階のパレットです。

<ColorPalette
  title="グレースケールパレット（C = 0）"
  swatches={[
    { name: "50",  oklch: "oklch(0.97 0 0)", hex: "#f5f5f5", usage: "ページ背景" },
    { name: "100", oklch: "oklch(0.93 0 0)", hex: "#e8e8e8", usage: "カード背景、バッジ" },
    { name: "200", oklch: "oklch(0.87 0 0)", hex: "#d4d4d4", usage: "ボーダー、区切り線" },
    { name: "300", oklch: "oklch(0.78 0 0)", hex: "#b7b7b7", usage: "無効状態、プレースホルダー" },
    { name: "400", oklch: "oklch(0.68 0 0)", hex: "#989898", usage: "補助テキスト、アイコン" },
    { name: "500", oklch: "oklch(0.55 0 0)", hex: "#717171", usage: "中間要素" },
    { name: "600", oklch: "oklch(0.45 0 0)", hex: "#555555", usage: "強調されたUI要素" },
    { name: "700", oklch: "oklch(0.37 0 0)", hex: "#404040", usage: "見出し" },
    { name: "800", oklch: "oklch(0.27 0 0)", hex: "#262626", usage: "ダークUI" },
    { name: "900", oklch: "oklch(0.21 0 0)", hex: "#181818", usage: "本文テキスト、ダーク背景" },
    { name: "950", oklch: "oklch(0.14 0 0)", hex: "#090909", usage: "最も暗い背景" },
  ]}
/>

### コントラスト比の検証

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

#### ライトモード

| 組み合わせ | コントラスト比 | AA通常 | AA大 | AAA |
|---|---|---|---|---|
| 背景50 × テキスト900 | **16.24:1** | ✅ | ✅ | ✅ |
| 背景50 × テキスト800 | **13.82:1** | ✅ | ✅ | ✅ |
| 背景50 × テキスト700 | **9.56:1** | ✅ | ✅ | ✅ |
| 背景50 × テキスト600 | **6.82:1** | ✅ | ✅ | ❌ |
| 背景50 × テキスト500 | **4.45:1** | ❌ | ✅ | ❌ |
| 背景50 × テキスト400 | **2.64:1** | ❌ | ❌ | ❌ |
| カード100 × テキスト900 | **14.42:1** | ✅ | ✅ | ✅ |
| カード100 × テキスト700 | **8.49:1** | ✅ | ✅ | ✅ |
| 背景200 × テキスト900 | **11.96:1** | ✅ | ✅ | ✅ |

#### ダークモード

| 組み合わせ | コントラスト比 | AA通常 | AA大 | AAA |
|---|---|---|---|---|
| 背景950 × テキスト50 | **18.25:1** | ✅ | ✅ | ✅ |
| 背景950 × テキスト100 | **16.20:1** | ✅ | ✅ | ✅ |
| 背景950 × テキスト200 | **13.43:1** | ✅ | ✅ | ✅ |
| 背景950 × テキスト300 | **9.95:1** | ✅ | ✅ | ✅ |
| 背景950 × テキスト400 | **6.91:1** | ✅ | ✅ | ❌ |
| カード900 × テキスト50 | **16.24:1** | ✅ | ✅ | ✅ |
| カード900 × テキスト100 | **14.42:1** | ✅ | ✅ | ✅ |
| カード800 × テキスト100 | **12.26:1** | ✅ | ✅ | ✅ |

### 実際に見て比較する

上記の数値を実際のUI要素としてレンダリングし、可読性を比較してみます。

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

<ContrastCompare
  bad={{
    bg: "oklch(0.97 0 0)",
    fg: "oklch(0.68 0 0)",
    bgLabel: "Gray-50 (L=0.97)",
    fgLabel: "Gray-400 (L=0.68)",
    ratio: "2.64:1",
    pass: false,
  }}
  good={{
    bg: "oklch(0.97 0 0)",
    fg: "oklch(0.21 0 0)",
    bgLabel: "Gray-50 (L=0.97)",
    fgLabel: "Gray-900 (L=0.21)",
    ratio: "16.24:1",
    pass: true,
  }}
/>

<ContrastCompare
  bad={{
    bg: "oklch(0.97 0 0)",
    fg: "oklch(0.55 0 0)",
    bgLabel: "Gray-50 (L=0.97)",
    fgLabel: "Gray-500 (L=0.55)",
    ratio: "4.45:1 — AA未達",
    pass: false,
  }}
  good={{
    bg: "oklch(0.97 0 0)",
    fg: "oklch(0.45 0 0)",
    bgLabel: "Gray-50 (L=0.97)",
    fgLabel: "Gray-600 (L=0.45)",
    ratio: "6.82:1 — AA達成",
    pass: true,
  }}
  sampleText="Gray-500（L=0.55）はAA通常をわずかに下回る4.45:1。1ステップ暗い600（L=0.45）にするだけで6.82:1まで改善されます。"
/>

#### ダークモードの比較

<ContrastCompare
  bad={{
    bg: "oklch(0.14 0 0)",
    fg: "oklch(0.45 0 0)",
    bgLabel: "Gray-950 (L=0.14)",
    fgLabel: "Gray-600 (L=0.45)",
    ratio: "2.82:1",
    pass: false,
  }}
  good={{
    bg: "oklch(0.14 0 0)",
    fg: "oklch(0.93 0 0)",
    bgLabel: "Gray-950 (L=0.14)",
    fgLabel: "Gray-100 (L=0.93)",
    ratio: "16.20:1",
    pass: true,
  }}
/>

#### カード型UIでの比較

<ContrastCardDemo
  bad={{
    bg: "#f5f5f5",
    cardBg: "#e8e8e8",
    fg: "#b7b7b7",
    mutedFg: "#d4d4d4",
    borderColor: "#e8e8e8",
    label: "Gray-300 × Gray-200 — 読めない",
    pass: false,
  }}
  good={{
    bg: "#f5f5f5",
    cardBg: "#ffffff",
    fg: "#181818",
    mutedFg: "#555555",
    borderColor: "#d4d4d4",
    label: "Gray-900 × Gray-600 — はっきり読める",
    pass: true,
  }}
/>

### 数値検証から得られる設計基準

上記のコントラスト比テーブルから、実務で使えるいくつかの基準が見えてきます。

- **本文テキストには L の差を 0.70 以上確保する**  
  背景50（L=0.97）に対してテキスト900（L=0.21）を置くと、L差は0.76となりコントラスト比は16.24:1に達します。これにより余裕をもってAAA基準（7:1）をクリアできます。
- **中間色の500番（L=0.55）はAA基準の境界線**  
  背景50（L=0.97）と500番の組み合わせは4.45:1となり、わずかに通常テキストのAA基準（4.5:1）を下回ります。本文テキストとして使用する場合は、1ステップ暗い600番（L=0.45以下）を指定するのが安全です。
- **400番以下はテキスト用途を避ける**  
  背景50に対する400番（L=0.68）は2.64:1にとどまり、大テキスト基準（3:1）も満たせません。ボーダーや無効状態、装飾的アイコンなどの用途に限定します。
- **ダークモードでは400番が補助テキストに適する**  
  背景950（L=0.14）に対する400番（L=0.68）はコントラスト比6.91:1となり、AA基準をクリアします。ダークテーマ時のサブテキストや補足ラベルとして使いやすい明るさです。

### CSS Custom Propertiesでの実装

```css
: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）で確立した明度設計は、色相（H）と彩度（C）を加えたブランドカラーやアクセントカラーのパレットにもそのまま適用できます。

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

同系色パレットにおいても、コントラスト比を支配するのは**明度（L）の差**です。色相（H）を固定し、彩度（C）を持たせたままグレースケールと同じ `L` 値のステップを踏襲すれば、同様のコントラスト比を再現できます。

OKLCHではL値の差が知覚的な明るさの差と直結しているため、**色相を切り替えても、同じLステップを保つ限り、同等のコントラスト比を維持できます**。

### 彩度カーブの設計

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

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

```
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`（青）でパレットを組んだ場合の具体例です。

<ColorPalette
  title="ブルー系パレット（H = 250）"
  swatches={[
    { name: "50",  oklch: "oklch(0.97 0.01 250)", hex: "#f0f6fc", usage: "ページ背景" },
    { name: "100", oklch: "oklch(0.93 0.02 250)", hex: "#dee9f5", usage: "カード背景" },
    { name: "200", oklch: "oklch(0.87 0.04 250)", hex: "#c1d7ee", usage: "ボーダー" },
    { name: "300", oklch: "oklch(0.78 0.08 250)", hex: "#90bce9", usage: "バッジ背景" },
    { name: "400", oklch: "oklch(0.68 0.12 250)", hex: "#5b9ddf", usage: "補助テキスト" },
    { name: "500", oklch: "oklch(0.55 0.15 250)", hex: "#0f74c5", usage: "プライマリボタン" },
    { name: "600", oklch: "oklch(0.45 0.14 250)", hex: "#00569f", usage: "ホバー状態" },
    { name: "700", oklch: "oklch(0.37 0.12 250)", hex: "#00407c", usage: "見出しテキスト" },
    { name: "800", oklch: "oklch(0.27 0.09 250)", hex: "#002750", usage: "ダークUI" },
    { name: "900", oklch: "oklch(0.21 0.06 250)", hex: "#001932", usage: "テキスト" },
    { name: "950", oklch: "oklch(0.14 0.04 250)", hex: "#000919", usage: "最暗背景" },
  ]}
/>

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

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

| 組み合わせ | コントラスト比 | AA通常 | AA大 |
|---|---|---|---|
| Blue-50 × Blue-900 | **16.26:1** | ✅ | ✅ |
| Blue-50 × Blue-700 | **9.49:1** | ✅ | ✅ |
| Blue-950 × Blue-100 | **16.23:1** | ✅ | ✅ |
| Blue-950 × Blue-200 | **13.48:1** | ✅ | ✅ |

グレースケールの同等ペア（Gray-50 × Gray-900 = 16.24:1）と比較すると、**L値を揃えている限り、色相や彩度を変えてもコントラスト比はほぼ変わりません**。知覚均一な色空間ならではの大きな利点です。

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

<ContrastCompare
  bad={{
    bg: "oklch(0.97 0.01 250)",
    fg: "oklch(0.68 0.12 250)",
    bgLabel: "Blue-50 (L=0.97)",
    fgLabel: "Blue-400 (L=0.68)",
    ratio: "2.64:1",
    pass: false,
  }}
  good={{
    bg: "oklch(0.97 0.01 250)",
    fg: "oklch(0.21 0.06 250)",
    bgLabel: "Blue-50 (L=0.97)",
    fgLabel: "Blue-900 (L=0.21)",
    ratio: "16.26:1",
    pass: true,
  }}
  sampleText="同系色でも、L値の差を十分に確保すれば読みやすいテキストを実現できます。色相を統一したブランドカラーパレットでも、アクセシビリティは両立可能です。"
/>

### 同系色パレット作成時のポイント

1. **L値の階調ステップをグレースケールと共通化する**: グレースケール側でコントラスト比を検証済みのL値を流用することで、カラー展開時の計算工数を削減できます。
2. **彩度は中明度をピークとする山型カーブにする**: 極端に明るい階調（50〜100）や暗い階調（900〜950）で彩度を高めすぎると不自然な色味になりやすいため、400〜600の中間域を最大値に設定します。
3. **sRGB色域（ガモット）のクリッピングに注意する**: OKLCHは[P3](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/color-gamut)などの広色域も指定できますが、一般的なsRGBディスプレイ環境では域外の色が近似値に丸め込まれます（クランプ）。高彩度なイエローやシアンを扱う際は、sRGB範囲内に収まっているかをカラーピッカー等で確認しておくと安心です。
4. **最終検証はsRGB変換値で行う**: 現行のWCAG 2.x基準はsRGBの相対輝度に基づいているため、最終的な合否判定は変換後のsRGB値で行います。

## 実践テクニック

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

CSS [`color-mix()`](https://developer.mozilla.org/ja/docs/Web/CSS/color_value/color-mix) を使えば、パレット外の中間色を動的に生成できます。

```css
/* 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）](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors)を使えば、既存の色の `L` だけを変更するといった操作も可能です。

```css
/* ベースカラーの明度だけを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）を避け、50番（L=0.97）や100番（L=0.93）を当てるのが実用的です。

### フォールバック

OKLCHは[主要なモダンブラウザでサポート](https://caniuse.com/mdn-css_types_color_oklch)されていますが、古いブラウザ向けにフォールバックを提供する場合は `@supports` を活用します。

```css
:root {
  /* フォールバック（sRGB） */
  --gray-900: #181818;

  /* OKLCH対応ブラウザ向け */
  @supports (color: oklch(0 0 0)) {
    --gray-900: oklch(0.21 0 0);
  }
}
```

## まとめ

OKLCHの知覚均質な明度軸（L）を利用することで、カラーパレット設計におけるコントラスト比の確保を「勘と微調整の繰り返し」から「再現可能なステップ」へと移行できます。

- グレースケールで検証したL値の階調をそのまま他の色相へ適用できるため、ブランドカラーや機能色（Success/Warning/Error）への展開が容易になります。
- 本文テキストと背景色にはL値の差を0.70以上確保することで、WCAG 2.x AA基準を確実に満たし、AAA基準も達成できます。
- 公式ガイドライン準拠にはWCAG 2.xを用いつつ、実際の視覚的なバランス調整にはAPCAのLc値を併用するのが効果的です。

デザインシステムやトークン設計の基盤としてOKLCHを導入する際の基準値としてご活用ください。

### 参考リンク

- [OKLCH Color Picker & Converter](https://oklch.com/) — OKLCHの色選び・変換ツール
- [Huetone](https://huetone.ardov.me/) — OKLCHベースのパレット生成ツール
- [WCAG 2.2 — W3C Recommendation](https://www.w3.org/TR/WCAG22/) — アクセシビリティガイドラインの仕様書
- [APCA Contrast Calculator](https://apcacontrast.com/) — APCAのコントラスト計算ツール
- [APCA Readability Criterion (ARC)](https://readtech.org/ARC/) — APCAの推奨基準
- [A perceptual color space for image processing (Björn Ottosson)](https://bottosson.github.io/posts/oklab/) — OKLab/OKLCHの原論文
- [OKLCH in CSS: why we moved from RGB and HSL (Evil Martians)](https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl) — OKLCHの実践的な解説
- [MDN — oklch()](https://developer.mozilla.org/ja/docs/Web/CSS/color_value/oklch) — CSSリファレンス