/** * Custom variant system for managing Tailwind CSS class variants. * * This module provides a type-safe, production-ready alternative to `tailwind-variants`, * supporting both regular variants and slot-based variants with full TypeScript inference. * * @module variants * * @example * // Regular variant usage * const button = createVariant({ * base: 'px-4 py-2 rounded', * variants: { * variant: { * primary: 'bg-blue-500 text-white', * secondary: 'bg-gray-500 text-white', * }, * size: { * sm: 'text-sm', * md: 'text-base', * lg: 'text-lg', * }, * }, * defaultVariants: { * variant: 'primary', * size: 'md', * }, * }); * * const classes = button({ variant: 'primary', size: 'lg' }); * * @example * // Slots variant usage (for multi-part components) * const modal = createVariant({ * slots: { * root: 'fixed inset-0', * panel: 'bg-white rounded-lg', * }, * variants: { * size: { * sm: { panel: 'max-w-sm' }, * md: { panel: 'max-w-md' }, * }, * }, * }); * * const { root, panel } = modal({ size: 'md' }); * const rootClasses = root(); * const panelClasses = panel({ className: 'custom-class' }); */ /** * Represents a single variant value - can be a string (class names), * an object (for slot-based variants), or a number (for numeric variants). */ type VariantValue = string | Record | number; /** * A variant definition maps variant keys (string/number) to variant values. * Used to define all possible values for a single variant property. * * @example * { * 'sm': 'text-sm px-2', * 'md': 'text-base px-4', * 'lg': 'text-lg px-6', * } */ type VariantDefinition = Record; /** * Collection of all variant definitions for a component. * Each key is a variant name, each value is a VariantDefinition. * * @example * { * size: { sm: '...', md: '...' }, * variant: { primary: '...', secondary: '...' }, * } */ type Variants = Record; /** * Defines a compound variant that applies classes when multiple variant conditions are met. * * Compound variants allow you to apply styles based on combinations of variant values. * * @example * { * variant: 'primary', * size: 'lg', * class: 'shadow-lg', // Applied when both variant is 'primary' AND size is 'lg' * } */ type CompoundVariant = { [K in keyof TVariants]?: keyof TVariants[K] | boolean; } & { class?: string; }; /** * Type-safe default variant values. * * Automatically infers the correct type based on variant definitions: * - Boolean variants (with 'true'/'false' keys) → boolean * - String/number variants → keyof the variant definition * * @example * { * size: 'md', // String variant * disabled: false, // Boolean variant * } */ type DefaultVariants = { [K in keyof TVariants]?: TVariants[K] extends Record<'true', any> & Record<'false', any> ? boolean : TVariants[K] extends Record<'true', any> ? boolean : keyof TVariants[K]; }; /** * Configuration for regular (non-slots) variants. * * Used for single-element components where all variant classes apply to one element. * * @template TVariants - The variant definitions type */ type VariantConfig = { /** Base classes that always apply */ base?: string; /** Variant definitions */ variants?: TVariants; /** Compound variants for conditional styling based on multiple variants */ compoundVariants?: Array>; /** Default values for variants */ defaultVariants?: DefaultVariants; /** Explicitly exclude slots to distinguish from SlotsConfig */ slots?: never; }; /** * Slot definitions for multi-part components. * Each key is a slot name, each value is the base classes for that slot. * * @example * { * root: 'flex items-center', * label: 'text-sm font-medium', * input: 'border rounded', * } */ type Slots = Record; /** * Slot-specific variant definitions. * * For each variant, you can specify different classes for different slots. * * @example * { * size: { * sm: { * root: 'p-2', * label: 'text-xs', * }, * md: { * root: 'p-4', * label: 'text-sm', * }, * }, * } */ type SlotsVariants = { [K in keyof TVariants]: Record>; }; /** * Compound variant for slots-based components. * * Allows applying classes to specific slots or all slots when conditions are met. * * @example * { * variant: 'primary', * size: 'lg', * class: { root: 'shadow-lg', label: 'font-bold' }, // Slot-specific * } * * @example * { * variant: 'primary', * class: 'bg-blue-500', // Applied to all slots * } */ type SlotsCompoundVariant = { [K in keyof TVariants]?: keyof TVariants[K] | boolean; } & { class?: string | Record | any; }; /** * Configuration for slots-based variants. * * Used for multi-part components where different variant classes apply to different elements. * * @template TSlots - The slots definitions type * @template TVariants - The variant definitions type */ type SlotsConfig = { /** Slot definitions - required for slots mode */ slots: TSlots; /** Slot-specific variant definitions */ variants?: SlotsVariants; /** Compound variants for slots */ compoundVariants?: Array | any>; /** Default values for variants */ defaultVariants?: DefaultVariants; /** Explicitly exclude base to distinguish from VariantConfig */ base?: never; }; /** * Extracts variant prop types from a createVariant function result. * * This utility type extracts the props that can be passed to a variant function, * excluding the `className` prop (which is handled separately). * * Works with both regular variants (returns string) and slots variants (returns object of functions). * * @template T - The createVariant function result type * * @example * const button = createVariant({ ... }); * type ButtonVariants = VariantProps; * // ButtonVariants = { variant?: 'primary' | 'secondary', size?: 'sm' | 'md' | 'lg' } */ type VariantPropsInternal = T extends (...args: any[]) => any ? T extends (props?: infer P) => any ? P extends { className?: string; } ? Omit : P extends Record ? P : never : never : T extends { [K: string]: (...args: any[]) => any; } ? { [K in keyof T]?: T[K] extends (props?: infer P) => any ? P extends { className?: string; } ? Omit : P : never; } : never; /** * Props type for variant functions. * * Automatically infers the correct prop types: * - Boolean variants (with 'true'/'false' keys) → boolean | undefined * - String/number variants → keyof variant definition | undefined * - Always includes optional className prop * * @template TVariants - The variant definitions type * @template TDefaults - The default variants type */ type VariantFunctionProps> = { [K in keyof TVariants]?: TVariants[K] extends Record<'true', any> & Record<'false', any> ? boolean | undefined : TVariants[K] extends Record<'true', any> ? boolean | undefined : K extends keyof TDefaults ? keyof TVariants[K] | undefined : keyof TVariants[K]; } & { className?: string; }; /** * Main export: Creates a type-safe variant function. * * Automatically detects whether to use regular or slots mode based on the config. * Uses function overloads for full TypeScript type inference. * * @overload * @template TSlots - The slots definitions type * @template TVariants - The variant definitions type * @param config - Slots variant configuration * @returns A function that returns slot resolvers * * @overload * @template TVariants - The variant definitions type * @param config - Regular variant configuration * @returns A function that returns a class string * * @example * // Regular variant * const button = createVariant({ * base: 'px-4 py-2', * variants: { * variant: { primary: 'bg-blue-500', secondary: 'bg-gray-500' }, * }, * }); * * @example * // Boolean variant with only 'true' key * const checkbox = createVariant({ * base: 'rounded border', * variants: { * disabled: { * true: 'opacity-50 cursor-not-allowed', * }, * }, * }); * // Usage: checkbox({ disabled: true }) → applies classes * // Usage: checkbox({ disabled: false }) → skips (key doesn't exist) * * @example * // Boolean variant with both 'true' and 'false' keys * const tab = createVariant({ * base: 'px-4 py-2', * variants: { * selected: { * true: 'bg-blue-500 text-white', * false: 'bg-gray-200 text-gray-700', * }, * }, * }); * // Usage: tab({ selected: true }) → applies 'bg-blue-500 text-white' * // Usage: tab({ selected: false }) → applies 'bg-gray-200 text-gray-700' * * @example * // Compound variant with 'false' value * const switch = createVariant({ * base: 'rounded-full', * variants: { * disabled: { * true: 'opacity-50', * }, * }, * compoundVariants: [ * { disabled: false, variant: 'outline', class: 'hover:bg-primary' }, * ], * }); * * @example * // Slots variant * const modal = createVariant({ * slots: { * root: 'fixed inset-0', * panel: 'bg-white', * }, * variants: { * size: { * sm: { panel: 'max-w-sm' }, * }, * }, * }); * * @remarks * - Production-ready with defensive programming * - Full TypeScript type inference * - Handles boolean, string, and number variants * - Supports boolean variants with 'true' only or both 'true' and 'false' keys * - Supports compound variants with boolean values (including 'false') * - Gracefully handles missing keys and undefined values * - Zero runtime dependencies (except cn utility) */ declare function createVariant(config: SlotsConfig): (props?: VariantFunctionProps>) => { [K in keyof TSlots]: (slotProps?: { className?: string; }) => string; }; declare function createVariant(config: VariantConfig): (props?: VariantFunctionProps>) => string; export { type VariantPropsInternal as VariantProps, createVariant };