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
npx @tessl/cli install tessl/npm-tailwind-variants@3.1.0Tailwind Variants provides a 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. It offers a comprehensive solution for managing complex Tailwind CSS styling patterns through a chainable API that supports base styles, variants, compound variants, and default variants with full TypeScript support.
npm install tailwind-variantsOptional conflict resolution:
npm install tailwind-mergeFull mode (with tailwind-merge support):
import { tv, createTV, cn, cnBase, defaultConfig, VariantProps } from "tailwind-variants";Lite mode (smaller bundle, no conflict resolution):
import { tv, createTV, cn, cnBase, defaultConfig } from "tailwind-variants/lite";Utilities only:
import { cn, falsyToString, isEmptyObject, flatArray, VariantProps } from "tailwind-variants/utils";For CommonJS:
const { tv, createTV, cn } = require("tailwind-variants");import { tv } from "tailwind-variants";
// Simple component with 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",
danger: "bg-red-500 text-white",
},
size: {
sm: "text-sm px-4 py-2",
md: "text-base px-6 py-3",
lg: "text-lg px-8 py-4",
},
},
defaultVariants: {
color: "primary",
size: "md",
},
});
// Use the component
const primaryButton = button({ color: "primary", size: "lg" });
// Returns: "font-medium bg-blue-500 text-white rounded-full active:opacity-80 bg-blue-500 text-white text-lg px-8 py-4"Tailwind Variants is built around several key components:
The main tv function for creating styled components with variants, slots, compound variants, and default values. Supports both simple class-based styling and complex multi-slot component structures.
const tv: TV;
function createTV(config: TVConfig): TV;
interface TV {
<V extends TVVariants<S, B, EV>, CV extends TVCompoundVariants<V, S, B, EV, ES>, DV extends TVDefaultVariants<V, S, EV, ES>, B extends ClassValue = undefined, S extends TVSlots = undefined, E extends TVReturnType = TVReturnType<V, S, B, EV extends undefined ? {} : EV, ES extends undefined ? {} : ES>, EV extends TVVariants<ES, B, E["variants"], ES> = E["variants"], ES extends TVSlots = E["slots"] extends TVSlots ? E["slots"] : undefined>(options: {
extend?: E;
base?: B;
slots?: S;
variants?: V;
compoundVariants?: CV;
compoundSlots?: TVCompoundSlots<V, S, B>[];
defaultVariants?: DV;
}, config?: TVConfig): TVReturnType<V, S, B, EV, ES, E>;
}Utility functions for conditional class name construction with support for objects, arrays, and automatic conflict resolution via tailwind-merge integration.
const cn: <T extends CnOptions>(...classes: T) => (config?: TWMConfig) => CnReturn;
const cnBase: <T extends CnOptions>(...classes: T) => CnReturn;Lightweight version of tailwind-variants with reduced bundle size, excluding tailwind-merge integration for applications that prefer minimal dependencies.
const tv: TVLite;
function createTV(): TVLite;
const cn: <T extends CnOptions>(...classes: T) => CnReturn;Type utility for extracting variant props from TV components for use in component interfaces, enabling type-safe prop passing in React and other frameworks.
type VariantProps<Component extends (...args: any) => any> = Omit<
OmitUndefined<Parameters<Component>[0]>,
"class" | "className"
>;interface TVConfig {
twMerge?: boolean;
twMergeConfig?: TWMergeConfig;
}
interface TWMConfig {
twMerge?: boolean;
twMergeConfig?: TWMergeConfig;
}
type TWMergeConfig = MergeConfig & LegacyMergeConfig;
const defaultConfig: TVConfig;type ClassValue = string | number | bigint | boolean | null | undefined | ClassDictionary | ClassArray;
interface ClassDictionary {
[key: string]: any;
}
interface ClassArray extends Array<ClassValue> {}
type CnOptions = ClassValue[];
type CnReturn = string | undefined;
type ClassProp<V extends unknown = ClassValue> =
| { class?: V; className?: never }
| { class?: never; className?: V };
type OmitUndefined<T> = T extends undefined ? never : T;
type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
type VariantProps<Component extends (...args: any) => any> = Omit<
OmitUndefined<Parameters<Component>[0]>,
"class" | "className"
>;
type TVSlots = Record<string, ClassValue> | undefined;
type TVVariants<S extends TVSlots | undefined, B extends ClassValue | undefined = undefined, EV extends TVVariants<ES> | undefined = undefined, ES extends TVSlots | undefined = undefined> =
EV extends undefined
? S extends undefined
? {}
: { [key: string]: { [key: string]: S extends TVSlots ? SlotsClassValue<S, B> | ClassValue : ClassValue; }; }
: { [K in keyof EV]: { [K2 in keyof EV[K]]: S extends TVSlots ? SlotsClassValue<S, B> | ClassValue : ClassValue; }; } | (S extends undefined ? {} : { [key: string]: { [key: string]: S extends TVSlots ? SlotsClassValue<S, B> | ClassValue : ClassValue; }; });