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 logo](/assets/img/wallpaper-4k.png) `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 () => ; ``` # 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) * ![](https://img.youtube.com/vi/kHQNK2jU_TQ/sddefault.jpg) # [Class Variance Authority (CVA) Quickstart](https://www.youtube.com/watch?v=kHQNK2jU_TQ) Coding in Public•15th September 2023 * ![](https://img.youtube.com/vi/qGQRdCg6JRQ/sddefault.jpg) # [Authoring Components with CVA + tailwindcss](https://www.youtube.com/watch?v=qGQRdCg6JRQ) React Tips with Brooks Lybrand•29th August 2023 * ![](https://img.youtube.com/vi/eXRlVpw1SIQ/sddefault.jpg) # [Creating High-Quality React Components: Best Practices for Reusability](https://www.youtube.com/watch?v=eXRlVpw1SIQ) Josh tried coding•26th February 2023 * ![](https://img.youtube.com/vi/kTSwLLFa3WM/sddefault.jpg) # [Building a design system in Next.js with Tailwind!](https://www.youtube.com/watch?v=kTSwLLFa3WM) mewtru•4th January 2023 * ![](https://img.youtube.com/vi/B6FrDu2Qbt0/sddefault.jpg) # [Large Tailwind Components — What to do About All Those Classes](https://www.youtube.com/watch?v=B6FrDu2Qbt0) frontendfyi•21st November 2022 * ![](https://img.youtube.com/vi/T-Zv73yZ_QI/sddefault.jpg) # [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)