DevToolBox

OKLCHとHSLの違い - 同じ明度50%なのに明るさが違う問題を解決する

HSL で hsl(60 100% 50%)(黄)と hsl(240 100% 50%)(青)を並べると、同じ「明度 50%」のはずなのに黄色のほうが圧倒的に明るく見えます。 これは HSL が人間の知覚ではなく sRGB の数値構造から機械的に計算された色空間だからです。 CSS Color Module Level 4 で導入された oklch() は、この問題を解決する 知覚均等(perceptually uniform)な色空間です。本記事では HSL の何が問題か、OKLCH の構文、 ブラウザ対応、color-mix() との組み合わせ、デザインシステムでの実践までを整理します。

HSL lightness is computed from sRGB geometry, so two colors with the same L can differ wildly in perceived brightness. OKLCH, added in CSS Color Level 4, fixes this with a perceptually uniform lightness axis. This guide covers the syntax, browser support, and how to use it with color-mix().

TL;DR

Color Converter

HEX / RGB / HSL / OKLCH を相互変換。手元の色が OKLCH でどう表現されるか、L・C・H の値をその場で確認できます。

今すぐ試す →

1. HSL の何が問題か / The problem with HSL

HSL は色相(Hue)・彩度(Saturation)・明度(Lightness)という直感的な軸を持ちますが、 値は sRGB の RGB 値から幾何的に導出されたもので、人間の目の感度は一切考慮されていません。 純色(S=100%, L=50%)を並べると差は歴然です。

色 / ColorHSL相対輝度 / LuminanceOKLCH の L
#ffff00hsl(60 100% 50%)0.9280.968
#00ff00hsl(120 100% 50%)0.7150.866
#ff0000hsl(0 100% 50%)0.2130.628
#0000ffhsl(240 100% 50%)0.0720.452

4色とも HSL では「明度 50%」ですが、相対輝度は 0.072〜0.928 と約13倍の開きがあります。 一方 OKLCH の L はこの見た目の差を素直に反映しています(黄 0.968、青 0.452)。 この性質のため、HSL で「L を固定して H だけ回す」方法でカテゴリカラーやバッジ色を量産すると、 明るさがバラバラでコントラスト比も不揃いなパレットになります。

All four colors above share L=50% in HSL, yet their relative luminance spans a 13x range. OKLCH lightness tracks what you actually see, which is why hue-rotation tricks break in HSL.

2. OKLCH とは - oklch() の構文と読み方 / What OKLCH is

OKLCH は、Björn Ottosson 氏が 2020 年に発表した Oklab 色空間を 明度 L・彩度 C・色相 H の極座標で表したものです。CSS Color Module Level 4 にoklch() 関数として採用されています。

/* oklch(明度 彩度 色相 / 不透明度) */
color: oklch(0.7 0.15 230);        /* 落ち着いた青 */
color: oklch(70% 0.15 230);        /* L はパーセント表記でも可 (100% = 1) */
color: oklch(0.7 0.15 230 / 0.5);  /* アルファ付き */
成分 / Channel範囲 / Range意味 / Meaning
L (Lightness)0〜1(0%〜100%)知覚的明度。0 が黒、1 が白
C (Chroma)0〜(sRGB では概ね 0.37 まで)彩度。0 で無彩色。上限は色相・明度に依存
H (Hue)0〜360(deg)色相角。HSL の色相角とは一致しない

注意点は2つ。第一に、H の値は HSL と互換性がありません(例: 青は HSL では 240、OKLCH では約 264)。 機械的に数値を移植せず、Color Converter で変換してください。 第二に、C を上げすぎると sRGB で表示できないガモット外の色になりますが、その場合も CSS は エラーにせず、ブラウザが表示可能な近い色へマッピングします。

OKLCH is the polar form of Oklab (Ottosson, 2020). Syntax: oklch(L C H / alpha). Hue angles do NOT match HSL hues, and out-of-gamut chroma is gamut-mapped by the browser rather than erroring.

3. ブラウザ対応とフォールバック / Browser support

機能 / FeatureChrome / EdgeFirefoxSafari
oklch()111+ (2023-03)113+ (2023-05)15.4+ (2022-03)
color-mix(in oklch, ...)111+113+16.2+
相対カラー構文 (from)119+128+16.4+

2026年6月時点で、上記すべてが全モダンブラウザで利用可能です。 古い WebView や社内の固定バージョン環境を抱える場合だけ、CSS のカスケードを使った定石のフォールバックを入れます。

.button {
  background: #3b82f6;                  /* 旧環境向けフォールバック */
  background: oklch(0.623 0.214 259.8); /* 対応ブラウザはこちらを採用 */
}

