T-CREATOR

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

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-backgroundtext-primary といった Tailwind クラスを使うだけで、自動的にテーマに応じた色が適用されます。

課題

カラートークンの種類と役割が不明瞭

shadcn/ui には primarysecondaryaccentmuteddestructive など、複数のカラートークンが用意されています。

しかし、それぞれの役割や使い分けが公式ドキュメントだけでは十分に理解しにくく、どのトークンにブランドカラーを割り当てるべきか迷ってしまうことがあります。

コントラスト比の確保が難しい

アクセシビリティ基準(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 要素に適用されます。

ブランドカラーを適用する際は、主に アクション系primaryaccent)と ボーダー系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-primarytext-primaryborder-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:1AA合格
2--primary (ダーク #66B3FF) / --primary-foreground (ダーク #0A1929)9.15:1AAA合格
3--accent (#E6F4FF) / --accent-foreground (#0088FF)8.22:1AAA合格
4--destructive (#E53E3E) / --destructive-foreground (#FFFFFF)4.51:1AA合格

このように、すべての組み合わせで 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-successtext-successborder-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 を、効率的に実装できるようになるでしょう。

関連リンク