color-palette について
起点となる色から、OKLCH のスケールやセマンティックトークンを含む配色システムを作るデザインスキルです。CSS や Tailwind に色を適用するデザイナーや開発者向けです。
主な機能
- 0150〜950 の OKLCH スケール生成
- 02ブランド・セマンティック・ニュートラルトークンの定義
- 03WCAG コントラストの確認
- 04ダークモードのトークン割り当て提案
- 05Tailwind 設定と CSS 変数の作成
- 06色見本の確認と design-context 更新の案内
活用シーン
- 01ブランドカラーから色階調を展開
- 02テキストと背景のコントラスト確認
- 03CSS や Tailwind 用の色トークン作成
- 04ダークテーマ向けのセマンティックカラー割り当て
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arnavpuri/ designskills --skill color-paletteインストール後、Claude Code で/color-paletteを入力して読み込まれたか確認します。
同梱ファイルscripts/palette.py
詳細
カラーパレットの生成
任意の開始色から、調和が取れ、アクセシブルで、本番環境に対応したカラーシステムを生成します。
前提条件
色を生成する前に、既存のデザインコンテキストを確認します。
.agents/design-context.md(design-contextスキルを参照)を読みます。Color System が定義されている場合、その HEX 値を正として扱い、置き換えるのではなく拡張してください。- コードベースに既存のデザインシステム(CSS 変数、Tailwind テーマ、
tokens.json)がある場合、置き換えるのではなく拡張してください。
命名規則(design-system、layout-composition などと共有): プリミティブスケールは --brand-50 … --brand-950、--gray-50 … --gray-950、--success-*、--warning-*、--error-*、--info-* です。セマンティックトークンは --bg-*、--text-*、--border-*、--interactive-* に加え、ブランドエイリアスの --color-primary、--color-secondary、--color-accent です。
ステップ 1:色彩理論の基本を理解する
パレットは OKLCH で生成します。明度チャンネルは知覚的に均等なため、明度の変化量が一定なら、どの色相でも同じ視覚的な変化になります(HSL では、「同じ」明度でも hsl(60 100% 50%) の黄色は hsl(240 100% 50%) の青よりはるかに明るく見えます)。従来のツールで必要な場合に限り、HSL を使用します。彩度の高い OKLCH 値は sRGB の範囲外になることがあるため、HEX に変換する際は明度と色相を保ったまま彩度を下げてください。
色相環の関係
| 調和 | 基準色からの角度 | 印象 |
|---|---|---|
| 補色 | 180° | 高コントラストで、活気がある |
| 類似色 | ±30° | 調和が取れ、自然 |
| 三色配色 | 120°、240° | 鮮やかで、バランスが取れている |
| 分裂補色 | 150°、210° | 緊張感を抑えたコントラスト |
| 四色配色 | 90°、180°、270° | 豊かだが、慎重なバランスが必要 |
OKLCH で調和色を生成する
:root {
/* Base brand color in oklch */
--brand-hue: 250; /* hue angle */
--brand-chroma: 0.15; /* chroma (not HSL saturation) */
--brand-l: 0.55; /* lightness */
--color-primary: oklch(var(--brand-l) var(--brand-chroma) var(--brand-hue));
--color-complement: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 180));
--color-analogous-1: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 30));
--color-analogous-2: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) - 30));
--color-triadic-1: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 120));
--color-triadic-2: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 240));
--color-split-comp-1: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 150));
--color-split-comp-2: oklch(var(--brand-l) var(--brand-chroma) calc(var(--brand-hue) + 210));
}
ステップ 2:色の濃淡スケール全体を生成する(50~950)
1 色のブランドカラーから、明度を 11 段階にしたスケール(50、100~900、950)を生成します。OKLCH では、色相を一定に保ち、両端で彩度を少し下げながら明度を変化させます。
明度マップ
| トークン | OKLCH 明度 | 用途 |
|---|---|---|
| 50 | 0.97 | 色味のある背景 |
| 100 | 0.93 | ホバー時の背景 |
| 200 | 0.87 | アクティブ時の背景 |
| 300 | 0.78 | ボーダー |
| 400 | 0.68 | プレースホルダーテキスト |
| 500 | 0.55 | 基本色/主要なアクション |
| 600 | 0.48 | 主要なアクションのホバー時 |
| 700 | 0.40 | 主要なアクションのアクティブ時 |
| 800 | 0.32 | 明るい背景上の見出し |
| 900 | 0.24 | 明るい背景上の本文テキスト |
| 950 | 0.16 | 高コントラストのテキスト |
CSS の実装
:root {
--brand-hue: 250;
--brand-chroma: 0.15;
--brand-50: oklch(0.97 calc(var(--brand-chroma) * 0.3) var(--brand-hue));
--brand-100: oklch(0.93 calc(var(--brand-chroma) * 0.4) var(--brand-hue));
--brand-200: oklch(0.87 calc(var(--brand-chroma) * 0.5) var(--brand-hue));
--brand-300: oklch(0.78 calc(var(--brand-chroma) * 0.7) var(--brand-hue));
--brand-400: oklch(0.68 calc(var(--brand-chroma) * 0.85) var(--brand-hue));
--brand-500: oklch(0.55 var(--brand-chroma) var(--brand-hue));
--brand-600: oklch(0.48 var(--brand-chroma) var(--brand-hue));
--brand-700: oklch(0.40 var(--brand-chroma) var(--brand-hue));
--brand-800: oklch(0.32 calc(var(--brand-chroma) * 0.9) var(--brand-hue));
--brand-900: oklch(0.24 calc(var(--brand-chroma) * 0.8) var(--brand-hue));
--brand-950: oklch(0.16 calc(var(--brand-chroma) * 0.7) var(--brand-hue));
}
ステップ 3:セマンティックカラーを定義する
用途別のトークンをパレットに割り当てるか、独立した色相を定義します。
推奨するセマンティックカラーの色相
| 役割 | 色相(OKLCH) | 理由 |
|---|---|---|
| 成功 | 145 | 緑。普遍的な「進め」の色 |
| 警告 | 85 | 琥珀色/黄色。注意喚起 |
| エラー | 25 | 赤。緊急性 |
| 情報 | 230 | 青。中立的な情報 |
:root {
/* Semantic: success */
--success-50: oklch(0.97 0.04 145);
--success-400: oklch(0.68 0.15 145); /* dark-mode foreground */
--success-500: oklch(0.55 0.16 145);
--success-700: oklch(0.40 0.14 145);
/* Semantic: warning */
--warning-50: oklch(0.97 0.04 85);
--warning-400: oklch(0.72 0.15 85);
--warning-500: oklch(0.60 0.16 85); /* ~4:1 on white: use dark text ON it, 700 for warning TEXT */
--warning-700: oklch(0.45 0.14 85);
/* Semantic: error */
--error-50: oklch(0.97 0.04 25);
--error-400: oklch(0.68 0.16 25);
--error-500: oklch(0.55 0.18 25);
--error-700: oklch(0.40 0.16 25);
/* Semantic: info */
--info-50: oklch(0.97 0.04 230);
--info-400: oklch(0.68 0.12 230);
--info-500: oklch(0.55 0.14 230);
--info-700: oklch(0.40 0.12 230);
}
製品で必要な場合(アラート、バッジ、グラフなど)に限り、セマンティックカラーの色相について 50~950 のスケール全体を生成します。ほとんどの UI では 50/400/500/700 で十分です。
ステップ 4:ニュートラルスケールを生成する
ニュートラルカラーは基盤となる要素です。ブランドカラーの色相をわずかに加えて、温かみを持たせます。
:root {
--neutral-hue: var(--brand-hue);
--neutral-chroma: 0.01; /* barely tinted */
--gray-50: oklch(0.98 var(--neutral-chroma) var(--neutral-hue));
--gray-100: oklch(0.96 var(--neutral-chroma) var(--neutral-hue));
--gray-200: oklch(0.90 var(--neutral-chroma) var(--neutral-hue));
--gray-300: oklch(0.83 var(--neutral-chroma) var(--neutral-hue));
--gray-400: oklch(0.71 var(--neutral-chroma) var(--neutral-hue));
--gray-500: oklch(0.55 var(--neutral-chroma) var(--neutral-hue));
--gray-600: oklch(0.45 var(--neutral-chroma) var(--neutral-hue));
--gray-700: oklch(0.37 var(--neutral-chroma) var(--neutral-hue));
--gray-800: oklch(0.27 var(--neutral-chroma) var(--neutral-hue));
--gray-900: oklch(0.20 var(--neutral-chroma) var(--neutral-hue));
--gray-950: oklch(0.13 var(--neutral-chroma) var(--neutral-hue));
}
ステップ 5:WCAG アクセシビリティを確保する
コントラスト要件(WCAG 2.2 -- 現行の法的/業界標準)
| レベル | 通常のテキスト | 大きなテキスト(≥24px、または ≥18.66px/14pt 太字) | テキスト以外の UI(アイコン、境界線、フォーカスリング、グラフのマーク) |
|---|---|---|---|
| WCAG AA | 4.5:1 | 3:1 | 隣接色とのコントラストが 3:1(SC 1.4.11) |
| WCAG AAA | 7:1 | 4.5:1 | -- |
コントラスト比 = (L1 + 0.05) / (L2 + 0.05)。L1/L2 は、線形化した sRGB における明るい方/暗い方の相対輝度です(0.2126 R + 0.7152 G + 0.0722 B)。比率は切り上げません。4.49:1 は不適合です。APCA(Lc 値)は WCAG 3 の草案候補です。特にダークモードではセカンドオピニオンとして有用ですが、適合性の基準として報告してはいけません。
実践的なルール
- 明るい背景上の本文:900 または 950 の色合いを使います(上記の明度マップでは AA に十分適合します)。
- 暗い背景上の本文:50 または 100 の色合いを使います。
- プライマリボタン:500 以上の色合いに白いテキストを使うと、通常は適合します(L 0.55 の青は約 4.9:1)。ただし、L 0.55~0.65 の黄色/アンバー/シアンは不適合になることが多いため、暗い色のテキストを使います。必ず測定してください。
- 無効状態:明るい背景には 400 の色合いを使います。無効な要素は WCAG の適用対象外ですが、それでも見分けられるようにしてください。
- リンク:色だけに頼らず、周囲のテキストと区別できる必要があります。下線を付けるか、周囲のテキストとのコントラストを 3:1 にし、さらにホバー/フォーカス時に色以外の手掛かりを加えます。
OKLCH の明度を使ったコントラストの簡易チェック
ほぼ無彩色の色では、相対輝度 ≈ L³ となるため、極端な明度の場合に 4.5:1 を得るには約 0.42~0.45 以上の明度差が必要です(中間域ではさらに大きな差が必要です)。候補選びにのみ使い、その後に測定してください。
text L 0.20 | bg L 0.97 | diff 0.77 -> ~16.6:1, passes AAA
text L 0.55 | bg L 0.97 | diff 0.42 -> ~4.5:1, borderline AA -- measure; safe for large text
text L 0.57 | bg L 0.97 | diff 0.40 -> ~4.1:1, FAILS AA for normal text
推測せず、測定する
ブラウザーは OKLCH を直接描画しますが、デザインコンテキストでは HEX を保存し、コントラストは sRGB で計算する必要があります。同梱スクリプトを使います(<color-palette> と <design-context> は各スキルのディレクトリです)。
python <color-palette>/scripts/palette.py --from "#2563eb" # scale around a brand color
python <color-palette>/scripts/palette.py 250 0.15 --name brand --css # hue + chroma -> CSS vars
python <color-palette>/scripts/palette.py 250 0.02 --name gray --json # neutrals as JSON
python <design-context>/scripts/contrast.py "#ffffff" "#2563eb" # any real pairing
palette.py は各段階の HEX 値と、白/黒に対するコントラストを出力し、AA(4.5:1)、large/UI(3:1)、または - のマークを付けます。色域外の段階では、明度や色相ではなく彩度が下がります。contrast.py は不適合の場合に終了コード 1 で終了し、最も近い適合色合いを提案します。
実際に提供する、テキストと背景のすべての組み合わせ(--bg-* 上のテキスト、--interactive-* 上のボタンラベル、ダークモードの組み合わせ)も確認してください。純粋な白/黒との比較だけでは不十分です。
ステップ 6:ダークモードへの適応
単純にスケールを反転しないでください。代わりに、セマンティックな用途を再マッピングします。
ダークモードの方針
[data-theme="dark"] {
/* Backgrounds: use 900/950 instead of 50/white */
--bg-primary: var(--gray-950);
--bg-secondary: var(--gray-900);
--bg-tertiary: var(--gray-800);
/* Text: use 50/100 instead of 900/950 */
--text-primary: var(--gray-50);
--text-secondary: var(--gray-300);
--text-tertiary: var(--gray-400);
/* Surfaces: slightly lighter than bg */
--surface: var(--gray-900);
--surface-hover: var(--gray-800);
/* Borders: use 700 instead of 200 */
--border-default: var(--gray-700);
/* Brand colors: increase lightness for dark bg */
--interactive-primary: var(--brand-400); /* lighter than 500 */
/* Semantic: bump to lighter shades */
--color-success: var(--success-400);
--color-warning: var(--warning-400);
--color-error: var(--error-400);
}
これらの名前は、ステップ 10 のライトモードのレイヤー 2 トークンと一致させる必要があります。ダークモードでは、それらを再マッピングするだけです。完全なダークテーマ(エレベーションサーフェス、画像、トグル)については、dark-mode スキルに引き継いでください。
主な原則
- コントラストを少し下げます。純白と純黒の組み合わせは刺激が強いため、gray-950 上では gray-50 を使います。
- ダークモードでは彩度を少し下げます。彩度の高い色は暗い背景で発光しているように見えます。
- ダークモードのエレベーションには、より明るいサーフェスを使います(ライトモードのシャドウとは逆です)。
ステップ 7:業界別の色彩心理
| 業界 | 主な色 | 理由 |
|---|---|---|
| 金融 | ネイビー、ダークブルー、緑 | 信頼、安定、成長 |
| 医療・健康 | 青、ティール、白 | 落ち着き、清潔感、信頼 |
| 食品 | 赤、オレンジ、黄色 | 食欲、活力、温かみ |
| テクノロジー/SaaS | 青、バイオレット、シアン | 革新、信頼性 |
| 高級品 | 黒、金、濃い紫 | 優雅さ、特別感 |
| 環境・エコ | 緑、アースカラー | 自然、持続可能性 |
| 教育 | 青、オレンジ、緑 | 信頼、熱意、成長 |
| クリエイティブ | 鮮やかなプライマリカラー、ネオンカラー | 活力、自己表現、楽しさ |
ステップ 8:グラデーションの組み合わせ
効果的なグラデーションのルール
- 控えめなグラデーションにするには、色相の回転を 60 度以内に保ちます。
- 鮮やかでエネルギッシュなグラデーションには、90〜180 度を使用します。
- 停止色を
oklch()で記述しても、グラデーションが OKLCH で補間されるわけではありません。補間ヒントを指定しない場合、ブラウザーはモダンな色には Oklab を、hex/rgb/hsl の停止色には sRGB を使用します。どちらも色空間を直線的に進むため、色相が離れていると、その間にあるグレーを通ることがあります。色相環に沿って補間するにはin oklchを追加します(虹色のように色相を一周させるにはlonger hueを追加します)。 - または、中間色に彩度を少し加えて、「グレーのデッドゾーン」を避けます。
/* Subtle brand gradient */
.gradient-subtle {
background: linear-gradient(
135deg in oklch,
oklch(0.55 0.15 250),
oklch(0.55 0.15 280)
);
}
/* Vibrant hero gradient */
.gradient-vibrant {
background: linear-gradient(
135deg in oklch,
oklch(0.65 0.20 280),
oklch(0.60 0.22 330),
oklch(0.65 0.20 20)
);
}
/* Mesh gradient (modern trend) */
.gradient-mesh {
background:
radial-gradient(at 0% 0%, oklch(0.70 0.15 280) 0%, transparent 50%),
radial-gradient(at 100% 0%, oklch(0.70 0.15 200) 0%, transparent 50%),
radial-gradient(at 100% 100%, oklch(0.70 0.15 330) 0%, transparent 50%),
oklch(0.97 0.01 250);
}
ステップ 9:Tailwind 設定の生成
Tailwind v4(CSS ファースト)――スケールを @theme に記述すると、bg-brand-500、text-brand-900 などが生成されます。
@import "tailwindcss";
@theme {
--color-brand-50: oklch(0.97 0.045 250);
--color-brand-500: oklch(0.55 0.15 250);
--color-brand-900: oklch(0.24 0.12 250);
/* ...every step 50-950, plus success/warning/error/info */
}
Tailwind v3(tailwind.config.js):
// tailwind.config.js
const brand = {
50: 'oklch(0.97 0.045 250)',
100: 'oklch(0.93 0.06 250)',
200: 'oklch(0.87 0.075 250)',
300: 'oklch(0.78 0.105 250)',
400: 'oklch(0.68 0.1275 250)',
500: 'oklch(0.55 0.15 250)',
600: 'oklch(0.48 0.15 250)',
700: 'oklch(0.40 0.15 250)',
800: 'oklch(0.32 0.135 250)',
900: 'oklch(0.24 0.12 250)',
950: 'oklch(0.16 0.105 250)',
};
module.exports = {
theme: {
extend: {
colors: {
brand,
success: { /* ... same pattern, hue 145 */ },
warning: { /* ... same pattern, hue 85 */ },
error: { /* ... same pattern, hue 25 */ },
info: { /* ... same pattern, hue 230 */ },
},
},
},
};
ステップ 10:CSS カスタムプロパティのアーキテクチャ
トークンを 3 層に分けて構成します。
/* Layer 1: Primitive (raw values) */
:root {
--brand-500: oklch(0.55 0.15 250);
--error-500: oklch(0.55 0.18 25);
/* ...full --brand-*, --gray-*, semantic scales from Steps 2-4 */
}
/* Layer 2: Semantic (purpose) -- same names design-system uses */
:root {
--color-primary: var(--brand-500);
--interactive-primary: var(--brand-500);
--color-error: var(--error-500);
--bg-primary: var(--gray-50);
--text-primary: var(--gray-900);
--border-default: var(--gray-200);
}
/* Layer 3: Component (scoped) */
.btn-primary {
background: var(--interactive-primary);
color: white; /* verified >= 4.5:1 against brand-500 */
}
この 3 層構成により、テーマ設定とダークモードの実装が簡単になります。レイヤー 2 を再マッピングするだけです。
ステップ 11:視覚的に確認してデザインコンテキストに保存する
- スウォッチシートをレンダリングする:小さな HTML ファイル(スクラッチパッド)を作成し、各スケールをスウォッチの行として表示します。トークン、hex 値、白/黒とのコントラストをラベルに記載し、ライトモードとダークモードの両方でテキストとボタンの組み合わせも表示します。スクリーンショットを撮影し(例:
npx playwright screenshot --full-page "file://$PWD/swatches.html" swatches.png。ダークモードで撮影する場合は--color-scheme=darkを追加します)、Read で画像を確認します。各段階が均等に見えること、濁った色やネオンのような色がないこと、ニュートラルカラーがニュートラルに見えることを確認します。 .agents/design-context.md(design-contextスキルのファイル)への書き戻しを提案する。変更された Color System の行だけを更新し、ファイル全体を書き換えないでください。スケールの値をデザインコンテキストの役割に、hex 値として対応付けます。
| デザインコンテキストの役割 | 使用する値 |
|---|---|
| プライマリ | --brand-500(または白いテキストとのコントラスト比が 4.5:1 を満たさない場合は 600) |
| プライマリライト/プライマリダーク | --brand-400 / --brand-700 |
| セカンダリ/アクセント | セカンダリ/アクセントの色相の 500 段階 |
| ニュートラル 50/100/200/500/800/900 | 対応する --gray-* の段階 |
| 成功/警告/エラー | --success-500 / --warning-500 / --error-500 |
変更した行の変更前/変更後の差分をユーザーに提示し、保存する前に確認を求めます。
クイックリファレンス:1 つの hex 値からパレットを作成する
.agents/design-context.mdを読み込み、ブランドの hex 値を OKLCH に変換します。- 色相を抽出します。これがブランドの色相です。
- ステップ 2 の明度マップを使用して、50〜950 のスケールを生成します。
- ブランドの色相で色味を付けたニュートラルカラーを生成します(ステップ 4)。
- セマンティックカラーの色相を選びます(ステップ 3)。
palette.py/contrast.pyでコントラストを測定します(ステップ 5)。続行する前に、基準を満たさない箇所を修正します。- ダークモード用の再マッピングを作成します(ステップ 6)。
- CSS カスタムプロパティまたは Tailwind 設定としてエクスポートします。
- スウォッチシートをレンダリングして確認し、hex 値をデザインコンテキストに保存することを提案します(ステップ 11)。