言語を切り替える
テーマを切り替える

shadcn/ui のインストールとテーマカスタマイズ完全ガイド(CSS 変数つき)

shadcn/ui を初めて使ったとき、「npm パッケージではない」という設定に面食らいました。コードをプロジェクトにコピーする?さすがに原始的すぎるのでは、と思ったのです。

ところが何度か使ううちに、そここそが shadcn/ui の強みだと気づきました。すべてのコンポーネントのソースを自分が持っているので、好きなように書き換えられます。バージョン競合に悩む必要もなく、コンポーネントライブラリのデザインに縛られることもありません。

今日は、shadcn/ui のインストール設定とテーマカスタマイズについて話していきます。とくに CSS 変数を使って、ブランド化されたデザインシステムをどう実現するかが中心です。読み終わるころには、5 分で基本設定を済ませ、あとは 1 時間ほどでテーマを自分好みに調整できるようになっているはずです。


一、クイックインストール:2 つの方法

方法その 1:CLI でワンコマンド初期化(おすすめ)

新規プロジェクトなら、このコマンドを使えば一発です。

npx shadcn@latest init

実行すると、いくつか質問されます。TypeScript と JavaScript のどちらを使うか?どのスタイルを選ぶか?デフォルトテーマは何にするか?すべて対話形式なので、案内に沿って選ぶだけで大丈夫です。

インストールが終わると、プロジェクトにいくつかファイルが追加されます。

  • components.json - 設定ファイル
  • lib/utils.ts - ユーティリティ関数
  • components/ui/ - コンポーネントの保存ディレクトリ

コンポーネントを追加するのも簡単です。たとえばボタンが欲しいなら、こうします。

npx shadcn@latest add button

コンポーネントのコードが components/ui/button.tsx に自動でコピーされるので、そのまま import して使えます。

ここに落とし穴があります。プロジェクトがすでにある程度進んでいると、tailwind.config.js や globals.css に色々と書き込まれているかもしれません。shadcn の init コマンドはこれらのファイルを上書きするので、プロジェクトの立ち上げ時に入れておくのが一番です。

あるブロガーがうまいことを言っていました。shadcn/ui はプロジェクトの「最初の依存関係の 1 つ」として入れておけ、あとから追加するな、と。痛い教訓ですね。

方法その 2:手動インストール(既存プロジェクト向け)

プロジェクトがすでに形になっていると、CLI で設定を上書きするリスクが大きすぎます。その場合は手動でインストールしましょう。

いくつかのステップに分けて進めます。

第一歩:Tailwind CSS が入っているか確認

shadcn のコンポーネントはすべて Tailwind で書かれているので、未導入なら先に Tailwind を入れます。これは詳しく説明しません。公式チュートリアルがわかりやすいです。

第二歩:依存関係をインストール

npm install class-variance-authority clsx tailwind-merge
npm install lucide-react

class-variance-authority(略して CVA)は便利なツールで、あとでコンポーネントのバリアントを作るときに使います。

第三歩:パスエイリアスを設定

tsconfig.json に次を追加します。

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}

こうすれば @/components/ui/button のようにコンポーネントを読み込めるようになり、../../../ を延々と書かずに済みます。

第四歩:components.json を作成

プロジェクトのルートディレクトリに、このファイルを新規作成します。

