cva@beta: Documentation for the cva@beta release (https://cva.style/beta)
# Class Variance Authority
> Build type-safe, variant-driven class names for any styling approach, with first-class Tailwind CSS support.
 `cva` builds type-safe, variant-driven class names for any styling approach, with first-class Tailwind CSS support. CSS-in-TS libraries such as [Stitches](https://stitches.dev/docs/variants) and [Vanilla Extract](https://vanilla-extract.style/documentation/api/style-variants/) handle type-safe UI variants without you managing class names or stylesheet composition by hand. CSS-in-TS isn’t for everyone, though. You may need full control over your stylesheet output, use a framework such as Tailwind CSS, or prefer writing your own CSS. Creating variants with the “traditional” CSS approach can become an arduous task: manually matching classes to props, and manually adding types. `cva` takes away those pain points, so you can focus on building your UI. ## Sponsors [Section titled “Sponsors”](#sponsors) ## Acknowledgments [Section titled “Acknowledgments”](#acknowledgments) * [**Stitches**](https://stitches.dev/) ([WorkOS](https://workos.com))\ Huge thanks to the WorkOS team for pioneering the `variants` API movement: your open-source contributions are immensely appreciated * [**clb**](https://github.com/crswll/clb) ([Bill Criswell](https://github.com/crswll))\ This project originally started out with the intention of merging into the wonderful [`clb`](https://github.com/crswll/clb) library, but after some discussion with Bill, we felt it was best to go down the route of a separate project.\ I’m so grateful to Bill for sharing his work publicly and for getting me excited about building a type-safe variants API for classes. If you have a moment, please go and [star the project on GitHub](https://github.com/crswll/clb). Thank you Bill! * [**clsx**](https://github.com/lukeed/clsx) ([Luke Edwards](https://github.com/lukeed))\ Previously, this project surfaced a custom `cx` utility for flattening classes, but it lacked the ability to handle variadic arguments or objects. [clsx](https://github.com/lukeed/clsx) provided those extra features with quite literally zero increase to the bundle size: a no-brainer to switch! * [**Vanilla Extract**](http://vanilla-extract.style) ([Seek](https://github.com/seek-oss)) ## Downloads [Section titled “Downloads”](#downloads) * [Wallpaper](/assets/img/wallpaper-4k.png) ## License [Section titled “License”](#license) [Apache-2.0 License](https://github.com/joe-bell/cva/blob/main/LICENSE) © [Joe Bell](https://joebell.studio)
# API Reference
> Reference for the cva, cx, getSchema, and defineConfig functions.
## `cva` [Section titled “cva”](#cva) Builds a `cva` component
```ts
import { cva } from "cva";
const component = cva(options);
```
### Parameters [Section titled “Parameters”](#parameters) 1. `options` * `base`: the base class name (`string`, `string[]` or other [`clsx` value](https://github.com/lukeed/clsx#input)) * `variants`: your variants schema * `compoundVariants`: variants based on a combination of previously defined variants * `defaultVariants`: set default values for previously defined variants * `composes`: shallow merge one or more other `cva` components into this one, as a single component or an array (see [Composing Components](/beta/getting-started/composing-components)) ### Returns [Section titled “Returns”](#returns) A `cva` component function ## `cx` [Section titled “cx”](#cx) Concatenates class names (an alias of [`clsx`](https://github.com/lukeed/clsx))
```ts
import { cx } from "cva";
const className = cx(classes);
```
### Parameters [Section titled “Parameters”](#parameters-1) * `classes`: array of classes to be concatenated ([see `clsx` usage](https://github.com/lukeed/clsx#input)) ### Returns [Section titled “Returns”](#returns-1) `string` ## `getSchema` [Section titled “getSchema”](#getschema) Extracts a plain-object schema (variant names, possible values, and default values) from a `cva` component. Use it to generate Storybook controls, documentation, or any other UI that reads a component’s variants without re-declaring them. See [Utilities](/beta/getting-started/utilities) for use cases.
```ts
import { cva, getSchema } from "cva";
const button = cva({
base: "button",
variants: {
intent: {
primary: "button--primary",
secondary: "button--secondary",
},
disabled: {
true: "button--disabled",
false: "button--enabled",
},
},
defaultVariants: {
intent: "primary",
disabled: false,
},
});
getSchema(button);
// => {
// intent: { values: ["primary", "secondary"], defaultValue: "primary" },
// disabled: { values: [true, false], defaultValue: false },
// }
```
`getSchema` omits a variant that has no values (e.g. `variants: { empty: {} }`). ### Parameters [Section titled “Parameters”](#parameters-2) `component`: a component created by `cva` (including components composed via [`composes`](#cva)) ### Returns [Section titled “Returns”](#returns-2) An object keyed by variant name. Each entry has: * `values`: a readonly array of the variant’s possible values * `defaultValue`: present only if the variant has a `defaultVariants` entry ## `defineConfig` [Section titled “defineConfig”](#defineconfig) Generate `cva` and `cx` functions based on your preferred configuration. Store in a `cva.config.ts` file, and import across your project. cva.config.ts
```ts
import { defineConfig } from "cva";
export const { cva, cx } = defineConfig(options);
```
1. `options` * `hooks` * `onComplete`: returns a concatenated class string of all classes passed to `cx` or `cva`. `defineConfig` also returns a deprecated `compose` function for migrating from `class-variance-authority`; use the `composes` property on `cva` instead. See [What’s new?](/beta/getting-started/whats-new#1-compose--composes).
# 11ty
> Build a cva button component in an 11ty template with Tailwind CSS.
This example builds a `button` component with `cva` in an 11ty template. ## Tailwind CSS [Section titled “Tailwind CSS”](#tailwind-css) button.11ty.js
```js
const { cva } = require("cva");
// ⚠️ Disclaimer: Use of Tailwind CSS is optional
const button = cva({
base: "button",
variants: {
intent: {
primary: [
"bg-blue-500",
"text-white",
"border-transparent",
"hover:bg-blue-600",
],
secondary: [
"bg-white",
"text-gray-800",
"border-gray-400",
"hover:bg-gray-100",
],
},
size: {
small: ["text-sm", "py-1", "px-2"],
medium: ["text-base", "py-2", "px-4"],
},
},
compoundVariants: [{ intent: "primary", size: "medium", class: "uppercase" }],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
module.exports = function ({ label, intent, size }) {
return ``;
};
```
# Astro
> A cva button component built with Astro and Tailwind CSS.
This example builds a `button` component with `cva` in an Astro project. ## Tailwind CSS [Section titled “Tailwind CSS”](#tailwind-css) [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/astro-with-tailwindcss/src/components/button.astro)
# BEM
> Use cva to apply BEM class names instead of utility classes.
This example applies BEM class names with `cva` instead of utility classes. styles.css
```css
.button {
/* */
}
.button--primary {
/* */
}
.button--secondary {
/* */
}
.button--small {
/* */
}
.button--medium {
/* */
}
.button--primary-small {
/* */
}
```
```ts
import { cva } from "cva";
const button = cva({
base: "button",
variants: {
intent: {
primary: "button--primary",
secondary: "button--secondary",
},
size: {
small: "button--small",
medium: "button--medium",
},
},
compoundVariants: [
{ intent: "primary", size: "medium", class: "button--primary-small" },
],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
button();
// => "button button--primary button--medium button--primary-small"
button({ intent: "secondary", size: "small" });
// => "button button--secondary button--small"
```
# Other Use Cases
> Use cva to manage variant-driven strings beyond class names, such as dynamic text content.
Although primarily designed for handling class names, at its core `cva` is a fancy way of managing a string… ## Dynamic text content [Section titled “Dynamic text content”](#dynamic-text-content)
```ts
import { cva } from "cva";
const greeter = cva({
base: "Good morning!",
variants: {
isLoggedIn: {
true: "Here's a secret only logged in users can see",
false: "Log in to find out more…",
},
},
defaultVariants: {
isLoggedIn: "false",
},
});
greeter();
// => "Good morning! Log in to find out more…"
greeter({ isLoggedIn: "true" });
// => "Good morning! Here's a secret only logged in users can see"
```
# React with CSS Modules
> A cva button component styled with CSS Modules in React.
This example builds a `button` component with `cva`, styled with CSS Modules, in a React project. [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/react-with-css-modules/src/components/button/button.tsx)
# React with Tailwind CSS
> A cva button component built with React and Tailwind CSS, including a compound-components example.
These examples build a `button` component with `cva` and Tailwind CSS in a React project, including a compound-components variant. ## Basic component [Section titled “Basic component”](#basic-component) [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/react-with-tailwindcss/src/components/button/button.tsx) ## Compound components [Section titled “Compound components”](#compound-components) [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/react-with-tailwindcss-compound/src/components/nav/nav.tsx)
# Svelte
> A cva button component built with Svelte.
This example builds a `button` component with `cva` in a Svelte project. Open the sandbox below to explore the full source. [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/svelte/src/components/button.svelte)
# Vue
> A cva button component built with Vue.
This example builds a `Button` component with `cva` in a Vue project. Open the sandbox below to explore the full source. [View on GitHub ↗](https://github.com/joe-bell/cva/tree/main/examples/beta/vue/src/components/Button.vue)
# FAQs
> Answers to common questions about cva's API design, including responsive variants and styled-component APIs.
## Why don’t you provide a `styled` API? [Section titled “Why don’t you provide a styled API?”](#why-dont-you-provide-a-styled-api) Long story short: it’s unnecessary. `cva` encourages you to think of components as traditional CSS classes: * Less JavaScript is better * They’re framework agnostic; truly reusable * Polymorphism is free: apply the class to your preferred HTML element * Less opinionated; you’re free to build components with `cva` however you’d like See the [“Polymorphism”](/beta/getting-started/polymorphism) documentation for further recommendations. ## How can I create [responsive variants like Stitches.js](https://stitches.dev/docs/responsive-styles#responsive-variants)? [Section titled “How can I create responsive variants like Stitches.js?”](#how-can-i-create-responsive-variants-like-stitchesjs) You can’t. `cva` doesn’t know about how you choose to apply CSS classes, and it doesn’t want to. We recommend either: * Showing/hiding elements with different variants, based on your preferred breakpoint. - Create a bespoke variant that changes based on the breakpoint. *e.g. `button({ intent: "primaryUntilMd" })`* This is something I’ve been thinking about since the project’s inception, and I’ve gone back and forth many times on the idea of building it. It’s a large undertaking and brings all the complexity of supporting many different build tools and frameworks. In my experience, “responsive variants” are typically rare, and hiding/showing different elements is usually good enough to get by. To be frank, I’m probably not going to build/maintain a solution unless someone periodically gives me a thick wad of cash to do so, and even then I’d probably rather spend my free time living my life.
# Composing Components
> Merge one or more cva components into a single component with the composes property.
Shallow merge one or more `cva` components into a single component with the `composes` property. Pass a single component directly, or pass multiple components as an array: components/card.ts
```ts
import { cva, type VariantProps } from "cva";
const box = cva({
base: "box box-border",
variants: {
margin: { 0: "m-0", 2: "m-2", 4: "m-4", 8: "m-8" },
padding: { 0: "p-0", 2: "p-2", 4: "p-4", 8: "p-8" },
},
defaultVariants: {
margin: 0,
padding: 0,
},
});
const root = cva({
base: "card rounded border-solid border-slate-300",
variants: {
shadow: {
md: "drop-shadow-md",
lg: "drop-shadow-lg",
xl: "drop-shadow-xl",
},
},
});
export const card = cva({ composes: [box, root] });
export interface CardProps extends VariantProps {}
card({ margin: 2, shadow: "md" });
// => "box box-border m-2 p-0 card rounded border-solid border-slate-300 drop-shadow-md"
card({ margin: 2, shadow: "md", class: "adhoc-class" });
// => "box box-border m-2 p-0 card rounded border-solid border-slate-300 drop-shadow-md adhoc-class"
```
## Extending variants [Section titled “Extending variants”](#extending-variants) If more than one composed component declares the same variant, their values combine. Each component still resolves and applies its own class, so overlapping values extend one another rather than override:
```ts
const a = cva({ base: "a", variants: { style: { primary: "a-primary" } } });
const b = cva({
base: "b",
variants: { style: { primary: "b-primary", secondary: "b-secondary" } },
});
const combined = cva({ composes: [a, b] });
combined({ style: "primary" });
// => "a a-primary b b-primary"
```
`defaultVariants` follow a last-wins merge. If multiple composed components declare a default for the same variant, the last one in the array wins. A local `defaultVariants` on the composing component wins over all of them. `cva` applies that value to every composed component, not just the one that declared it:
```ts
const a = cva({
base: "a",
variants: { style: { primary: "a-primary" } },
defaultVariants: { style: "primary" },
});
const b = cva({
base: "b",
variants: { style: { primary: "b-primary", secondary: "b-secondary" } },
defaultVariants: { style: "secondary" },
});
const combined = cva({ composes: [a, b] });
combined();
// => "a b b-secondary"
```
Caution Pass components to `composes` as an inline array literal, or one marked `as const`. A pre-declared, mutable array variable (`const list = [a, b]`) loses the tuple inference `composes` relies on, which can silently widen or drop variant types.
# Compound Components
> Build compound component sets, such as Accordion.Item, with cva and CSS.
For larger, more complex components, you may end up wanting to create a set of composable components that work together: “Compound Components” `cva` encourages you to build these compound components with CSS: leverage the cascade, custom properties, `:has()` selectors, and more… ## Examples [Section titled “Examples”](#examples) * [React with Tailwind CSS (Compound Components)](../examples/react/tailwindcss#compound-components)
# Extending Components
> Pass extra classes to a cva component with the class or className prop.
All `cva` components provide an optional `class` **or** `className` prop, which can be used to pass additional classes to the component. components/button.ts
```ts
import { cva } from "cva";
const button = cva({ base: "font-semibold" });
button({ class: "m-4" });
// => "font-semibold m-4"
button({ className: "m-4" });
// => "font-semibold m-4"
```
# Installation
> Install cva@beta and configure Tailwind CSS IntelliSense and tailwind-merge.
* pnpm
```sh
pnpm i cva@beta
```
* npm
```sh
npm i cva@beta
```
* yarn
```sh
yarn add cva@beta
```
* bun
```sh
bun add cva@beta
```
* deno
```sh
deno add cva@beta
```
## Tailwind CSS [Section titled “Tailwind CSS”](#tailwind-css) If you’re a Tailwind user, here are some additional (optional) steps to get the most out of `cva`: ### IntelliSense [Section titled “IntelliSense”](#intellisense) You can enable autocompletion inside `cva` using the steps below: * Visual Studio Code 1. [Install the “Tailwind CSS IntelliSense” Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) 2. Add the following to your [`.vscode/settings.json`](https://code.visualstudio.com/docs/getstarted/settings): .vscode/settings.json
```json
{
"tailwindCSS.classFunctions": ["cva", "cx"],
}
```
* Zed Add the following to your [`.zed/settings.json`](https://zed.dev/docs/configuring-zed#settings-files): .zed/settings.json
```json
{
"lsp": {
"tailwindcss-language-server": {
"settings": {
"classFunctions": ["cva", "cx"],
}
}
}
}
```
* Neovim 1. [Install the extension](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) 2. Add the following configuration:
```lua
require 'lspconfig'.tailwindcss.setup({
settings = {
tailwindCSS = {
classFunctions = { "cva", "cx" },
},
},
})
```
* WebStorm 1. Check the version. Available for [WebStorm 2023.1](https://www.jetbrains.com/webstorm/whatsnew/#version-2023-1-tailwind-css-configuration) and later 2. Open the settings. Go to [Languages and Frameworks | Style Sheets | Tailwind CSS](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_configuration) 3. Add the following to your tailwind configuration
```json
{
"classFunctions": ["cva", "cx"]
}
```
### Handling style conflicts [Section titled “Handling style conflicts”](#handling-style-conflicts) If you want to merge Tailwind CSS classes without conflicts, you may wish to [roll-your-own `cva`](../api-reference#defineconfig) with the [`tailwind-merge`](https://github.com/dcastil/tailwind-merge) package:
# Polymorphism
> Apply cva classes to any HTML element, or roll your own render prop with Base UI's useRender.
`cva` components are polymorphic (and framework-agnostic) by default: apply the class to your preferred HTML element…
```tsx
import { button } from "./components/button";
export default () => (
Sign up
);
```
## Alternative approaches [Section titled “Alternative approaches”](#alternative-approaches) ### React [Section titled “React”](#react) If you’d prefer to use a React-based API, `cva` strongly recommends using [Base UI’s `useRender` hook](https://base-ui.com/react/utils/use-render) to roll your own `render` prop.
```tsx
"use client";
import { cva, type VariantProps } from "cva";
import { useRender } from "@base-ui/react/use-render";
import { mergeProps } from "@base-ui/react/merge-props";
const _BUTTON_DEFAULT_TAG = "button" satisfies React.ElementType;
export interface ButtonProps
extends
useRender.ComponentProps,
VariantProps {}
const button = cva({
base: "button",
variants: {
intent: {
primary: "bg-blue-500 hover:bg-blue-600 border-transparent text-white",
secondary: "border-gray-400 bg-white text-gray-800 hover:bg-gray-100",
},
},
});
export function Button({
render,
intent = "primary",
className,
...props
}: ButtonProps) {
const defaultProps: useRender.ElementProps = {
className: button({ intent, className }),
// Consider data-attributes for debugging purposes
["data-button" as string]: "",
["data-intent" as string]: intent,
};
return useRender({
defaultTagName: _BUTTON_DEFAULT_TAG,
render,
props: mergeProps(defaultProps, props),
});
}
```
#### Usage [Section titled “Usage”](#usage)
```tsx
import { Button } from "./components/button";
// Renders:
//
// Contact
//
export default () => }>Contact;
```
# TypeScript
> Extract variant types with VariantProps and require specific variants with TypeScript utility types.
## Extracting variant types [Section titled “Extracting variant types”](#extracting-variant-types) `cva` offers the `VariantProps` helper to extract variant types components/button.ts
```ts
import type { VariantProps } from "cva";
import { cva, cx } from "cva";
/**
* Button
*/
export type ButtonProps = VariantProps;
export const button = cva(/* … */);
```
## Required variants [Section titled “Required variants”](#required-variants) To keep the API small and unopinionated, `cva` **doesn’t** offer a built-in solution for setting required variants. Instead, we recommend using TypeScript’s [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html): components/button.ts
```ts
import { cva, type VariantProps } from "cva";
export type ButtonVariantProps = VariantProps;
export const buttonVariants = cva({
base: "…",
variants: {
optional: { a: "…", b: "…" },
required: { a: "…", b: "…" },
},
});
/**
* Button
*/
export interface ButtonProps
extends
Omit,
Required> {}
export const button = (props: ButtonProps) => buttonVariants(props);
// ❌ TypeScript Error:
// Argument of type "{}": is not assignable to parameter of type "ButtonProps".
// Property "required" is missing in type "{}" but required in type
// "ButtonProps".
button({});
// ✅
button({ required: "a" });
```
# Utilities
> Extract a component's variant names, values, and defaults with getSchema.
## `getSchema` [Section titled “getSchema”](#getschema) Re-declaring variants for a Storybook story or prop table creates a second copy to keep in sync by hand. `getSchema` extracts them from the component instead, so your config stays the single source of truth:
```ts
import { cva, getSchema } from "cva";
const button = cva({
base: "button",
variants: {
intent: {
primary: "button--primary",
secondary: "button--secondary",
},
size: {
small: "button--small",
large: "button--large",
},
},
defaultVariants: {
intent: "primary",
size: "small",
},
});
getSchema(button);
// => {
// intent: { values: ["primary", "secondary"], defaultValue: "primary" },
// size: { values: ["small", "large"], defaultValue: "small" },
// }
```
The schema is fully typed: `values` narrows to the variant’s literal values, and `defaultValue` only appears when the component declares one. For the full signature, see the [API reference](/beta/api-reference#getschema).
# Variants
> Create variants, compound variants, and disable variants with cva@beta.
## Creating variants [Section titled “Creating variants”](#creating-variants) To kick things off, let’s build a “basic” `button` component, using `cva` to handle our variant’s classes components/button.ts
```ts
import { cva } from "cva";
const button = cva({
base: "rounded border font-semibold",
// **or**
// base: ["font-semibold", "border", "rounded"],
variants: {
intent: {
primary: "border-transparent bg-blue-500 text-white hover:bg-blue-600",
// **or**
// primary: [
// "bg-blue-500",
// "text-white",
// "border-transparent",
// "hover:bg-blue-600",
// ],
secondary: "border-gray-400 bg-white text-gray-800 hover:bg-gray-100",
},
size: {
small: "px-2 py-1 text-sm",
medium: "px-4 py-2 text-base",
},
},
compoundVariants: [
{
intent: "primary",
size: "medium",
class: "uppercase",
// **or** if you're a React.js user, `className` may feel more consistent:
// className: "uppercase"
},
],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
button();
// => "rounded border font-semibold border-transparent bg-blue-500 text-white hover:bg-blue-600 px-4 py-2 text-base uppercase"
button({ intent: "secondary", size: "small" });
// => "rounded border font-semibold border-gray-400 bg-white text-gray-800 hover:bg-gray-100 px-2 py-1 text-sm"
```
## Compound variants [Section titled “Compound variants”](#compound-variants) Variants that apply when multiple other variant conditions are met. components/button.ts
```ts
import { cva } from "cva";
const button = cva({
base: "…",
variants: {
intent: { primary: "…", secondary: "…" },
size: { small: "…", medium: "…" },
},
compoundVariants: [
// Applied via:
// `button({ intent: "primary", size: "medium" })`
{
intent: "primary",
size: "medium",
class: "…",
},
],
});
```
### Targeting multiple variant conditions [Section titled “Targeting multiple variant conditions”](#targeting-multiple-variant-conditions) components/button.ts
```ts
import { cva } from "cva";
const button = cva({
base: "…",
variants: {
intent: { primary: "…", secondary: "…" },
size: { small: "…", medium: "…" },
},
compoundVariants: [
// Applied via:
// `button({ intent: "primary", size: "medium" })`
// or
// `button({ intent: "secondary", size: "medium" })`
{
intent: ["primary", "secondary"],
size: "medium",
class: "…",
},
],
});
```
## Disabling variants [Section titled “Disabling variants”](#disabling-variants) To disable a variant completely, provide an option with a value of `null`. If you’re stuck on naming, we recommend setting an explicit `"unset"` option ([similar to the CSS keyword](https://developer.mozilla.org/en-US/docs/Web/CSS/unset)).
```ts
import { cva } from "cva";
const button = cva({
base: "button",
variants: {
intent: {
unset: null,
primary: "button--primary",
secondary: "button--secondary",
},
},
});
button({ intent: "unset" });
// => "button"
```
# What's New?
> What changed in cva@1.0, defineConfig, composes, getSchema, and other breaking changes since class-variance-authority@0.x.
*** What’s changed since `class-variance-authority@0.*`? ## Features [Section titled “Features”](#features) ### 1. `defineConfig` [Section titled “1. defineConfig”](#1-defineconfig) Extend `cva` via the new [`defineConfig` API](/beta/api-reference#defineconfig). Use `cva`/`cx` with `tailwind-merge` via the new [`hooks.onComplete` option](/beta/getting-started/installation#handling-style-conflicts). ### 2. `composes` [Section titled “2. composes”](#2-composes) Shallow merge one or more `cva` components into a single component via the new [`composes` property](/beta/getting-started/composing-components). This [replaces the `compose` function](#1-compose--composes). ### 3. `getSchema` [Section titled “3. getSchema”](#3-getschema) Extract a plain-object schema (variant names, values and defaults) from a `cva` component via the new [`getSchema` utility](/beta/getting-started/utilities). Use it to generate Storybook controls or any other UI that reads a component’s variants. ## Deprecations [Section titled “Deprecations”](#deprecations) ### 1. `compose` → `composes` [Section titled “1. compose → composes”](#1-compose--composes) The `compose` method is deprecated in favor of the `composes` property inside `cva`. Pass a single component directly, or pass multiple components as an array.
```diff
-import { cva, compose } from "cva";
+import { cva } from "cva";
const box = cva({ /* ... */ });
const root = cva({ /* ... */ });
-const card = compose(box, root);
+const card = cva({ composes: [box, root] });
```
See [Composing Components](/beta/getting-started/composing-components) for more details and migration examples. ## Enhancements [Section titled “Enhancements”](#enhancements) ### 1. `class-variance-authority` → `cva` [Section titled “1. class-variance-authority → cva”](#1-class-variance-authority--cva) One of the biggest (and let’s be honest, most important) complaints about `class-variance-authority` was that the name was just too damn long. Shout-out to GitHub for transferring `npm` ownership of `cva`! ### 2. `cva` now accepts a single parameter [Section titled “2. cva now accepts a single parameter”](#2-cva-now-accepts-a-single-parameter) Base styles are now applied via the named `base` property.
```diff
-import { cva } from "class-variance-authority";
+import { cva } from "cva";
const component = cva({ base: "your-base-class" });
```
### 3. Goodbye `null` [Section titled “3. Goodbye null”](#3-goodbye-null) Previously, passing `null` to a variant would disable it completely, to match the behavior of Stitches.js. However, this [caused a great deal of confusion](https://github.com/joe-bell/cva/discussions/97). Instead, we now recommend explicitly [rolling your own `unset` variant](/beta/getting-started/variants#disabling-variants). ### 4. Clearer type guards [Section titled “4. Clearer type guards”](#4-clearer-type-guards) `cva` uses generic type parameters to infer variant types. Some users mistook these for a customization option. If you now pass a generic type parameter, `cva` throws an error.
# Tutorials
> Community videos, podcasts, and articles about cva.
## YouTube [Section titled “YouTube”](#youtube) *  # [Class Variance Authority (CVA) Quickstart](https://www.youtube.com/watch?v=kHQNK2jU_TQ) Coding in Public•15th September 2023 *  # [Authoring Components with CVA + tailwindcss](https://www.youtube.com/watch?v=qGQRdCg6JRQ) React Tips with Brooks Lybrand•29th August 2023 *  # [Creating High-Quality React Components: Best Practices for Reusability](https://www.youtube.com/watch?v=eXRlVpw1SIQ) Josh tried coding•26th February 2023 *  # [Building a design system in Next.js with Tailwind!](https://www.youtube.com/watch?v=kTSwLLFa3WM) mewtru•4th January 2023 *  # [Large Tailwind Components — What to do About All Those Classes](https://www.youtube.com/watch?v=B6FrDu2Qbt0) frontendfyi•21st November 2022 *  # [Tru Narla: Building a design system in Next.js with Tailwind](https://www.youtube.com/watch?v=T-Zv73yZ_QI) mewtru•8th October 2022 ## Audio [Section titled “Audio”](#audio) * [JS Party – Episode #277](https://changelog.com/jsparty/277) (25th May 2023) ## Articles [Section titled “Articles”](#articles) * ["Building a design system using Solidjs, Typescript, SCSS, CSS Variables and Vite"](https://dev.to/yaldram/building-a-design-system-using-solidjs-typescript-scss-css-variables-and-vite-setup-45nj) by Arsalan Ahmed Yaldram(15th April 2023) * ["Building a design system with dark mode using React, Typescript, scss, cva and Vite"](https://dev.to/yaldram/build-a-design-system-with-dark-mode-using-react-typescript-scss-cva-and-vite-setup-5778) by Arsalan Ahmed Yaldram(20th March 2023)