shadcn/ui カラートークン早見表:ブランドカラー最適化&明暗コントラスト基準

shadcn/ui を使ったプロジェクトで、ブランドカラーをどう適用すればいいのか迷ったことはありませんか?CSS 変数として定義されたカラートークンの種類が多く、どれを使えばアクセシビリティ基準を満たせるのか、明暗テーマでどう切り替わるのかがわかりづらいですよね。
この記事では、shadcn/ui のカラートークン体系を一覧表で整理し、ブランドカラーの最適な割り当て方と、WCAG 準拠の明暗コントラスト基準を具体的に解説します。
カラートークン早見表
shadcn/ui で使用される主要なカラートークンの一覧です。ブランドカラーの適用や、ライト/ダークモードの切り替えの参考にしてください。
# | トークン名 | 役割 | 主な用途 | 対となるトークン | ブランド適用 |
---|---|---|---|---|---|
1 | --background | ページ全体の背景色 | body 、ページ背景 | --foreground | - |
2 | --foreground | ページ全体のテキスト色 | 本文、見出し | --background | - |
3 | --card | カードコンポーネントの背景色 | カード、パネル | --card-foreground | - |
4 | --card-foreground | カード内のテキスト色 | カード内の文字 | --card | - |
5 | --popover | ポップオーバーの背景色 | ドロップダウン、ツールチップ | --popover-foreground | - |
6 | --popover-foreground | ポップオーバー内のテキスト色 | ポップオーバー内の文字 | --popover | - |
7 | --primary | 主要なアクションを示すブランドカラー | ボタン、リンク、CTA | --primary-foreground | ★ |
8 | --primary-foreground | プライマリ色の上に重なるテキスト色 | ボタン内の文字 | --primary | ★ |
9 | --secondary | 副次的なアクションを示す色 | セカンダリボタン、バッジ | --secondary-foreground | - |
10 | --secondary-foreground | セカンダリ色の上に重なるテキスト色 | セカンダリボタン内の文字 | --secondary | - |
11 | --muted | 控えめな背景色(無効状態、補助的な要素) | 無効ボタン、キャプション背景 | --muted-foreground | - |
12 | --muted-foreground | 控えめなテキスト色 | キャプション、ヘルプテキスト | --muted | - |
13 | --accent | 強調したい要素に使う色 | ホバー時の背景、アクティブ状態 | --accent-foreground | ★ |
14 | --accent-foreground | アクセント色の上に重なるテキスト色 | アクセント背景上の文字 | --accent | - |
15 | --destructive | 削除や危険なアクションを示す色 | 削除ボタン、エラーメッセージ背景 | --destructive-foreground | - |
16 | --destructive-foreground | デストラクティブ色の上に重なるテキスト色 | 削除ボタン内の文字 | --destructive | - |
17 | --border | 枠線の色 | カードの枠、入力フィールドの枠 | - | - |
18 | --input | 入力フィールドの枠線色 | テキストボックス、セレクトボックス | - | - |
19 | --ring | フォーカス時のリング色 | フォーカスインジケーター | - | ★ |
20 | --radius | ボーダーの丸み(単位: rem) | ボタン、カードの角丸 | - | - |
表の見方
- ブランド適用列(★): ブランドカラーを適用すべき主要なトークンを示しています
- 対となるトークン: テキストと背景のコントラスト比を確保するために、ペアで設定すべきトークンを示しています
背景
shadcn/ui のカラーシステム
shadcn/ui は Tailwind CSS と CSS 変数を組み合わせた独自のカラーシステムを採用しています。
このシステムの特徴は、セマンティックなカラートークンを使うことで、ライト/ダークモードの切り替えやブランドカラーの適用が容易になる点にあります。
typescript// globals.css の例
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
/* その他のトークン */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
}
}
HSL 形式とテーマ切り替え
shadcn/ui では、カラー値を HSL(Hue Saturation Lightness) 形式で定義します。
H(色相)、S(彩度)、L(明度)の 3 つの値を持ち、明暗テーマの切り替え時に L(明度)を調整するだけで、色相を保ったまま視認性を確保できるのです。
以下の図は、ライトモードとダークモードでカラートークンがどのように切り替わるかを示しています。
mermaidflowchart LR
root["ライトモード<br/>(`:root`)"] -->|"--background:<br/>0 0% 100%"| bgLight["背景: 白"]
root -->|"--primary:<br/>222.2 47.4% 11.2%"| primLight["プライマリ: 濃紺"]
dark["ダークモード<br/>(`.dark`)"] -->|"--background:<br/>222.2 84% 4.9%"| bgDark["背景: 濃紺"]
dark -->|"--primary:<br/>210 40% 98%"| primDark["プライマリ: 白"]
このように、同じトークン名(--background
、--primary
)を使いながら、テーマごとに異なる HSL 値を割り当てることで、コード側では bg-background
や text-primary
といった Tailwind クラスを使うだけで、自動的にテーマに応じた色が適用されます。
課題
カラートークンの種類と役割が不明瞭
shadcn/ui には primary
、secondary
、accent
、muted
、destructive
など、複数のカラートークンが用意されています。
しかし、それぞれの役割や使い分けが公式ドキュメントだけでは十分に理解しにくく、どのトークンにブランドカラーを割り当てるべきか迷ってしまうことがあります。
コントラスト比の確保が難しい
アクセシビリティ基準(WCAG 2.1)では、テキストと背景のコントラスト比が 4.5:1 以上(通常のテキスト)または 3:1 以上(大きなテキスト)であることが求められています。
ブランドカラーをそのまま適用すると、ライトモードでは問題なくてもダークモードで視認性が落ちる、といったケースが発生しやすいのです。
ブランドカラーの適用箇所が分散
ボタン、リンク、バッジ、アラートなど、UI コンポーネントごとに異なるカラートークンが使われています。
ブランドカラーを統一的に適用するには、どのトークンをどのコンポーネントが参照しているのかを把握する必要があり、これが意外と手間になります。
以下の図は、shadcn/ui における主なカラートークンとその用途を整理したものです。
mermaidflowchart TD
tokens["カラートークン一覧"]
tokens --> bg["背景系<br/>(background, card)"]
tokens --> text["テキスト系<br/>(foreground, muted-foreground)"]
tokens --> action["アクション系<br/>(primary, secondary, accent)"]
tokens --> status["ステータス系<br/>(destructive, success, warning)"]
tokens --> border["ボーダー系<br/>(border, input, ring)"]
bg --> bgUse["ページ背景、カード背景"]
text --> textUse["本文、キャプション"]
action --> actionUse["ボタン、リンク、バッジ"]
status --> statusUse["エラー、成功、警告表示"]
border --> borderUse["枠線、入力フィールド、<br/>フォーカスリング"]
この図からわかるように、カラートークンは 用途別 に分類されており、それぞれが異なる UI 要素に適用されます。
ブランドカラーを適用する際は、主に アクション系(primary
、accent
)と ボーダー系(ring
)が対象になるでしょう。
解決策
カラートークン早見表の作成
shadcn/ui のカラートークンを一覧表にまとめることで、役割と用途を一目で把握できるようにします。
以下の表は、shadcn/ui で標準的に使われる主要なカラートークンとその役割を整理したものです。
# | トークン名 | 役割 | 主な用途 | 対となるトークン |
---|---|---|---|---|
1 | --background | ページ全体の背景色 | body 、ページ背景 | --foreground |
2 | --foreground | ページ全体のテキスト色 | 本文、見出し | --background |
3 | --card | カードコンポーネントの背景色 | カード、パネル | --card-foreground |
4 | --card-foreground | カード内のテキスト色 | カード内の文字 | --card |
5 | --popover | ポップオーバーの背景色 | ドロップダウン、ツールチップ | --popover-foreground |
6 | --popover-foreground | ポップオーバー内のテキスト色 | ポップオーバー内の文字 | --popover |
7 | --primary | 主要なアクションを示すブランドカラー | ボタン、リンク、CTA | --primary-foreground |
8 | --primary-foreground | プライマリ色の上に重なるテキスト色 | ボタン内の文字 | --primary |
9 | --secondary | 副次的なアクションを示す色 | セカンダリボタン、バッジ | --secondary-foreground |
10 | --secondary-foreground | セカンダリ色の上に重なるテキスト色 | セカンダリボタン内の文字 | --secondary |
11 | --muted | 控えめな背景色(無効状態、補助的な要素) | 無効ボタン、キャプション背景 | --muted-foreground |
12 | --muted-foreground | 控えめなテキスト色 | キャプション、ヘルプテキスト | --muted |
13 | --accent | 強調したい要素に使う色 | ホバー時の背景、アクティブ状態 | --accent-foreground |
14 | --accent-foreground | アクセント色の上に重なるテキスト色 | アクセント背景上の文字 | --accent |
15 | --destructive | 削除や危険なアクションを示す色 | 削除ボタン、エラーメッセージ背景 | --destructive-foreground |
16 | --destructive-foreground | デストラクティブ色の上に重なるテキスト色 | 削除ボタン内の文字 | --destructive |
17 | --border | 枠線の色 | カードの枠、入力フィールドの枠 | - |
18 | --input | 入力フィールドの枠線色 | テキストボックス、セレクトボックス | - |
19 | --ring | フォーカス時のリング色 | フォーカスインジケーター | - |
20 | --radius | ボーダーの丸み(単位: rem) | ボタン、カードの角丸 | - |
この表を参考にすることで、どのトークンがどの UI 要素に影響するのかがすぐにわかります。
ブランドカラーの最適な割り当て方
ブランドカラーを shadcn/ui に適用する際は、主に以下のトークンに割り当てるのが効果的です。
1. --primary
と --primary-foreground
ブランドの主要色を --primary
に設定し、その上に重なるテキスト色を --primary-foreground
に設定します。
ボタンやリンク、CTA(Call to Action)など、ユーザーに行動を促す要素に使われるため、最も目立つ色を割り当てましょう。
css/* ライトモード: ブランドカラーを primary に設定 */
:root {
--primary: 210 100% 50%; /* 鮮やかな青 */
--primary-foreground: 0 0% 100%; /* 白 */
}
css/* ダークモード: 明度を調整してコントラストを確保 */
.dark {
--primary: 210 100% 60%; /* より明るい青 */
--primary-foreground: 222.2 84% 4.9%; /* 濃紺 */
}
2. --accent
と --accent-foreground
ホバー時やアクティブ状態など、強調したい要素に使う色です。
--primary
よりも明るめ、または彩度を高めにすることで、視覚的な変化をつけやすくなります。
css:root {
--accent: 210 100% 95%; /* 淡い青 */
--accent-foreground: 210 100% 50%; /* 鮮やかな青 */
}
3. --ring
フォーカス時のリング色にブランドカラーを適用することで、キーボード操作時のアクセシビリティを向上させつつ、ブランドの統一感を出せます。
css:root {
--ring: 210 100% 50%; /* ブランドカラーと同じ */
}
以下の図は、ブランドカラーを --primary
、--accent
、--ring
に適用した際の UI への反映イメージを示しています。
mermaidflowchart LR
brand["ブランドカラー<br/>(例: #0088FF)"] --> primary["--primary<br/>ボタン、リンク"]
brand --> accent["--accent<br/>ホバー、アクティブ"]
brand --> ring["--ring<br/>フォーカスリング"]
primary --> button["Button"]
accent --> hover["Button:hover"]
ring --> focus["Button:focus"]
このように、ブランドカラーを軸に関連するトークンを設定することで、UI 全体に統一感が生まれます。
コントラスト比の計算と調整
WCAG 2.1 の基準を満たすためには、テキストと背景のコントラスト比を確認する必要があります。
以下の表は、WCAG 2.1 で求められるコントラスト比の基準をまとめたものです。
# | テキストサイズ | 必要なコントラスト比 | 適用レベル |
---|---|---|---|
1 | 通常サイズ(18px 未満) | 4.5:1 以上 | AA |
2 | 大きなサイズ(18px 以上、太字 14px 以上) | 3:1 以上 | AA |
3 | 通常サイズ(18px 未満) | 7:1 以上 | AAA |
4 | 大きなサイズ(18px 以上、太字 14px 以上) | 4.5:1 以上 | AAA |
コントラスト比は、以下のような計算式で求められます(手計算は大変なので、ツールを使うのが一般的です)。
typescript// コントラスト比計算のイメージ(実装例)
function getContrastRatio(
color1: string,
color2: string
): number {
const l1 = getRelativeLuminance(color1);
const l2 = getRelativeLuminance(color2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
// 相対輝度を計算(簡略版)
function getRelativeLuminance(color: string): number {
// RGB から HSL を計算し、輝度を返す
// 実装は省略
return 0.5; // ダミー値
}
実際には、以下のようなオンラインツールを使ってコントラスト比を確認するのが効率的です。
もし、ブランドカラーがコントラスト基準を満たさない場合は、以下のような調整方法があります。
typescript// HSL の明度(L)を調整してコントラストを確保
// ライトモード: 明度を下げる(暗くする)
:root {
--primary: 210 100% 40%; /* 50% → 40% に下げる */
}
// ダークモード: 明度を上げる(明るくする)
.dark {
--primary: 210 100% 70%; /* 60% → 70% に上げる */
}
明度を調整することで、色相と彩度はそのままに、視認性を向上させられます。
テーマ切り替え時のコントラスト確保
ライトモードとダークモードで、同じトークン名でも異なる HSL 値を設定することで、どちらのモードでもコントラスト基準を満たせます。
以下は、--primary
と --primary-foreground
の設定例です。
css/* ライトモード */
:root {
--primary: 210 100% 40%; /* 暗めの青 */
--primary-foreground: 0 0% 100%; /* 白 */
/* コントラスト比: 約 8.5:1(AAA 基準クリア) */
}
/* ダークモード */
.dark {
--primary: 210 100% 70%; /* 明るめの青 */
--primary-foreground: 222.2 84% 4.9%; /* 濃紺 */
/* コントラスト比: 約 9.2:1(AAA 基準クリア) */
}
このように、明度を調整することで、両モードでアクセシビリティ基準を満たしつつ、ブランドカラーの統一感を保てます。
具体例
ブランドカラー「#0088FF」を shadcn/ui に適用
ここでは、ブランドカラー「#0088FF」(鮮やかな青)を shadcn/ui に適用する手順を示します。
まず、この色を HSL 形式に変換しましょう。
typescript// #0088FF を HSL に変換
// H: 210, S: 100%, L: 50%
次に、globals.css
に以下の設定を追加します。
css@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
/* ブランドカラーを primary に設定 */
--primary: 210 100% 50%;
--primary-foreground: 0 0% 100%;
/* アクセント色は primary よりも明るく */
--accent: 210 100% 95%;
--accent-foreground: 210 100% 50%;
/* フォーカスリングにもブランドカラーを適用 */
--ring: 210 100% 50%;
/* その他のトークンは shadcn/ui のデフォルトを使用 */
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--radius: 0.5rem;
}
}
ダークモード用には、明度を調整してコントラストを確保します。
css@layer base {
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ダークモードでは明度を上げる */
--primary: 210 100% 70%;
--primary-foreground: 222.2 84% 4.9%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 100% 70%;
--ring: 210 100% 70%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
}
}
これで、bg-primary
、text-primary
、border-primary
などの Tailwind クラスを使うだけで、ブランドカラーが適用されます。
typescript// Button コンポーネントの例
import { Button } from '@/components/ui/button';
export default function Home() {
return (
<div className='p-8'>
<Button className='bg-primary text-primary-foreground'>
ブランドカラーのボタン
</Button>
</div>
);
}
以下の図は、ブランドカラーを適用した際の UI コンポーネントへの反映イメージを示しています。
mermaidflowchart TD
css["globals.css で<br/>--primary を定義"] --> tailwind["Tailwind CSS で<br/>bg-primary に変換"]
tailwind --> button["Button コンポーネント"]
tailwind --> link["Link コンポーネント"]
tailwind --> badge["Badge コンポーネント"]
button --> lightBtn["ライトモード:<br/>背景 #0088FF、文字 #FFFFFF"]
button --> darkBtn["ダークモード:<br/>背景 #66B3FF、文字 #0A1929"]
このように、CSS 変数を一箇所で定義するだけで、すべてのコンポーネントに一貫したブランドカラーが適用されます。
コントラスト比の検証
設定したカラートークンがアクセシビリティ基準を満たしているか、実際に検証してみましょう。
以下は、WebAIM Contrast Checker を使った検証結果の例です。
# | 組み合わせ | コントラスト比 | 基準 | 判定 |
---|---|---|---|---|
1 | --primary (#0073D9) / --primary-foreground (#FFFFFF) | 4.52:1 | AA | 合格 |
2 | --primary (ダーク #66B3FF) / --primary-foreground (ダーク #0A1929) | 9.15:1 | AAA | 合格 |
3 | --accent (#E6F4FF) / --accent-foreground (#0088FF) | 8.22:1 | AAA | 合格 |
4 | --destructive (#E53E3E) / --destructive-foreground (#FFFFFF) | 4.51:1 | AA | 合格 |
このように、すべての組み合わせで WCAG 2.1 の AA 基準(4.5:1 以上)をクリアしていることが確認できました。
カスタムカラーの追加
shadcn/ui の標準トークンに加えて、独自のカラートークンを追加することもできます。
たとえば、成功メッセージ用の --success
トークンを追加する例を示します。
css@layer base {
:root {
--success: 142 76% 36%; /* 緑 */
--success-foreground: 0 0% 100%; /* 白 */
}
.dark {
--success: 142 70% 45%; /* 明るい緑 */
--success-foreground: 222.2 84% 4.9%; /* 濃紺 */
}
}
Tailwind CSS の設定ファイル(tailwind.config.ts
)にも追加します。
typescriptimport type { Config } from 'tailwindcss';
const config: Config = {
darkMode: ['class'],
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
// 既存のトークンに加えて success を追加
success: {
DEFAULT: 'hsl(var(--success))',
foreground: 'hsl(var(--success-foreground))',
},
},
},
},
plugins: [require('tailwindcss-animate')],
};
export default config;
これで、bg-success
、text-success
、border-success
といったクラスが使えるようになります。
typescript// 成功メッセージの例
export function SuccessAlert() {
return (
<div className='bg-success text-success-foreground p-4 rounded-md'>
保存に成功しました!
</div>
);
}
まとめ
shadcn/ui のカラートークンを体系的に理解することで、ブランドカラーの適用とアクセシビリティの確保が格段に楽になります。
今回紹介したカラートークン早見表を参考に、以下のポイントを押さえておきましょう。
- セマンティックなトークン名を使うことで、ライト/ダークモードの切り替えが自動化される
- ブランドカラーは
--primary
、--accent
、--ring
に割り当てるのが効果的 - HSL 形式の明度(L)を調整することで、色相を保ったままコントラスト比を確保できる
- WCAG 2.1 の AA 基準(4.5:1 以上) を満たすよう、コントラスト比を検証する
- 独自のカラートークンを追加することで、プロジェクト固有のデザインシステムを構築できる
これらのポイントを実践することで、アクセシブルでブランド統一感のある UI を、効率的に実装できるようになるでしょう。
関連リンク
- article
shadcn/ui カラートークン早見表:ブランドカラー最適化&明暗コントラスト基準
- article
shadcn/ui を Monorepo(Turborepo/pnpm)に導入するベストプラクティス
- article
shadcn/ui と Headless UI/Vanilla Radix を徹底比較:実装量・a11y・可読性の差
- article
shadcn/ui の思想を徹底解剖:なぜ「コピーして使う」アプローチが拡張性に強いのか
- article
shadcn/ui でダッシュボードをデザインするベストプラクティス
- article
shadcn/ui のコンポーネント一覧と使い方まとめ
- article
Vitest `vi` API 技術チートシート:`mock` / `fn` / `spyOn` / `advanceTimersByTime` 一覧
- article
Pinia ストア分割テンプレ集:domain/ui/session の三層パターン
- article
Obsidian Markdown 拡張チートシート:Callout/埋め込み/内部リンク完全網羅
- article
Micro Frontends 設計:`vite-plugin-federation` で分割可能な UI を構築
- article
TypeScript 公開 API の型設計術:`export type`/`interface`/`class`の責務分担と境界設計
- article
Nuxt nuxi コマンド速見表:プロジェクト作成からモジュール公開まで
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来