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 Joe Bell (opens in a new tab) for creating CVA (opens in a new tab) it served as a great inspiration for this library.
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.