/* 分岐が必要なら @supports でも判定できる */
@supports (color: oklch(0 0 0)) {
  .badge { background: oklch(0.75 0.12 145); }
}

As of 2026 every modern browser supports oklch(), color-mix() in OKLCH, and relative color syntax. For legacy WebViews, declare a hex value first and let the cascade pick oklch().

4. color-mix() と相対カラー構文で色を派生させる / Deriving colors

OKLCH が真価を発揮するのは色の派生です。sRGB 空間で色を混ぜると中間色が濁る (灰色がかる)ことがありますが、in oklch を指定すると知覚的に自然な中間色になります。

:root { --brand: oklch(0.623 0.214 259.8); }

/* hover / active を brand 色から派生させる */
.button:hover  { background: color-mix(in oklch, var(--brand), white 20%); }
.button:active { background: color-mix(in oklch, var(--brand), black 15%); }

さらに相対カラー構文(Relative Color Syntax)を使えば、混色ではなくL・C・H を直接演算して派生色を定義できます。

/* brand 色の L だけ +0.15(明るいバリアント) */
.brand-light { color: oklch(from var(--brand) calc(l + 0.15) c h); }

/* C を半分にして落ち着いたトーンに(色相・明度は維持) */
.brand-muted { color: oklch(from var(--brand) l calc(c * 0.5) h); }

HSL で同じことをすると「L を足したら色相によって白飛びしたり、ほとんど変わらなかったり」という ムラが出ますが、OKLCH なら +0.15 がどの色相でもほぼ同じ「明るくなり方」になります。

color-mix(in oklch, ...) avoids the muddy midpoints of sRGB mixing, and relative color syntax lets you do arithmetic on L, C and H directly — a +0.15 lightness step looks the same at any hue.

5. デザインシステムでの採用 / Adoption in design systems

Tailwind CSS v4(2025年1月リリース)は、デフォルトカラーパレットの定義を OKLCH に切り替えました。 デザインシステムでの基本パターンは「H をほぼ固定し、L を段階的に動かしてシェイドを作る」です。

:root {
  /* H を 259.8 に固定し、L 段階でシェイドを構成 */
  --blue-300: oklch(0.81 0.10 259.8);
  --blue-500: oklch(0.62 0.21 259.8);
  --blue-700: oklch(0.46 0.18 259.8);
}

L の刻みを揃えておけば、別の色相(green、red など)でも同じ L 値を使うだけで トーンの揃ったパレットになります。これは HSL では原理的に不可能だったことです。 ひとつ注意として、OKLCH の L 差は WCAG コントラスト比の保証にはなりません(コントラスト比は相対輝度ベースの別計算)。テキスト色の最終確認はColor Contrast Checker で行ってください。

Tailwind CSS v4 defines its default palette in OKLCH. Fix the hue, step the lightness, and you get tonally consistent shades across hues. Note that equal L steps do not guarantee WCAG contrast ratios — always verify text colors with a contrast checker.

まとめ / Summary

HSL の明度は sRGB の計算値にすぎず、色相をまたいだ明るさの統一には使えません。 OKLCH は L=知覚的明度という保証があるため、パレット設計・色の派生・テーマ生成のすべてが 予測可能になります。ブラウザ対応の心配は 2026 年時点で実質不要です。 新規のデザイントークンは OKLCH で定義し、既存の HSL は弱点が出る場面から順次置き換えるのが現実解です。

HSL looks intuitive but its lightness channel is a geometric artifact of the sRGB cube: pure yellow and pure blue both sit at L=50% while their relative luminance differs by a factor of 13. OKLCH, the polar form of the Oklab color space, makes lightness perceptually uniform, so the same L value looks equally bright at any hue. The CSS syntax is oklch(L C H / alpha), where L runs 0–1, chroma starts at 0 (about 0.37 max within sRGB), and hue is an angle that does not match HSL hues. Support has been universal since 2023 — Chrome 111, Firefox 113, Safari 15.4 — so in 2026 you can use it without hesitation, adding a hex fallback only for legacy WebViews. Combine it with color-mix(in oklch, ...) for clean hover states and with relative color syntax to derive variants by adjusting L or C directly. Tailwind CSS v4 already ships its palette in OKLCH; new design tokens should follow suit.

Color Converter

手持ちの HEX / HSL の色を OKLCH に変換して、L・C・H の実際の値を確認。パレットの OKLCH 移行はここから始められます。

今すぐ試す →

関連ツール / Related tools

関連ガイド / Related guides