color-mix()
問題
CSSでティント、シェード、半透明のカラーバリアントを作成するには、従来はすべての色値を手動で計算する必要がありました。AIエージェントはすべてのシェードを個別の hex や rgb 値としてハードコードしがちで(例:#3366cc、#5588dd、#99bbee を独立して生成)、パレットが脆く保守しにくくなります。ベースのブランドカラーが変わると、すべての派生値を再計算する必要があります。Sassのような CSSプリプロセッサは lighten() や darken() でこれを解決しましたが、ネイティブCSSには同等の機能がありませんでした — color-mix() が登場するまでは。
解決方法
color-mix() は、指定されたカラースペースで2つの色をブレンドし、結果の色を返すCSS関数です。カラースペース、2つの色、およびミックス比率を制御するオプションのパーセンテージ値を取ります。
color-mix(in <color-space>, <color> <percentage>?, <color> <percentage>?)
カラースペースパラメータは、補間の計算方法を決定します。oklch や oklab を使うと、srgb よりも知覚的に均一なブレンドが得られます。
コード例
基本構文
:root {
--brand: oklch(55% 0.25 264);
/* Mix 70% brand with 30% white = a lighter tint */
--brand-light: color-mix(in oklch, var(--brand) 70%, white);
/* Mix 70% brand with 30% black = a darker shade */
--brand-dark: color-mix(in oklch, var(--brand) 70%, black);
/* Equal mix of two colors */
--blend: color-mix(in oklch, var(--brand), orange);
}
単一ベースカラーからティントとシェードを作成
:root {
--brand: oklch(50% 0.22 264);
/* Tints (lighter) — mixing with white */
--brand-50: color-mix(in oklch, var(--brand) 5%, white);
--brand-100: color-mix(in oklch, var(--brand) 10%, white);
--brand-200: color-mix(in oklch, var(--brand) 25%, white);
--brand-300: color-mix(in oklch, var(--brand) 40%, white);
--brand-400: color-mix(in oklch, var(--brand) 60%, white);
/* Base */
--brand-500: var(--brand);
/* Shades (darker) — mixing with black */
--brand-600: color-mix(in oklch, var(--brand) 80%, black);
--brand-700: color-mix(in oklch, var(--brand) 60%, black);
--brand-800: color-mix(in oklch, var(--brand) 40%, black);
--brand-900: color-mix(in oklch, var(--brand) 25%, black);
}
color-mix() — ティント、シェード、半透明バリアント
半透明バリアント
transparent とミックスすることで、任意の色の半透明バージョンを作成できます。ホバー状態、オーバーレイ、背景に特に便利です:
:root {
--brand: oklch(55% 0.25 264);
/* Semi-transparent variants */
--brand-alpha-10: color-mix(in oklch, var(--brand) 10%, transparent);
--brand-alpha-20: color-mix(in oklch, var(--brand) 20%, transparent);
--brand-alpha-50: color-mix(in oklch, var(--brand) 50%, transparent);
}
.hover-card:hover {
background-color: var(--brand-alpha-10);
}
.overlay {
background-color: var(--brand-alpha-50);
}
.subtle-border {
border-color: var(--brand-alpha-20);
}
インタラクティブステートカラー
:root {
--btn-bg: oklch(55% 0.22 264);
--btn-hover: color-mix(in oklch, var(--btn-bg), white 15%);
--btn-active: color-mix(in oklch, var(--btn-bg), black 15%);
--btn-disabled: color-mix(in oklch, var(--btn-bg) 40%, oklch(70% 0 0));
}
.button {
background: var(--btn-bg);
}
.button:hover {
background: var(--btn-hover);
}
.button:active {
background: var(--btn-active);
}
.button:disabled {
background: var(--btn-disabled);
}
カラースペースの比較
:root {
--red: oklch(60% 0.25 30);
--blue: oklch(55% 0.25 264);
/* sRGB interpolation — can produce muddy, desaturated results */
--mix-srgb: color-mix(in srgb, var(--red), var(--blue));
/* oklch interpolation — maintains vibrancy through the blend */
--mix-oklch: color-mix(in oklch, var(--red), var(--blue));
}
単一ベースカラーからの動的テーマ
:root {
--base: oklch(55% 0.2 264);
--surface: color-mix(in oklch, var(--base) 5%, white);
--surface-raised: color-mix(in oklch, var(--base) 10%, white);
--border: color-mix(in oklch, var(--base) 20%, oklch(80% 0 0));
--text: color-mix(in oklch, var(--base) 40%, black);
--text-muted: color-mix(in oklch, var(--base) 30%, oklch(50% 0 0));
--accent: var(--base);
--accent-hover: color-mix(in oklch, var(--base), white 20%);
}
隣接するカラートークンのブレンド
:root {
--success: oklch(60% 0.2 145);
--warning: oklch(70% 0.2 85);
/* Blend between semantic colors for status transitions */
--status-improving: color-mix(in oklch, var(--warning) 60%, var(--success));
}
AIがよくやるミス
color-mix()で単一ベースからティントとシェードを導出する代わりに、すべての色シェードを個別の hex 値としてハードコードしているin oklchやin oklabの方が視覚的に均一な結果を生むのに、ブレンドにin srgbを使っている — sRGBブレンドは特に彩度の高い色間でくすんだ中間点を作る- カラースペースパラメータを完全に省略している — これは仕様で必須です
- パーセンテージの意味を混同している:
color-mix(in oklch, red 70%, blue)は70%の赤と30%の青を意味し、青の70%を加えるという意味ではない color-mix()がネイティブで処理できる色操作に JavaScript や CSSプリプロセッサ(sass darken()、lighten())を使っているtransparentとミックスすることで色の半透明バージョンが作れることを知らない — AIエージェントは今でもrgba()や手動のアルファ値を使いがち- 明度を調整したシンプルな
oklch()値の方がより明確で保守しやすい場面でcolor-mix()を使っている
使い分け
- デザインシステム: 単一のブランドカラーからシェードスケール全体を導出
- インタラクティブステート: hover、active、focus、disabled のカラーバリアントを体系的に生成
- 半透明オーバーレイ: rgba 値をハードコードせずにセマンティックカラーのアルファバリアントを作成
- テーマカスタマイズ: ユーザーにベースカラーを選ばせ、そこからすべてのUIカラーを導出
- セマンティックトークンのブレンド: ステータス遷移のために success/warning/danger カラー間をミックス
使わない方がよい場面
- ベースに関連付ける必要のないシンプルな静的カラー —
oklch()やhexを直接使いましょう - すべてのシェードが、白/黒との単純なミックスに従わない、デザイナーが正確に指定した値を必要とする場合
- 正確な仕様に一致する必要がある重要なブランドカラー(ミックス結果は補間カラースペースに依存します)