First-class variant API for Tailwind CSS that enables developers to create reusable component styles with type-safe variants, slots support, and automatic conflict resolution
—
Tailwind Variants Lite Mode provides a lightweight version of the library with reduced bundle size by excluding tailwind-merge integration. This mode is ideal for applications that prefer minimal dependencies or implement their own conflict resolution.
import { tv, createTV, cn, cnBase, defaultConfig } from "tailwind-variants/lite";For CommonJS:
const { tv, createTV, cn } = require("tailwind-variants/lite");Lightweight version of the main tv function without tailwind-merge integration.
const tv: TVLite;The lite tv function provides the same variant API as the full version but without automatic conflict resolution. All class names are concatenated without merging conflicting Tailwind classes.
Creates a lite tv instance (no configuration needed since tailwind-merge is not available).
function createTV(): TVLite;Returns: A lite tv function instance
Basic class name utility without tailwind-merge integration.
const cn: <T extends CnOptions>(...classes: T) => CnReturn;Parameters:
...classes - Variable number of class valuesReturns: Concatenated class string or undefined
Base class name utility (same as full version).
const cnBase: <T extends CnOptions>(...classes: T) => CnReturn;Internal adapter function used by lite mode for class name processing.
const cnAdapter: (...classnames: CnOptions) => (config?: any) => CnReturn;Parameters:
...classnames - Variable number of class valuesReturns: Function that accepts optional config and returns processed class string
Note: This is primarily an internal function but available as a public export.
import { tv } from "tailwind-variants/lite";
const button = tv({
base: "font-medium rounded-lg px-4 py-2",
variants: {
color: {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-gray-500 text-white hover:bg-gray-600",
},
size: {
sm: "px-3 py-1.5 text-sm",
lg: "px-6 py-3 text-lg",
},
},
defaultVariants: {
color: "primary",
size: "sm",
},
});
// Usage
const primaryButton = button({ color: "primary", size: "lg" });
// Returns: "font-medium rounded-lg px-4 py-2 bg-blue-500 text-white hover:bg-blue-600 px-6 py-3 text-lg"import { tv } from "tailwind-variants/lite";
const card = tv({
slots: {
base: "rounded-lg border shadow-sm bg-white",
header: "px-6 py-4 border-b border-gray-200",
body: "px-6 py-4",
footer: "px-6 py-4 border-t border-gray-100 bg-gray-50",
},
variants: {
padding: {
none: {
header: "px-0 py-0",
body: "px-0 py-0",
footer: "px-0 py-0",
},
sm: {
header: "px-4 py-3",
body: "px-4 py-3",
footer: "px-4 py-3",
},
},
},
});
const { base, header, body, footer } = card({ padding: "sm" });import { cn } from "tailwind-variants/lite";
// Basic usage
const classes = cn(
"px-4 py-2 rounded",
isActive && "bg-blue-500 text-white",
{ "opacity-50": isDisabled }
);
// Returns concatenated classes without conflict resolutionThe lite mode does not include tailwind-merge, so conflicting classes are not automatically resolved:
// Full mode with tailwind-merge
import { tv } from "tailwind-variants";
const fullButton = tv({ base: "px-4 px-6" }); // Resolves to "px-6"
// Lite mode
import { tv } from "tailwind-variants/lite";
const liteButton = tv({ base: "px-4 px-6" }); // Returns "px-4 px-6"By excluding tailwind-merge dependency, the lite mode significantly reduces bundle size:
Since tailwind-merge is not available, configuration options related to it are ignored:
// This has no effect in lite mode
const button = tv({
base: "px-4 py-2",
}, { twMerge: true, twMergeConfig: { /* config */ } });When using lite mode, you need to handle class conflicts manually:
import { tv } from "tailwind-variants/lite";
// Manually avoid conflicts
const button = tv({
base: "font-medium rounded-lg",
variants: {
size: {
sm: "px-3 py-1.5 text-sm", // Don't repeat px/py in base
md: "px-4 py-2 text-base", // Ensure no conflicts
lg: "px-6 py-3 text-lg",
},
},
});interface TVLite {
<V extends TVVariants<S, B>, CV extends TVCompoundVariants<V, S, B>, DV extends TVDefaultVariants<V, S>, B extends ClassValue = undefined, S extends TVSlots = undefined>(options: {
base?: B;
slots?: S;
variants?: V;
compoundVariants?: CV[];
compoundSlots?: TVCompoundSlots<V, S, B>[];
defaultVariants?: DV;
}): TVReturnType<V, S, B>;
}When using lite mode, structure your variants to avoid class conflicts:
// Good: No conflicting classes
const button = tv({
base: "font-medium rounded-lg transition-colors",
variants: {
color: {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
},
size: {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
},
},
});
// Avoid: Conflicting padding classes
const badButton = tv({
base: "px-4 py-2 font-medium",
variants: {
size: {
sm: "px-2 py-1 text-sm", // Conflicts with base px-4 py-2
lg: "px-6 py-3 text-lg", // Conflicts with base px-4 py-2
},
},
});const button = tv({
base: "font-medium rounded-lg",
variants: {
color: {
primary: "bg-blue-500 text-white",
danger: "bg-red-500 text-white",
},
outline: {
true: "bg-transparent border-2",
false: "",
},
},
compoundVariants: [
{
color: "primary",
outline: true,
class: "border-blue-500 text-blue-500 hover:bg-blue-50",
},
{
color: "danger",
outline: true,
class: "border-red-500 text-red-500 hover:bg-red-50",
},
],
});Install with Tessl CLI
npx tessl i tessl/npm-tailwind-variants