{
  "style": "new-york",
  "rsc": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

cssVariables: true の行が重要です。Tailwind の utility class ではなく、CSS 変数でテーマを作るという指定です。

第五歩:スタイルを追加

globals.css に shadcn の基本スタイルを追加します。これはあとのテーマの章で詳しく説明します。


二、テーマシステムを理解する:CSS 変数の使いこなし方

shadcn/ui のテーマシステムは、シンプルな約束ごとに基づいています。どの色にも backgroundforeground の 2 つの変数がある、というものです。

どういう意味でしょうか。例を挙げてみます。

:root {
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
}

--primary はボタンの背景色、--primary-foreground はボタン上の文字色です。このようにペアにしておく利点は、1 つの変数を変えるだけで、関連するすべてのコンポーネントが一緒に変わることです。

CSS 変数の一覧

shadcn/ui はデフォルトで次の変数を定義しています。

変数用途
--backgroundページの背景色
--foregroundページの文字色
--cardカードの背景
--card-foregroundカードの文字
--popoverポップオーバーの背景
--popover-foregroundポップオーバーの文字
--primaryメインカラー(ボタン、リンク)
--primary-foregroundメインカラー上の文字
--secondaryサブカラー
--secondary-foregroundサブカラー上の文字
--muted控えめな背景
--muted-foreground控えめな文字
--accentアクセントカラー
--accent-foregroundアクセントカラー上の文字
--destructive危険な操作(削除ボタン)
--destructive-foreground危険な操作上の文字
--borderボーダー
--input入力欄
--ringフォーカスリング

数は多く見えますが、background / foreground のパターンさえ理解すれば、覚えるのは簡単です。

HSL 形式の秘密

気づいたかもしれませんが、shadcn の色の値は標準的な HSL 形式ではありません。

/* ❌ 標準の HSL */
--primary: hsl(222.2, 47.4%, 11.2%);

/* ✅ shadcn の形式 */
--primary: 222.2 47.4% 11.2%;

なぜこの「裸」の形式で書くのでしょうか。

Tailwind が透明度修飾子に対応しているからです。たとえば bg-primary/50 は 50% の透明度のメインカラーを表します。変数が完全な hsl() 形式だと、この機能は使えません。

裸の形式にしておくと、Tailwind が自動的に hsl() と透明度を付けてくれます。よく考えられた設計です。


三、自分のブランドテーマをカスタマイズする

方法その 1:CSS 変数を直接書き換える

一番シンプルな方法です。globals.css を開いて :root の部分を探し、色の値を書き換えるだけです。

たとえばメインカラーをデフォルトの青から紫に変えたいなら、こうします。

:root {
  --primary: 270 60% 60%;
  --primary-foreground: 0 0% 100%;
}

.dark {
  --primary: 270 60% 70%;
  --primary-foreground: 0 0% 0%;
}

保存すれば、bg-primary を使っているボタンやリンクはすべて紫に変わります。

方法その 2:OKLCH カラースペースを使う(Tailwind v4)

Tailwind v4 を使っているなら、OKLCH カラースペースを検討してもよいでしょう。HSL に比べて、OKLCH は色の感じ方が人の目に近く、生成される色階調がより均一になります。

:root {
  --primary: oklch(0.6 0.2 270);
  --primary-foreground: oklch(0.98 0 0);
}

ここで oklch(0.6 0.2 270) の 3 つのパラメータは次の意味です。

  • 0.6 - 明度(0〜1)
  • 0.2 - 彩度(0〜0.4 程度)
  • 270 - 色相の角度(0〜360)

方法その 3:オンラインツールで生成

自分で色を組み合わせるのが面倒なら、オンラインツールが使えます。

おすすめはこちらです。Shadcn Theme Generator

メインカラーを 1 つ選べば、ツールがライトとダークの両方を含む完全な CSS 変数一式を自動生成してくれます。あとは globals.css にコピー&ペーストするだけです。


四、ダークモードの設定

next-themes でテーマ切り替えを実装

shadcn/ui 自体にはテーマ切り替え機能がありませんが、next-themes というライブラリで実現できます。

まずインストールします。

npm install next-themes

次に layout.tsx で設定します。

import { ThemeProvider } from "next-themes"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ja" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

いくつか重要なポイントがあります。

  • suppressHydrationWarning は必須です。付けないとハイドレーション警告が出ます
  • attribute="class" は class 名でテーマを切り替えるという意味です
  • defaultTheme="system" はデフォルトでシステムに追随するという意味です
  • enableSystem でシステムテーマの検出を有効にします

テーマ切り替えボタンを作る

useTheme フックで現在のテーマと切り替え関数を取得します。

import { useTheme } from "next-themes"
import { Moon, Sun } from "lucide-react"

export function ThemeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <button
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      className="p-2 rounded-md hover:bg-accent"
    >
      {theme === "dark" ? <Sun size={20} /> : <Moon size={20} />}
    </button>
  )
}

