web-styling-cva
Class Variance Authorityというツールを使い、コンポーネントのデザインパターンを型安全に定義し、様々なバリエーションを効率的に管理・適用することで、柔軟で一貫性のあるUIを構築するSkill。
📜 元の英語説明(参考)
Class Variance Authority - type-safe component variant styling with cva(), compound variants, and VariantProps
🇯🇵 日本人クリエイター向け解説
Class Variance Authorityというツールを使い、コンポーネントのデザインパターンを型安全に定義し、様々なバリエーションを効率的に管理・適用することで、柔軟で一貫性のあるUIを構築するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o web-styling-cva.zip https://jpskill.com/download/10308.zip && unzip -o web-styling-cva.zip && rm web-styling-cva.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10308.zip -OutFile "$d\web-styling-cva.zip"; Expand-Archive "$d\web-styling-cva.zip" -DestinationPath $d -Force; ri "$d\web-styling-cva.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
web-styling-cva.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
web-styling-cvaフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
CVA (Class Variance Authority) パターン
クイックガイド: CVA を使用して、宣言的な API で型安全なコンポーネントのバリアントを定義します。基本クラス、バリアントグループ (size, intent, state)、複合条件のための複合バリアント、およびデフォルト値を定義します。
VariantPropsで型を抽出します。あらゆる CSS アプローチ (ユーティリティクラス、CSS modules、プレーン CSS) で動作します。常にdefaultVariantsを設定し、常にブール値のバリアントに対してtrue/falseの両方を定義し、常に型抽出にVariantPropsを使用します。
<critical_requirements>
重要: この Skill を使用する前に
すべてのコードは、CLAUDE.md のプロジェクト規約に従う必要があります (kebab-case, named exports, import ordering,
import type, named constants)
(variants オブジェクトにすべてのバリアントオプションを定義する必要があります - cva の外部で条件付きクラスロジックを絶対に使用しないでください)
(VariantProps を使用して型を抽出する必要があります - バリアントプロップの型を手動で定義しないでください)
(初期状態には defaultVariants を使用する必要があります - デフォルトとして未定義のプロップに依存しないでください)
(複数条件のスタイルには compoundVariants を使用する必要があります - 結合された状態のために三項演算子をネストしないでください)
</critical_requirements>
自動検出: cva, class-variance-authority, VariantProps, variants, compoundVariants, defaultVariants, component variants, type-safe styling, cx
使用する場合:
- 複数の視覚的なバリアント (size, intent, state) を持つコンポーネントを構築する場合
- 型安全なプロップを持つデザインシステムコンポーネントを作成する場合
- 複合条件 (例: "large primary" には特別なスタイルがある) を実装する場合
- プロジェクトまたはフレームワーク間でバリアントスタイルを共有する場合
使用しない場合:
- バリアントのないシンプルなコンポーネント (プレーンなクラスを使用するだけ)
- パターンの再利用がない、一度限りのスタイリング
- ランタイム値に基づく動的なスタイル (インラインスタイルまたは CSS 変数を使用する)
- ビューポートに基づいて変化するレスポンシブスタイル (CSS メディアクエリを使用する)
カバーされる主要なパターン:
cva()を使用した基本的なバリアント定義- トグル状態のためのブール値バリアント
- 複合条件のための複合バリアント
VariantPropsを使用した型抽出cx()および外部ユーティリティを使用したクラスのマージ- マルチパートコンポーネントパターン
- バリアント定義の構成と拡張
詳細なリソース:
- examples/core.md - 基本的なバリアント、ブール値の状態、デフォルト値、必須バリアント
- examples/compound-variants.md - 複数条件のスタイル、配列構文、状態の組み合わせ
- examples/composition.md - VariantProps, クラスのマージ、マルチパートコンポーネント、バリアントの拡張
- reference.md - 意思決定フレームワーク、アンチパターン、クイックリファレンス
<philosophy>
哲学
CVA は、コンポーネントのバリアントを UI 状態の型システム として扱います。コンポーネント全体に条件付きクラスロジックを分散させる代わりに、CVA はバリアント定義を単一の型付き構成オブジェクトに集中させます。
コア原則:
- 命令型よりも宣言型: 各バリアントがどのように見えるかを定義し、クラスを計算する方法を定義しない
- 型安全なバリアント: TypeScript はコンパイル時に無効なバリアント値をキャッチする
- フレームワークに依存しない: あらゆる UI フレームワークおよびあらゆる CSS アプローチで動作する
- 構成に優しい: バリアントは組み合わせ、拡張、および構成できる
- 信頼できる唯一の情報源: バリアント定義は 1 か所に存在し、型は派生する
手動の条件付きクラスよりも CVA を使用する理由:
// 悪い例: ロジックが分散し、型安全性がなく、保守が困難
function getButtonClasses(size: string, variant: string, disabled: boolean) {
let classes = "btn";
if (size === "sm") classes += " btn-sm";
else if (size === "lg") classes += " btn-lg";
if (variant === "primary") classes += " btn-primary";
if (disabled) classes += " btn-disabled";
return classes;
}
// 良い例: 宣言的、型安全、構成可能
const buttonVariants = cva("btn", {
variants: {
size: { sm: "btn-sm", lg: "btn-lg" },
variant: { primary: "btn-primary" },
disabled: { true: "btn-disabled" },
},
});
</philosophy>
<patterns>
コアパターン
パターン 1: 基本的なバリアント定義
基本クラスとバリアントオプションを使用してコンポーネントスタイルを定義します。可読性のために配列構文を使用し、常に defaultVariants を設定します。
import { cva } from "class-variance-authority";
const buttonVariants = cva(
["font-semibold", "border", "rounded"], // 基本クラスを配列として
{
variants: {
intent: {
primary: ["bg-blue-600", "text-white"],
secondary: ["bg-white", "text-gray-800"],
},
size: {
sm: ["text-sm", "py-1", "px-2"],
md: ["text-base", "py-2", "px-4"],
},
},
defaultVariants: {
intent: "primary",
size: "md",
},
},
);
buttonVariants(); // デフォルト: primary + md
buttonVariants({ intent: "secondary" }); // secondary + md
重要なルール: 常に defaultVariants を指定します (そうしないと、プロップなしで呼び出すと不完全なクラスが返されます)。可読性のために、スペースで区切られた文字列ではなく配列を使用します。
完全なボタン、バッジ、およびアイコンボタンの例については、examples/core.md を参照してください。
パターン 2: ブール値バリアント
バイナリ状態には true / false キーを使用します。常に両側を定義します。
const inputVariants = cva(["border", "rounded", "px-3", "py-2"], {
variants: {
disabled: {
false: ["bg-white", "cursor-text"], // 通常の状態
true: ["bg-gray-100", "cursor-not-allowed"], // 無効の状態
},
error: {
false: ["border-gray-300"],
true: ["border-red-500"],
},
},
defaultVariants: { disabled: false, error: false },
});
重要なルール: false のケースが欠落していると、通常の状態ではスタイルが適用されません。バリアントロジックが不完全になります。
loading, disabled, および error 状態でのブール値バリアントパターンについては、examples/core.md を参照してください。
パターン 3: 複合バリアント
特定のバリアントの組み合わせに特別なスタイルが必要な場合は、compoundVariants を使用します。配列構文は複数の値と一致します。
(原文がここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
CVA (Class Variance Authority) Patterns
Quick Guide: Use CVA to define type-safe component variants with a declarative API. Define base classes, variant groups (size, intent, state), compound variants for combined conditions, and default values. Extract types with
VariantProps. Works with any CSS approach (utility classes, CSS modules, plain CSS). Always setdefaultVariants, always define bothtrue/falsefor boolean variants, always useVariantPropsfor type extraction.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST define all variant options in the variants object - NEVER use conditional class logic outside cva)
(You MUST use VariantProps to extract types - NEVER manually define variant prop types)
(You MUST use defaultVariants for initial state - NEVER rely on undefined props for defaults)
(You MUST use compoundVariants for multi-condition styles - NEVER nest ternaries for combined states)
</critical_requirements>
Auto-detection: cva, class-variance-authority, VariantProps, variants, compoundVariants, defaultVariants, component variants, type-safe styling, cx
When to use:
- Building components with multiple visual variants (size, intent, state)
- Creating design system components with type-safe props
- Implementing compound conditions (e.g., "large primary" has special styles)
- Sharing variant styling across projects or frameworks
When NOT to use:
- Simple components with no variants (just use plain classes)
- One-off styling without pattern reuse
- Dynamic styles based on runtime values (use inline styles or CSS variables)
- Responsive styles that change based on viewport (use CSS media queries)
Key patterns covered:
- Basic variant definitions with
cva() - Boolean variants for toggle states
- Compound variants for combined conditions
- Type extraction with
VariantProps - Class merging with
cx()and external utilities - Multi-part component patterns
- Composing and extending variant definitions
Detailed Resources:
- examples/core.md - Basic variants, boolean states, default values, required variants
- examples/compound-variants.md - Multi-condition styles, array syntax, state combinations
- examples/composition.md - VariantProps, class merging, multi-part components, extending variants
- reference.md - Decision frameworks, anti-patterns, quick reference
<philosophy>
Philosophy
CVA treats component variants as a type system for UI states. Instead of scattering conditional class logic throughout components, CVA centralizes variant definitions in a single, typed configuration object.
Core Principles:
- Declarative over imperative: Define what each variant looks like, not how to compute classes
- Type-safe variants: TypeScript catches invalid variant values at compile time
- Framework-agnostic: Works with any UI framework and any CSS approach
- Composition-friendly: Variants can be combined, extended, and composed
- Single source of truth: Variant definitions live in one place, types are derived
Why CVA over manual conditional classes:
// BAD: scattered logic, no type safety, hard to maintain
function getButtonClasses(size: string, variant: string, disabled: boolean) {
let classes = "btn";
if (size === "sm") classes += " btn-sm";
else if (size === "lg") classes += " btn-lg";
if (variant === "primary") classes += " btn-primary";
if (disabled) classes += " btn-disabled";
return classes;
}
// GOOD: declarative, type-safe, composable
const buttonVariants = cva("btn", {
variants: {
size: { sm: "btn-sm", lg: "btn-lg" },
variant: { primary: "btn-primary" },
disabled: { true: "btn-disabled" },
},
});
</philosophy>
<patterns>
Core Patterns
Pattern 1: Basic Variant Definition
Define component styles with base classes and variant options. Use array syntax for readability, always set defaultVariants.
import { cva } from "class-variance-authority";
const buttonVariants = cva(
["font-semibold", "border", "rounded"], // base classes as array
{
variants: {
intent: {
primary: ["bg-blue-600", "text-white"],
secondary: ["bg-white", "text-gray-800"],
},
size: {
sm: ["text-sm", "py-1", "px-2"],
md: ["text-base", "py-2", "px-4"],
},
},
defaultVariants: {
intent: "primary",
size: "md",
},
},
);
buttonVariants(); // defaults: primary + md
buttonVariants({ intent: "secondary" }); // secondary + md
Key rules: always provide defaultVariants (calling without props returns incomplete classes otherwise), use arrays over space-separated strings for readability.
See examples/core.md for complete button, badge, and icon button examples.
Pattern 2: Boolean Variants
Use true/false keys for binary states. Always define both sides.
const inputVariants = cva(["border", "rounded", "px-3", "py-2"], {
variants: {
disabled: {
false: ["bg-white", "cursor-text"], // normal state
true: ["bg-gray-100", "cursor-not-allowed"], // disabled state
},
error: {
false: ["border-gray-300"],
true: ["border-red-500"],
},
},
defaultVariants: { disabled: false, error: false },
});
Key rule: missing false case means no styles applied in normal state -- variant logic becomes incomplete.
See examples/core.md for boolean variant patterns with loading, disabled, and error states.
Pattern 3: Compound Variants
Use compoundVariants when specific variant combinations need special styles. Array syntax matches multiple values.
const buttonVariants = cva(["font-semibold", "rounded"], {
variants: {
intent: {
primary: ["bg-blue-600", "text-white"],
secondary: ["bg-white", "text-gray-800"],
},
disabled: {
false: null,
true: ["opacity-50", "cursor-not-allowed"],
},
},
compoundVariants: [
// Hover only when enabled
{ intent: "primary", disabled: false, class: ["hover:bg-blue-700"] },
{ intent: "secondary", disabled: false, class: ["hover:bg-gray-100"] },
// Array syntax: matches multiple values
{
intent: ["primary", "secondary"],
disabled: true,
class: ["pointer-events-none"],
},
],
defaultVariants: { intent: "primary", disabled: false },
});
Key rules: compound variants express "when X AND Y, also apply Z", array syntax avoids duplicating rules across similar variants.
See examples/compound-variants.md for hover states, loading overrides, state matrices, and multi-part compounds.
Pattern 4: Type Extraction with VariantProps
Always use VariantProps to extract types from cva definitions -- never manually define variant types.
import { cva, type VariantProps } from "class-variance-authority";
const cardVariants = cva(["rounded-lg", "border"], {
variants: {
elevation: { flat: ["shadow-none"], raised: ["shadow-md"] },
padding: { none: ["p-0"], sm: ["p-2"], md: ["p-4"] },
},
defaultVariants: { elevation: "flat", padding: "md" },
});
// Extract types -- always in sync with cva definition
type CardVariants = VariantProps<typeof cardVariants>;
// { elevation?: "flat" | "raised" | null; padding?: "none" | "sm" | "md" | null }
interface CardProps extends CardVariants {
children: unknown;
className?: string;
}
Key rule: manual types drift when you add/remove variants. VariantProps is always in sync.
To make specific variants required (no default):
type BadgeProps = Omit<BadgeVariants, "color"> &
Required<Pick<BadgeVariants, "color">>;
See examples/composition.md for complete type extraction and required variant patterns.
Pattern 5: Class Merging with cx()
Use cx() (built-in, alias for clsx) for class concatenation. Use an external merge utility for class conflict resolution.
import { cva, cx } from "class-variance-authority";
// cx() concatenates and filters falsy values
cx(buttonVariants({ intent: "primary" }), highlighted && "ring-2", className);
// For conflict resolution (e.g., caller overriding variant padding),
// use a class-merging utility wrapper
function button(variants: ButtonVariants, className?: string): string {
return cn(buttonVariants(variants), className); // cn() resolves conflicts
}
See examples/composition.md for class merging patterns and conflict resolution.
Pattern 6: Multi-Part Components
Define separate cva for each styled part of a component (label, input, helper text). Share variant values for visual consistency.
const formFieldVariants = {
label: cva(["block", "font-medium"], {
variants: { size: { sm: ["text-sm"], md: ["text-base"] } },
defaultVariants: { size: "md" },
}),
input: cva(["w-full", "border", "rounded"], {
variants: {
size: { sm: ["text-sm", "px-2"], md: ["text-base", "px-3"] },
error: { false: ["border-gray-300"], true: ["border-red-500"] },
},
defaultVariants: { size: "md", error: false },
}),
helper: cva(["mt-1"], {
variants: {
size: { sm: ["text-xs"], md: ["text-sm"] },
error: { false: ["text-gray-500"], true: ["text-red-600"] },
},
defaultVariants: { size: "md", error: false },
}),
};
See examples/composition.md for multi-part and extending/composing variant patterns.
Pattern 7: Composing and Extending Variants
Combine multiple cva definitions with cx() for shared base + specialized variants.
const interactiveVariants = cva(["transition-colors", "focus:ring-2"], {
variants: { focusRing: { blue: ["focus:ring-blue-500"] } },
defaultVariants: { focusRing: "blue" },
});
const buttonVariants = cva(["font-semibold", "rounded"], {
variants: { intent: { primary: ["bg-blue-600"] } },
defaultVariants: { intent: "primary" },
});
// Compose with cx()
type ButtonProps = VariantProps<typeof interactiveVariants> &
VariantProps<typeof buttonVariants>;
function button(props: ButtonProps): string {
return cx(
interactiveVariants({ focusRing: props.focusRing }),
buttonVariants({ intent: props.intent }),
);
}
See examples/composition.md for composition and extension patterns.
</patterns>
<red_flags>
RED FLAGS
High Priority Issues:
- Manual variant type definitions -- Types drift from cva definition, defeating type safety. Always use
VariantProps<typeof variants>. - Conditional class logic outside cva -- Defeats purpose of centralized variant definitions. Add the condition as a variant.
- Missing
defaultVariants-- Calling without props returns incomplete classes. Always set defaults. - Nested ternaries for combined states -- Use
compoundVariantsinstead. - Only defining
truefor boolean variants --falsecase should provide base/normal styles.
Medium Priority Issues:
- Space-separated class strings instead of arrays -- arrays are more readable and maintainable
- Not using
cx()for class merging -- manual concatenation is error-prone - Duplicating variant styles across components -- compose from shared base variants
- Putting complex responsive logic in cva -- keep cva simple, handle responsiveness in CSS
Gotchas & Edge Cases:
VariantPropsmakes all variants optional (nullable) -- use TypeScript utilities (Required<Pick<>>) to make specific ones requiredcompoundVariantsare applied AFTER regular variants -- order matters for class specificity- Empty variant values (
nullor empty string) are valid -- useful for "no additional styles" case - Base classes are always applied -- you cannot conditionally remove them via variants
- Both
classandclassNamework incompoundVariantsconfig -- pick one and be consistent (classin non-React contexts,classNameif you prefer React conventions) - Don't forget
import typeforVariantProps:import { cva, type VariantProps }
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST define all variant options in the variants object - NEVER use conditional class logic outside cva)
(You MUST use VariantProps to extract types - NEVER manually define variant prop types)
(You MUST use defaultVariants for initial state - NEVER rely on undefined props for defaults)
(You MUST use compoundVariants for multi-condition styles - NEVER nest ternaries for combined states)
Failure to follow these rules will break type safety, create inconsistent styling, and defeat the purpose of using CVA.
</critical_reminders>