Class Variance Authority is a framework agnostic tool for creating variants of a component with different classes. It’s super simple and you probably could write it yourself if you had to—but, you don’t have to because it already exists.
CVA allows you to:
- Define a set of base styles for the component.
- Define the styles unique to each variant.
- Allow you to define default variants when one isn’t explicitly specified.
- It allows you to create compound variants.
It works super well with Tailwind, but you don’t need use Tailwind to use CVA. You can use any utility classes that you want or even just the classes from our CSS modules like we did earlier.
If you’re using Tailwind and Visual Studio Code and CVA and the Tailwind CSS IntelliSense extension, then you might want to make this tweak to the settings.json
in your project.
Let’s look at an example where we refactor our button component from earlier to use CVA.
Refactoring the Variant Variant
I regret some of my previous naming choices, but here we are. Let’s look at some code and talk through it.
import { cva, type VariantProps } from 'class-variance-authority';
export const variants = cva(
[
'font-semibold',
'border',
'rounded',
'shadow-sm',
'inline-flex',
'items-center',
'cursor-pointer',
'gap-1.5',
'focus-visible:outline',
'focus-visible:outline-2',
'focus-visible:outline-offset-2',
'transition-colors',
'disabled:opacity-50',
'disabled:cursor-not-allowed',
'disabled:pointer-events-none',
],
{
variants: {
variant: {
primary: [
'bg-primary-600',
'text-white',
'border-transparent',
'hover:bg-primary-500',
'active:bg-primary-400',
],
secondary: [
'bg-white',
'text-slate-900',
'border-slate-300',
'hover:bg-slate-50',
'active:bg-slate-100',
],
destructive: [
'bg-danger-600',
'text-white',
'border-transparent',
'hover:bg-danger-500',
'active:bg-danger-400',
],
},
},
defaultVariants: {
variant: 'secondary',
},
},
);
One of the things that you’ll notice is that this is just a JavaScript object. You can use it with Svelte just as easily as you could with React or anything else.
Generating Types Automatically
We can also add the following:
type ButtonVariants = VariantProps<typeof variants>;
VariantProps
looks at the object returned from CVA and creates a type that you can use as a prop with almost any framework—as long as you’re using TypeScript, of course. This allows you the ability to share you component styling (e.g. your design language) across multiple projects—even if they’re using different frameworks.
Now, we can update the component to use a type based on the variants.
type ButtonProps = ComponentProps<'button'> & {
variant?: ButtonVariants['variant'];
size?: 'small' | 'medium' | 'large';
};
Can you add an additional variant for the size
property? We’ll checkout a solution here.