Introduction
Tailwind Variants a first-class variant API library for TailwindCSS (opens in a new tab).
Key features
Variants
Since Stitches (opens in a new tab) introduced variants, we became big fans of them, they are a great way to create a consistent design system, so we created Tailwind Variants to bring them to TailwindCSS.
import { tv } from 'tailwind-variants';
const button = tv({
base: 'font-medium bg-blue-500 text-white rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 text-white',
secondary: 'bg-purple-500 text-white'
},
size: {
sm: 'text-sm',
md: 'text-base',
lg: 'px-4 py-3 text-lg'
}
},
compoundVariants: [
{
size: ['sm', 'md'],
class: 'px-3 py-1'
}
],
defaultVariants: {
size: 'md',
color: 'primary'
}
});
return (
<button className={button({ size: 'sm', color: 'secondary' })}>
Click me
</button>
);To learn more about variants, check the variants page.
Responsive variants
Tailwind Variants allows you to apply variants to different screen sizes.
import { tv } from 'tailwind-variants';
const button = tv(
{
base: 'font-semibold text-white py-1 px-3 rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 hover:bg-blue-700',
secondary: 'bg-purple-500 hover:bg-purple-700',
success: 'bg-green-500 hover:bg-green-700',
error: 'bg-red-500 hover:bg-red-700'
}
}
},
{
responsiveVariants: ['xs', 'sm', 'md'] // `true` to apply to all screen sizes
}
);
button({
color: {
initial: 'primary',
xs: 'secondary',
sm: 'success',
md: 'error'
}
});
/**
* Result:
* font-semibold text-white py-1 px-3 rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700
* xs:bg-purple-500 xs:hover:bg-purple-700 sm:bg-green-500 sm:hover:bg-green-700 md:bg-red-500
* md:hover:bg-red-700
*/To learn more about responsive variants, check the variants page and the slots page
Split components into multiple slots
You can style multiple components at once using the slots property.
import { tv } from 'tailwind-variants';
const card = tv({
slots: {
base: 'md:flex bg-slate-100 rounded-xl p-8 md:p-0 dark:bg-gray-900',
avatar:
'w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto drop-shadow-lg',
wrapper: 'flex-1 pt-6 md:p-8 text-center md:text-left space-y-4',
description: 'text-md font-medium',
infoWrapper: 'font-medium',
name: 'text-sm text-sky-500 dark:text-sky-400',
role: 'text-sm text-slate-700 dark:text-slate-500'
}
});
const { base, avatar, wrapper, description, infoWrapper, name, role } = card();
return (
<figure className={base()}>
<img
className={avatar()}
src="/intro-avatar.png"
alt=""
width="384"
height="512"
/>
<div className={wrapper()}>
<blockquote>
<p className={description()}>
“Tailwind variants allows you to reduce repeated code in your project
and make it more readable. They fixed the headache of building a
design system with TailwindCSS.”
</p>
</blockquote>
<figcaption className={infoWrapper()}>
<div className={name()}>Zoey Lang</div>
<div className={role()}>Full-stack developer, NextUI</div>
</figcaption>
</div>
</figure>
);To learn more about slots and how to use them, check out the Slots page.
Overrides
Tailwind Variants provides a class / className prop for overriding classes on any component.
import { tv } from 'tailwind-variants';
const button = tv({
base: 'font-semibold text-white py-1 px-3 rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 hover:bg-blue-700',
secondary: 'bg-purple-500 hover:bg-purple-700',
success: 'bg-green-500 hover:bg-green-700',
error: 'bg-red-500 hover:bg-red-700'
}
}
});
button({
color: 'secondary',
class: 'bg-pink-500 hover:bg-pink-500' // overrides the color variant
});
/**
* Result:
* font-semibold text-white py-1 px-3 rounded-full active:opacity-80 bg-pink-500 hover:bg-pink-500
*/To learn more the overrides, check out this page.
Components composition
Tailwind Variants allows you to compose components using the extend parameter. It automatically merges the classes, slots, variants, defaultVariants and compoundVariants of the extended component.
import { tv } from 'tailwind-variants';
const baseButton = tv({
base: [
'font-semibold',
'dark:text-white',
'py-1',
'px-3',
'rounded-full',
'active:opacity-80',
'bg-zinc-100',
'hover:bg-zinc-200',
'dark:bg-zinc-800',
'dark:hover:bg-zinc-800'
]
});
const buyButton = tv({
extend: baseButton,
base: [
'text-sm',
'text-white',
'rounded-lg',
'shadow-lg',
'uppercase',
'tracking-wider',
'bg-blue-500',
'hover:bg-blue-600',
'shadow-blue-500/50',
'dark:bg-blue-500',
'dark:hover:bg-blue-600'
]
});
return (
<div className="flex gap-3">
<button className={baseButton()}>Button</button>
<button className={buyButton()}>Buy button</button>
</div>
);
/**
* buyButton();
*
* Result:
* font-semibold dark:text-white py-1 px-3 active:opacity-80 text-sm text-white rounded-lg
* shadow-lg shadow-blue-500/50 uppercase tracking-wider bg-blue-500 hover:bg-blue-600
* dark:bg-blue-500 dark:hover:bg-blue-600
*/To learn more about Components composition, check out this page.
Developer experience
Tailwind Variants is built with developer experience in mind, it provides a great autocomplete experience thanks to the fully-typed API, so when using TypeScript, slots, values, and breakpoints will be auto-completed for you.
Automatic conflict resolution
Tailwind Variants implements tailwind-merge (opens in a new tab) under the hood, so it will efficiently merge your classes, so you don't have to worry about TailwindCSS class conflicts.
Framework agnostic
Tailwind Variants is a utility library that works with any framework. It's not tied to React.
Community
We're excited to see the community adopt NextUI, raise issues, and provide feedback. Whether it's a feature request, bug report, or a project to showcase, please get involved!
- Discord (opens in a new tab)
- Twitter (opens in a new tab)
- GitHub Discussions (opens in a new tab)
- GitHub (opens in a new tab)
Credits
Tailwind Variants is heavily inspired by Stitches (opens in a new tab) and CVA (opens in a new tab).
Special thanks to Tianen Pang (opens in a new tab) for helping with the API design and library creation.
Special thanks to Junior Garcia (opens in a new tab) for building the library and the documentation.
Special thanks to Mark Skelton (opens in a new tab) for mantaining the library and the documentation.
Special thanks to Joe Bell (opens in a new tab) for creating CVA (opens in a new tab) it served as a great inspiration for this library.