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.