デフォルトをダークモードにする

サイトをデフォルトでダークモードにしたいなら、2 つの方法があります。

方法その 1:dark class をハードコードする

<html lang="ja" className="dark">

こう書くと、テーマはダークに固定され、切り替えられなくなります。

方法その 2:デフォルトテーマを設定する

<ThemeProvider
  attribute="class"
  defaultTheme="dark"  // デフォルトはダーク
  enableSystem={false} // システム検出をオフ
>

これなら、ユーザーは手動で切り替えられますが、初期状態はダークになります。


五、上級カスタマイズ:コンポーネントのバリアント

CVA でカスタムバリアントを作る

ボタンにいくつかのスタイル、たとえば「危険」「成功」「グラデーション」を追加したいときがあります。CVA を使えば、こうしたバリアントを手軽に定義できます。

import { cva, type VariantProps } from "class-variance-authority"

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

そしてコンポーネントの中で使います。

<button className={buttonVariants({ variant: "destructive", size: "lg" })}>
  削除
</button>

shadcn コンポーネントのソースを直接書き換えない

これはベストプラクティスの問題です。

shadcn のコンポーネントコードは自分のプロジェクトの中にあるので、好きなように書き換えられます。とはいえ、元のファイルを直接いじるのではなく、ラッパーコンポーネントを作ることをおすすめします。

なぜでしょうか。shadcn はコンポーネントを頻繁に更新するため、元のファイルを書き換えていると、更新のときに手動でマージする必要があり、とても面倒だからです。

より良いやり方はこちらです。

// components/brand-button.tsx
import { Button } from "@/components/ui/button"
import { cva } from "class-variance-authority"

const brandButtonVariants = cva("...", {
  variants: {
    brand: {
      primary: "bg-brand-primary text-white",
      secondary: "bg-brand-secondary text-black",
    },
  },
})

export function BrandButton({ brand, ...props }) {
  return <Button className={brandButtonVariants({ brand })} {...props} />
}

こうすれば元の Button コンポーネントはそのまま保たれ、自分専用の BrandButton を作れます。今後 shadcn が更新されても、自分のカスタマイズには影響しません。


六、よくある問題とハマりどころ

問題その 1:インストール後にスタイルが効かない

次の点を確認しましょう。

  1. globals.csslayout.tsx で import されているか?
  2. Tailwind の content 設定に components/**/* が含まれているか?
  3. components.json のパス設定は正しいか?

問題その 2:テーマ切り替え時にちらつく

これはたいていハイドレーションの不一致が原因です。次を確認しましょう。

  1. <html> タグに suppressHydrationWarning を付けたか
  2. ThemeProvider がアプリ全体を包んでいるか
  3. サーバーサイドレンダリング時に theme を読み取っていないか(undefined になります)

問題その 3:CSS 変数が効かない

考えられる原因はこちらです。

  1. 変数名が間違っている(--primaryForeground ではなく --primary-foreground に注意)
  2. 対応する .dark のスタイルがない
  3. 変数の値の形式が間違っている(裸の HSL か OKLCH を使う)

問題その 4:コンポーネントのスタイルが競合する

