Build and review typed component variants with cva. Use when defining cva components, exposing variant props, composing styles, handling class conflicts, or generating variant galleries. For version upgrades, use migration guidance instead.
68
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Use the project's styling approach and component conventions. Apply the guidance below to the requested component rather than refactoring unrelated code. The beta docs are the source of truth for these recommendations.
Read the consuming package's manifest, lockfile, and existing imports before choosing an API. cva@1.0.0-beta.x and class-variance-authority@0.x are different packages. Beta releases can change without semver guarantees; verify the installed exports and types when an API below is unavailable. Do not upgrade dependencies as part of ordinary component work.
This guidance targets the cva 1.0 betas (verified against cva@1.0.0-beta.12): the single-object cva call, composes, defineConfig from cva/config, and getSchema from cva/tools. Older betas may have different entry points or APIs. For stable class-variance-authority, follow the stable docs: import from class-variance-authority and use cva(base, options). Do not apply beta-only configuration, composition, schema, or Tailwind CSS exports to stable projects. When an upgrade is requested, consult the release-specific migration guidance instead.
base, independent choices in variants, and combinations in compoundVariants.defaultVariants only supplies defaults inside the class function; it cannot set an element's attributes. Use it when the class function should supply defaults to its callers. Omitted props and undefined use those defaults. Avoid duplicating defaults in both places, and remember getSchema cannot read framework prop defaults.unset: null, then pass "unset". Do not assume passing null disables a beta variant.See Variants, Default variants, and the cva API reference.
Use VariantProps<typeof button> rather than repeating variant unions. It contains public variant props, not class or className, and omits variant names prefixed with _. Internal variants still work in defaults, compound variants, and direct calls; they are not a runtime access-control mechanism.
For a React wrapper, combine variant types with native element props and forward className to the class function. If names overlap incompatibly, omit the overlapping native props before combining them. Forward semantic props such as disabled to the actual HTML element as well as the class function. Use TypeScript's Required<Pick<...>> and Omit when a public variant must be required.
See Extracting variant types and the React gallery.
The preset exports cx, backed by clsx, for strings, nested arrays, and conditional objects. Use it instead of adding clsx or classnames for ordinary conditional class joining. cx does not deduplicate classes or resolve CSS conflicts.
For Tailwind CSS, write complete utility class strings in variant definitions. Do not interpolate fragments such as bg-${tone}-500: Tailwind scans source text and cannot infer the resulting names. Map variant values to static class strings instead, as shown in Installation.
Pass extra classes through the class function's class or className prop. Appending a utility does not guarantee a CSS override. For Tailwind CSS, follow the project's existing conflict strategy:
cn: import { cn as merge } from "cn", then defineConfig({ cx: merge }).cva/tailwindcss: import after Tailwind CSS and prefix overridable component defaults with base:, including defaults selected by variants or compound variants. Ordinary utilities override them through the cascade. Keep hover and disabled state styles ordinary; any ordinary utility also beats conditional base: defaults. Put base: before pseudo-element variants, and remember important declarations reverse layer priority.tailwind-merge: combine its twMerge with the preset cx to preserve conditional objects. Bare twMerge has a narrower input grammar.For the last option:
import { defineConfig } from "cva/config";
import { cx as joinClasses } from "cva";
import { twMerge } from "tailwind-merge";
export const { cva, cx: cn } = defineConfig({
cx: (...inputs) => twMerge(joinClasses(...inputs)),
});Import the configured functions throughout that project. Do not accidentally use the preset for components expected to merge conflicts. A custom concatenator owns the input grammar and must support empty calls, variadic inputs, and composed strings.
See Handling class conflicts and Extending Components.
Use composes for reusable cva class functions. Pass one function or an inline array; use as const for a stored array to retain tuple inference. Overlapping variant values add each component's matching classes. Defaults merge with the last composed default winning, then local defaults take precedence across the composition.
A cva class function produces a string, so apply it to the appropriate HTML element. For a React render-prop API, the docs recommend Base UI's useRender; do not invent a cva styled API. For compound component styling, consider the CSS cascade, custom properties, and selectors such as :has() before introducing JavaScript state solely to share styles.
cva does not interpret responsive variant objects. Express responsive styles in your CSS, define a named variant with breakpoint utilities, or show/hide variants at breakpoints as appropriate for the existing UI.
See Composing Components, Compound Components, Polymorphism, and FAQs.
Call getSchema on the cva class function, not its React or other framework wrapper. Read the schema outside rendering and iterate typed values to generate galleries, documentation, or controls without duplicate lists. Boolean and numeric values retain their types; internal and empty variants are omitted. defaultValue is present only when that variant has a declared default.
Keep an omitted-prop case when demonstrating defaults. Derive layout counts from the displayed schema arrays rather than hard-coding them. Put schema consumers in stories or documentation when the application does not need them at runtime.
See Tools. Verify changed class outputs against the installed package and type-check consuming components using the project's checks.
638844f
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.