プロジェクトにすでにスタイルシステムがあると、shadcn のものと競合する場合があります。解決策はこちらです。

  1. shadcn コンポーネントに namespace を付ける(例:shadcn-button
  2. Tailwind の layer の優先度を調整する
  3. CVA で自分のバリアントを作り、デフォルトスタイルに依存しない

七、まとめ

shadcn/ui のインストール設定は、実はかなりシンプルです。鍵になるのは「依存パッケージではなくコードをコピーする」という設計思想を理解することです。これによって完全にコントロールできる一方、プロジェクトごとにコンポーネントのコードを自分で管理する必要があります。

テーマカスタマイズの面では、CSS 変数システムがとてもエレガントに設計されています。いくつかの変数の値を変えるだけで、アプリ全体の配色が一緒に変わります。next-themes と組み合わせれば、ライトとダークの切り替えも数行のコードで済みます。

最後にいくつかのアドバイスです。

  1. 新規プロジェクトはまず CLI で初期化して、手動設定の手間を省く
  2. 意味のある色変数を使う。具体的な色名ではなく primary や secondary を使う
  3. ライトとダークの両方でコントラストをテストして、読みやすさを確保する
  4. ソースを書き換えずラッパーコンポーネントを作る。あとから更新しやすい

次にテーマ付きの UI を素早く組み立てたくなったら、shadcn/ui を試してみてください。コピー&ペーストの快適さは、使った人にしかわかりません。



参考資料

shadcn/ui のインストールとテーマカスタマイズ

ゼロから shadcn/ui をインストールし、テーマシステムを設定して、ブランド化デザインを実現する

⏱️ 目安時間: 30 分

  1. 1

    ステップ1: CLI でのクイック初期化

    新しいプロジェクトでインストールコマンドを実行します。

    • npx shadcn@latest init
    • TypeScript / New York スタイル / デフォルトテーマを選択
    • CLI の設定完了を待つ
  2. 2

    ステップ2: ブランドのメインカラーを変更

    globals.css の CSS 変数を編集します。

    • app/globals.css を開く
    • :root にある --primary 変数を探す
    • 自分のブランドカラーに書き換える(HSL または OKLCH 形式)
    • コントラストを確保するため --primary-foreground も同時に変更
  3. 3

    ステップ3: ダークモードを設定

    next-themes をインストールして設定します。

    • npm install next-themes
    • layout.tsx に ThemeProvider を追加
    • ハイドレーション警告を防ぐため suppressHydrationWarning を設定
    • テーマ切り替えコンポーネントを作成
  4. 4

    ステップ4: コンポーネントのバリアントを作成

    CVA でカスタムスタイルを定義します。

    • class-variance-authority をインストール
    • variants と defaultVariants を定義
    • コンポーネントで buttonVariants() を適用
    • 元の shadcn コンポーネントはそのまま保つ

FAQ

shadcn/ui と従来の UI コンポーネントライブラリは何が違うの?
shadcn/ui は npm パッケージではなく、コンポーネントのソースコードをプロジェクトにコピーする方式です。完全にコントロールでき、カスタマイズも自由で、バージョン競合の心配もありません。一方で、プロジェクトごとにコンポーネントのコードを自分で管理する必要があります。
なぜ新規プロジェクトの初期化時に shadcn/ui を入れることを勧めるの?
shadcn の init コマンドが tailwind.config.js と globals.css を上書きするからです。すでにしばらく開発を進めたプロジェクトだと、これらのファイルの設定が上書きされてしまうため、できるだけ早く入れたほうがよいのです。
CSS 変数はなぜ標準の HSL ではなく「裸」の形式で書くの?
裸の形式(例:222.2 47.4% 11.2%)なら Tailwind の透明度修飾子が使えるからです。たとえば bg-primary/50 は 50% の透明度を表します。完全な hsl() 形式だと、この機能は使えません。
ブランドのメインカラーはどう変更すればいい?
globals.css を開き、:root にある --primary と --primary-foreground 変数を探して、自分のブランドカラーの値に書き換えます。変更すれば、bg-primary を使っているコンポーネントはすべて自動で更新されます。
ダークモードでちらつきが起きるのはなぜ?
たいていはハイドレーションの不一致が原因です。html タグに suppressHydrationWarning 属性を付け、ThemeProvider がアプリ全体を正しく包んでいることを確認しましょう。サーバーサイドレンダリング時に theme を読み取らないことも大切です。
shadcn コンポーネントのソースを直接書き換えてもいい?
おすすめしません。shadcn はコンポーネントを頻繁に更新するため、元のファイルを書き換えていると、更新のたびに手動でマージする必要があり面倒です。元のコンポーネントはそのまま残し、ラッパーコンポーネントを作るほうがよいでしょう。

4分で読めます · 公開日: 2026年3月26日 · 更新日: 2026年6月8日

関連記事

コメント

GitHubアカウントでログインしてコメントできます