This commit is contained in:
eewing
2026-02-17 14:10:16 -06:00
parent 2bca5834c5
commit cf73cd3b4c
11246 changed files with 1690552 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Tailwid Variants
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+193
View File
@@ -0,0 +1,193 @@
<p align="center">
<a href="https://tailwind-variants.org">
<img width="20%" src=".github/assets/isotipo.png" alt="tailwind-variants" />
<h1 align="center">tailwind-variants</h1>
</a>
</p>
<p align="center">
The <em>power</em> of Tailwind combined with a <em>first-class</em> variant API.<br><br>
<a href="https://www.npmjs.com/package/tailwind-variants">
<img src="https://img.shields.io/npm/dm/tailwind-variants.svg?style=flat-round" alt="npm downloads">
</a>
<a href="https://www.npmjs.com/package/tailwind-variants">
<img alt="NPM Version" src="https://badgen.net/npm/v/tailwind-variants" />
</a>
<a href="https://github.com/heroui-inc/tailwind-variants/blob/main/LICENSE">
<img src="https://img.shields.io/npm/l/tailwind-variants?style=flat" alt="License">
</a>
</p>
## Features
- First-class variant API
- Slots support
- Composition support
- Fully typed
- Framework agnostic
- Automatic conflict resolution
- Tailwindcss V4 support
## Documentation
For full documentation, visit [tailwind-variants.org](https://tailwind-variants.org)
> ❕ Note: `Tailwindcss V4` no longer supports the `config.content.transform` so we remove the `responsive variants` feature
>
> If you want to use `responsive variants`, you need to add it manually to your classname.
## Quick Start
1. Installation:
To use Tailwind Variants in your project, you can install it as a dependency:
```bash
yarn add tailwind-variants
# or
npm i tailwind-variants
# or
pnpm add tailwind-variants
```
**Optional:** If you want automatic conflict resolution, also install `tailwind-merge`:
```bash
yarn add tailwind-merge
# or
npm i tailwind-merge
# or
pnpm add tailwind-merge
```
> **⚠️ Compatibility Note:** Supports Tailwind CSS v4.x (requires `tailwind-merge` v3.x). If you use Tailwind CSS v3.x, use tailwind-variants v0.x with [tailwind-merge v2.6.0](https://github.com/dcastil/tailwind-merge/tree/v2.6.0).
> **💡 Lite mode (v3):** For smaller bundle size and faster runtime without conflict resolution, use the `/lite` import:
>
> ```js
> import {tv} from "tailwind-variants/lite";
> ```
> **⚠️ Upgrading?**
>
> - From v2 to v3: See the [v3 migration guide](./MIGRATION-V3.md)
> - From v1 to v2: See the [v2 migration guide](./MIGRATION-V2.md)
2. Usage:
```js
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>;
```
## Utility Functions
Tailwind Variants provides several utility functions for combining and merging class names:
### `cx` - Simple Concatenation
Combines class names without merging conflicting classes (similar to `clsx`):
```js
import {cx} from "tailwind-variants";
cx("text-xl", "font-bold"); // => "text-xl font-bold"
cx("px-2", "px-4"); // => "px-2 px-4" (no merging)
```
### `cn` - Merge with Default Config
> **Updated in v3.2.2** - Now returns a string directly (no function call needed)
Combines class names and merges conflicting Tailwind CSS classes using the default `tailwind-merge` config. Returns a string directly:
```js
import {cn} from "tailwind-variants";
cn("bg-red-500", "bg-blue-500"); // => "bg-blue-500"
cn("px-2", "px-4", "py-2"); // => "px-4 py-2"
```
### `cnMerge` - Merge with Custom Config
> **Available from v3.2.2**
Combines class names and merges conflicting Tailwind CSS classes with support for custom `twMerge` configuration via a second function call:
```js
import {cnMerge} from "tailwind-variants";
// Disable merging
cnMerge("px-2", "px-4")({twMerge: false}) // => "px-2 px-4"
// Enable merging explicitly
cnMerge("bg-red-500", "bg-blue-500")({twMerge: true}) // => "bg-blue-500"
// Use custom twMergeConfig
cnMerge("px-2", "px-4")({twMergeConfig: {...}}) // => merged with custom config
```
**When to use which:**
- Use `cx` when you want simple concatenation without any merging
- Use `cn` for most cases when you want automatic conflict resolution with default settings
- Use `cnMerge` when you need to customize the merge behavior (disable merging, custom config, etc.)
## Acknowledgements
- [**cva**](https://github.com/joe-bell/cva) ([Joe Bell](https://github.com/joe-bell))
This project as started as an extension of Joe's work on `cva` a great tool for generating variants for a single element with Tailwind CSS. Big shoutout to [Joe Bell](https://github.com/joe-bell) and [contributors](https://github.com/joe-bell/cva/graphs/contributors) you guys rock! 🤘 - we recommend to use `cva` if don't need any of the **Tailwind Variants** features listed [here](https://www.tailwind-variants.org/docs/comparison).
- [**Stitches**](https://stitches.dev/) ([Modulz](https://modulz.app))
The pioneers of the `variants` API movement. Inmense thanks to [Modulz](https://modulz.app) for their work on Stitches and the community around it. 🙏
## Community
We're excited to see the community adopt HeroUI, raise issues, and provide feedback. Whether it's a feature request, bug report, or a project to showcase, please get involved!
- [Discord](https://discord.gg/9b6yyZKmH4)
- [Twitter](https://twitter.com/getnextui)
- [GitHub Discussions](https://github.com/heroui-inc/tailwind-variants/discussions)
## Contributing
Contributions are always welcome!
Please follow our [contributing guidelines](./CONTRIBUTING.md).
Please adhere to this project's [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md).
## Authors
- Junior garcia ([@jrgarciadev](https://github.com/jrgaciadev))
- Tianen Pang ([@tianenpang](https://github.com/tianenpang))
## License
Licensed under the MIT License.
See [LICENSE](./LICENSE.md) for more information.
+124
View File
@@ -0,0 +1,124 @@
'use strict';
// src/utils.js
var SPACE_REGEX = /\s+/g;
var removeExtraSpaces = (str) => {
if (typeof str !== "string" || !str) return str;
return str.replace(SPACE_REGEX, " ").trim();
};
var cx = (...classnames) => {
const classList = [];
const buildClassString = (input) => {
if (!input && input !== 0 && input !== 0n) return;
if (Array.isArray(input)) {
for (let i = 0, len = input.length; i < len; i++) buildClassString(input[i]);
return;
}
const type = typeof input;
if (type === "string" || type === "number" || type === "bigint") {
if (type === "number" && input !== input) return;
classList.push(String(input));
} else if (type === "object") {
const keys = Object.keys(input);
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
if (input[key]) classList.push(key);
}
}
};
for (let i = 0, len = classnames.length; i < len; i++) {
const c = classnames[i];
if (c !== null && c !== void 0) buildClassString(c);
}
return classList.length > 0 ? removeExtraSpaces(classList.join(" ")) : void 0;
};
var falsyToString = (value) => value === false ? "false" : value === true ? "true" : value === 0 ? "0" : value;
var isEmptyObject = (obj) => {
if (!obj || typeof obj !== "object") return true;
for (const _ in obj) return false;
return true;
};
var isEqual = (obj1, obj2) => {
if (obj1 === obj2) return true;
if (!obj1 || !obj2) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let i = 0; i < keys1.length; i++) {
const key = keys1[i];
if (!keys2.includes(key)) return false;
if (obj1[key] !== obj2[key]) return false;
}
return true;
};
var isBoolean = (value) => value === true || value === false;
var joinObjects = (obj1, obj2) => {
for (const key in obj2) {
if (Object.prototype.hasOwnProperty.call(obj2, key)) {
const val2 = obj2[key];
if (key in obj1) {
obj1[key] = cx(obj1[key], val2);
} else {
obj1[key] = val2;
}
}
}
return obj1;
};
var flat = (arr, target) => {
for (let i = 0; i < arr.length; i++) {
const el = arr[i];
if (Array.isArray(el)) flat(el, target);
else if (el) target.push(el);
}
};
function flatArray(arr) {
const flattened = [];
flat(arr, flattened);
return flattened;
}
var flatMergeArrays = (...arrays) => {
const result = [];
flat(arrays, result);
const filtered = [];
for (let i = 0; i < result.length; i++) {
if (result[i]) filtered.push(result[i]);
}
return filtered;
};
var mergeObjects = (obj1, obj2) => {
const result = {};
for (const key in obj1) {
const val1 = obj1[key];
if (key in obj2) {
const val2 = obj2[key];
if (Array.isArray(val1) || Array.isArray(val2)) {
result[key] = flatMergeArrays(val2, val1);
} else if (typeof val1 === "object" && typeof val2 === "object" && val1 && val2) {
result[key] = mergeObjects(val1, val2);
} else {
result[key] = val2 + " " + val1;
}
} else {
result[key] = val1;
}
}
for (const key in obj2) {
if (!(key in obj1)) {
result[key] = obj2[key];
}
}
return result;
};
exports.cx = cx;
exports.falsyToString = falsyToString;
exports.flat = flat;
exports.flatArray = flatArray;
exports.flatMergeArrays = flatMergeArrays;
exports.isBoolean = isBoolean;
exports.isEmptyObject = isEmptyObject;
exports.isEqual = isEqual;
exports.joinObjects = joinObjects;
exports.mergeObjects = mergeObjects;
exports.removeExtraSpaces = removeExtraSpaces;
+273
View File
@@ -0,0 +1,273 @@
'use strict';
var chunk2JY7EID6_cjs = require('./chunk-2JY7EID6.cjs');
// src/config.js
var defaultConfig = {
twMerge: true,
twMergeConfig: {}
};
// src/state.js
function createState() {
let cachedTwMerge = null;
let cachedTwMergeConfig = {};
let didTwMergeConfigChange = false;
return {
get cachedTwMerge() {
return cachedTwMerge;
},
set cachedTwMerge(value) {
cachedTwMerge = value;
},
get cachedTwMergeConfig() {
return cachedTwMergeConfig;
},
set cachedTwMergeConfig(value) {
cachedTwMergeConfig = value;
},
get didTwMergeConfigChange() {
return didTwMergeConfigChange;
},
set didTwMergeConfigChange(value) {
didTwMergeConfigChange = value;
},
reset() {
cachedTwMerge = null;
cachedTwMergeConfig = {};
didTwMergeConfigChange = false;
}
};
}
var state = createState();
// src/core.js
var getTailwindVariants = (cn) => {
const tv = (options, configProp) => {
const {
extend = null,
slots: slotProps = {},
variants: variantsProps = {},
compoundVariants: compoundVariantsProps = [],
compoundSlots = [],
defaultVariants: defaultVariantsProps = {}
} = options;
const config = { ...defaultConfig, ...configProp };
const base = extend?.base ? chunk2JY7EID6_cjs.cx(extend.base, options?.base) : options?.base;
const variants = extend?.variants && !chunk2JY7EID6_cjs.isEmptyObject(extend.variants) ? chunk2JY7EID6_cjs.mergeObjects(variantsProps, extend.variants) : variantsProps;
const defaultVariants = extend?.defaultVariants && !chunk2JY7EID6_cjs.isEmptyObject(extend.defaultVariants) ? { ...extend.defaultVariants, ...defaultVariantsProps } : defaultVariantsProps;
if (!chunk2JY7EID6_cjs.isEmptyObject(config.twMergeConfig) && !chunk2JY7EID6_cjs.isEqual(config.twMergeConfig, state.cachedTwMergeConfig)) {
state.didTwMergeConfigChange = true;
state.cachedTwMergeConfig = config.twMergeConfig;
}
const isExtendedSlotsEmpty = chunk2JY7EID6_cjs.isEmptyObject(extend?.slots);
const componentSlots = !chunk2JY7EID6_cjs.isEmptyObject(slotProps) ? {
// add "base" to the slots object
base: chunk2JY7EID6_cjs.cx(options?.base, isExtendedSlotsEmpty && extend?.base),
...slotProps
} : {};
const slots = isExtendedSlotsEmpty ? componentSlots : chunk2JY7EID6_cjs.joinObjects(
{ ...extend?.slots },
chunk2JY7EID6_cjs.isEmptyObject(componentSlots) ? { base: options?.base } : componentSlots
);
const compoundVariants = chunk2JY7EID6_cjs.isEmptyObject(extend?.compoundVariants) ? compoundVariantsProps : chunk2JY7EID6_cjs.flatMergeArrays(extend?.compoundVariants, compoundVariantsProps);
const component = (props) => {
if (chunk2JY7EID6_cjs.isEmptyObject(variants) && chunk2JY7EID6_cjs.isEmptyObject(slotProps) && isExtendedSlotsEmpty) {
return cn(base, props?.class, props?.className)(config);
}
if (compoundVariants && !Array.isArray(compoundVariants)) {
throw new TypeError(
`The "compoundVariants" prop must be an array. Received: ${typeof compoundVariants}`
);
}
if (compoundSlots && !Array.isArray(compoundSlots)) {
throw new TypeError(
`The "compoundSlots" prop must be an array. Received: ${typeof compoundSlots}`
);
}
const getVariantValue = (variant, vrs = variants, _slotKey = null, slotProps2 = null) => {
const variantObj = vrs[variant];
if (!variantObj || chunk2JY7EID6_cjs.isEmptyObject(variantObj)) {
return null;
}
const variantProp = slotProps2?.[variant] ?? props?.[variant];
if (variantProp === null) return null;
const variantKey = chunk2JY7EID6_cjs.falsyToString(variantProp);
if (typeof variantKey === "object") {
return null;
}
const defaultVariantProp = defaultVariants?.[variant];
const key = variantKey != null ? variantKey : chunk2JY7EID6_cjs.falsyToString(defaultVariantProp);
const value = variantObj[key || "false"];
return value;
};
const getVariantClassNames = () => {
if (!variants) return null;
const keys = Object.keys(variants);
const result = [];
for (let i = 0; i < keys.length; i++) {
const value = getVariantValue(keys[i], variants);
if (value) result.push(value);
}
return result;
};
const getVariantClassNamesBySlotKey = (slotKey, slotProps2) => {
if (!variants || typeof variants !== "object") return null;
const result = [];
for (const variant in variants) {
const variantValue = getVariantValue(variant, variants, slotKey, slotProps2);
const value = slotKey === "base" && typeof variantValue === "string" ? variantValue : variantValue && variantValue[slotKey];
if (value) result.push(value);
}
return result;
};
const propsWithoutUndefined = {};
for (const prop in props) {
const value = props[prop];
if (value !== void 0) propsWithoutUndefined[prop] = value;
}
const getCompleteProps = (key, slotProps2) => {
const initialProp = typeof props?.[key] === "object" ? {
[key]: props[key]?.initial
} : {};
return {
...defaultVariants,
...propsWithoutUndefined,
...initialProp,
...slotProps2
};
};
const getCompoundVariantsValue = (cv = [], slotProps2) => {
const result = [];
const cvLength = cv.length;
for (let i = 0; i < cvLength; i++) {
const { class: tvClass, className: tvClassName, ...compoundVariantOptions } = cv[i];
let isValid = true;
const completeProps = getCompleteProps(null, slotProps2);
for (const key in compoundVariantOptions) {
const value = compoundVariantOptions[key];
const completePropsValue = completeProps[key];
if (Array.isArray(value)) {
if (!value.includes(completePropsValue)) {
isValid = false;
break;
}
} else {
if ((value == null || value === false) && (completePropsValue == null || completePropsValue === false))
continue;
if (completePropsValue !== value) {
isValid = false;
break;
}
}
}
if (isValid) {
if (tvClass) result.push(tvClass);
if (tvClassName) result.push(tvClassName);
}
}
return result;
};
const getCompoundVariantClassNamesBySlot = (slotProps2) => {
const compoundClassNames = getCompoundVariantsValue(compoundVariants, slotProps2);
if (!Array.isArray(compoundClassNames)) return compoundClassNames;
const result = {};
const cnFn = cn;
for (let i = 0; i < compoundClassNames.length; i++) {
const className = compoundClassNames[i];
if (typeof className === "string") {
result.base = cnFn(result.base, className)(config);
} else if (typeof className === "object") {
for (const slot in className) {
result[slot] = cnFn(result[slot], className[slot])(config);
}
}
}
return result;
};
const getCompoundSlotClassNameBySlot = (slotProps2) => {
if (compoundSlots.length < 1) return null;
const result = {};
const completeProps = getCompleteProps(null, slotProps2);
for (let i = 0; i < compoundSlots.length; i++) {
const {
slots: slots2 = [],
class: slotClass,
className: slotClassName,
...slotVariants
} = compoundSlots[i];
if (!chunk2JY7EID6_cjs.isEmptyObject(slotVariants)) {
let isValid = true;
for (const key in slotVariants) {
const completePropsValue = completeProps[key];
const slotVariantValue = slotVariants[key];
if (completePropsValue === void 0 || (Array.isArray(slotVariantValue) ? !slotVariantValue.includes(completePropsValue) : slotVariantValue !== completePropsValue)) {
isValid = false;
break;
}
}
if (!isValid) continue;
}
for (let j = 0; j < slots2.length; j++) {
const slotName = slots2[j];
if (!result[slotName]) result[slotName] = [];
result[slotName].push([slotClass, slotClassName]);
}
}
return result;
};
if (!chunk2JY7EID6_cjs.isEmptyObject(slotProps) || !isExtendedSlotsEmpty) {
const slotsFns = {};
if (typeof slots === "object" && !chunk2JY7EID6_cjs.isEmptyObject(slots)) {
const cnFn = cn;
for (const slotKey in slots) {
slotsFns[slotKey] = (slotProps2) => {
const compoundVariantClasses = getCompoundVariantClassNamesBySlot(slotProps2);
const compoundSlotClasses = getCompoundSlotClassNameBySlot(slotProps2);
return cnFn(
slots[slotKey],
getVariantClassNamesBySlotKey(slotKey, slotProps2),
compoundVariantClasses ? compoundVariantClasses[slotKey] : void 0,
compoundSlotClasses ? compoundSlotClasses[slotKey] : void 0,
slotProps2?.class,
slotProps2?.className
)(config);
};
}
}
return slotsFns;
}
return cn(
base,
getVariantClassNames(),
getCompoundVariantsValue(compoundVariants),
props?.class,
props?.className
)(config);
};
const getVariantKeys = () => {
if (!variants || typeof variants !== "object") return;
return Object.keys(variants);
};
component.variantKeys = getVariantKeys();
component.extend = extend;
component.base = base;
component.slots = slots;
component.variants = variants;
component.defaultVariants = defaultVariants;
component.compoundSlots = compoundSlots;
component.compoundVariants = compoundVariants;
return component;
};
const createTV = (configProp) => {
return (options, config) => tv(options, config ? chunk2JY7EID6_cjs.mergeObjects(configProp, config) : configProp);
};
return {
tv,
createTV
};
};
exports.defaultConfig = defaultConfig;
exports.getTailwindVariants = getTailwindVariants;
exports.state = state;
+112
View File
@@ -0,0 +1,112 @@
// src/utils.js
var SPACE_REGEX = /\s+/g;
var removeExtraSpaces = (str) => {
if (typeof str !== "string" || !str) return str;
return str.replace(SPACE_REGEX, " ").trim();
};
var cx = (...classnames) => {
const classList = [];
const buildClassString = (input) => {
if (!input && input !== 0 && input !== 0n) return;
if (Array.isArray(input)) {
for (let i = 0, len = input.length; i < len; i++) buildClassString(input[i]);
return;
}
const type = typeof input;
if (type === "string" || type === "number" || type === "bigint") {
if (type === "number" && input !== input) return;
classList.push(String(input));
} else if (type === "object") {
const keys = Object.keys(input);
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
if (input[key]) classList.push(key);
}
}
};
for (let i = 0, len = classnames.length; i < len; i++) {
const c = classnames[i];
if (c !== null && c !== void 0) buildClassString(c);
}
return classList.length > 0 ? removeExtraSpaces(classList.join(" ")) : void 0;
};
var falsyToString = (value) => value === false ? "false" : value === true ? "true" : value === 0 ? "0" : value;
var isEmptyObject = (obj) => {
if (!obj || typeof obj !== "object") return true;
for (const _ in obj) return false;
return true;
};
var isEqual = (obj1, obj2) => {
if (obj1 === obj2) return true;
if (!obj1 || !obj2) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let i = 0; i < keys1.length; i++) {
const key = keys1[i];
if (!keys2.includes(key)) return false;
if (obj1[key] !== obj2[key]) return false;
}
return true;
};
var isBoolean = (value) => value === true || value === false;
var joinObjects = (obj1, obj2) => {
for (const key in obj2) {
if (Object.prototype.hasOwnProperty.call(obj2, key)) {
const val2 = obj2[key];
if (key in obj1) {
obj1[key] = cx(obj1[key], val2);
} else {
obj1[key] = val2;
}
}
}
return obj1;
};
var flat = (arr, target) => {
for (let i = 0; i < arr.length; i++) {
const el = arr[i];
if (Array.isArray(el)) flat(el, target);
else if (el) target.push(el);
}
};
function flatArray(arr) {
const flattened = [];
flat(arr, flattened);
return flattened;
}
var flatMergeArrays = (...arrays) => {
const result = [];
flat(arrays, result);
const filtered = [];
for (let i = 0; i < result.length; i++) {
if (result[i]) filtered.push(result[i]);
}
return filtered;
};
var mergeObjects = (obj1, obj2) => {
const result = {};
for (const key in obj1) {
const val1 = obj1[key];
if (key in obj2) {
const val2 = obj2[key];
if (Array.isArray(val1) || Array.isArray(val2)) {
result[key] = flatMergeArrays(val2, val1);
} else if (typeof val1 === "object" && typeof val2 === "object" && val1 && val2) {
result[key] = mergeObjects(val1, val2);
} else {
result[key] = val2 + " " + val1;
}
} else {
result[key] = val1;
}
}
for (const key in obj2) {
if (!(key in obj1)) {
result[key] = obj2[key];
}
}
return result;
};
export { cx, falsyToString, flat, flatArray, flatMergeArrays, isBoolean, isEmptyObject, isEqual, joinObjects, mergeObjects, removeExtraSpaces };
+269
View File
@@ -0,0 +1,269 @@
import { cx, isEmptyObject, mergeObjects, isEqual, joinObjects, flatMergeArrays, falsyToString } from './chunk-LQJYWU4O.js';
// src/config.js
var defaultConfig = {
twMerge: true,
twMergeConfig: {}
};
// src/state.js
function createState() {
let cachedTwMerge = null;
let cachedTwMergeConfig = {};
let didTwMergeConfigChange = false;
return {
get cachedTwMerge() {
return cachedTwMerge;
},
set cachedTwMerge(value) {
cachedTwMerge = value;
},
get cachedTwMergeConfig() {
return cachedTwMergeConfig;
},
set cachedTwMergeConfig(value) {
cachedTwMergeConfig = value;
},
get didTwMergeConfigChange() {
return didTwMergeConfigChange;
},
set didTwMergeConfigChange(value) {
didTwMergeConfigChange = value;
},
reset() {
cachedTwMerge = null;
cachedTwMergeConfig = {};
didTwMergeConfigChange = false;
}
};
}
var state = createState();
// src/core.js
var getTailwindVariants = (cn) => {
const tv = (options, configProp) => {
const {
extend = null,
slots: slotProps = {},
variants: variantsProps = {},
compoundVariants: compoundVariantsProps = [],
compoundSlots = [],
defaultVariants: defaultVariantsProps = {}
} = options;
const config = { ...defaultConfig, ...configProp };
const base = extend?.base ? cx(extend.base, options?.base) : options?.base;
const variants = extend?.variants && !isEmptyObject(extend.variants) ? mergeObjects(variantsProps, extend.variants) : variantsProps;
const defaultVariants = extend?.defaultVariants && !isEmptyObject(extend.defaultVariants) ? { ...extend.defaultVariants, ...defaultVariantsProps } : defaultVariantsProps;
if (!isEmptyObject(config.twMergeConfig) && !isEqual(config.twMergeConfig, state.cachedTwMergeConfig)) {
state.didTwMergeConfigChange = true;
state.cachedTwMergeConfig = config.twMergeConfig;
}
const isExtendedSlotsEmpty = isEmptyObject(extend?.slots);
const componentSlots = !isEmptyObject(slotProps) ? {
// add "base" to the slots object
base: cx(options?.base, isExtendedSlotsEmpty && extend?.base),
...slotProps
} : {};
const slots = isExtendedSlotsEmpty ? componentSlots : joinObjects(
{ ...extend?.slots },
isEmptyObject(componentSlots) ? { base: options?.base } : componentSlots
);
const compoundVariants = isEmptyObject(extend?.compoundVariants) ? compoundVariantsProps : flatMergeArrays(extend?.compoundVariants, compoundVariantsProps);
const component = (props) => {
if (isEmptyObject(variants) && isEmptyObject(slotProps) && isExtendedSlotsEmpty) {
return cn(base, props?.class, props?.className)(config);
}
if (compoundVariants && !Array.isArray(compoundVariants)) {
throw new TypeError(
`The "compoundVariants" prop must be an array. Received: ${typeof compoundVariants}`
);
}
if (compoundSlots && !Array.isArray(compoundSlots)) {
throw new TypeError(
`The "compoundSlots" prop must be an array. Received: ${typeof compoundSlots}`
);
}
const getVariantValue = (variant, vrs = variants, _slotKey = null, slotProps2 = null) => {
const variantObj = vrs[variant];
if (!variantObj || isEmptyObject(variantObj)) {
return null;
}
const variantProp = slotProps2?.[variant] ?? props?.[variant];
if (variantProp === null) return null;
const variantKey = falsyToString(variantProp);
if (typeof variantKey === "object") {
return null;
}
const defaultVariantProp = defaultVariants?.[variant];
const key = variantKey != null ? variantKey : falsyToString(defaultVariantProp);
const value = variantObj[key || "false"];
return value;
};
const getVariantClassNames = () => {
if (!variants) return null;
const keys = Object.keys(variants);
const result = [];
for (let i = 0; i < keys.length; i++) {
const value = getVariantValue(keys[i], variants);
if (value) result.push(value);
}
return result;
};
const getVariantClassNamesBySlotKey = (slotKey, slotProps2) => {
if (!variants || typeof variants !== "object") return null;
const result = [];
for (const variant in variants) {
const variantValue = getVariantValue(variant, variants, slotKey, slotProps2);
const value = slotKey === "base" && typeof variantValue === "string" ? variantValue : variantValue && variantValue[slotKey];
if (value) result.push(value);
}
return result;
};
const propsWithoutUndefined = {};
for (const prop in props) {
const value = props[prop];
if (value !== void 0) propsWithoutUndefined[prop] = value;
}
const getCompleteProps = (key, slotProps2) => {
const initialProp = typeof props?.[key] === "object" ? {
[key]: props[key]?.initial
} : {};
return {
...defaultVariants,
...propsWithoutUndefined,
...initialProp,
...slotProps2
};
};
const getCompoundVariantsValue = (cv = [], slotProps2) => {
const result = [];
const cvLength = cv.length;
for (let i = 0; i < cvLength; i++) {
const { class: tvClass, className: tvClassName, ...compoundVariantOptions } = cv[i];
let isValid = true;
const completeProps = getCompleteProps(null, slotProps2);
for (const key in compoundVariantOptions) {
const value = compoundVariantOptions[key];
const completePropsValue = completeProps[key];
if (Array.isArray(value)) {
if (!value.includes(completePropsValue)) {
isValid = false;
break;
}
} else {
if ((value == null || value === false) && (completePropsValue == null || completePropsValue === false))
continue;
if (completePropsValue !== value) {
isValid = false;
break;
}
}
}
if (isValid) {
if (tvClass) result.push(tvClass);
if (tvClassName) result.push(tvClassName);
}
}
return result;
};
const getCompoundVariantClassNamesBySlot = (slotProps2) => {
const compoundClassNames = getCompoundVariantsValue(compoundVariants, slotProps2);
if (!Array.isArray(compoundClassNames)) return compoundClassNames;
const result = {};
const cnFn = cn;
for (let i = 0; i < compoundClassNames.length; i++) {
const className = compoundClassNames[i];
if (typeof className === "string") {
result.base = cnFn(result.base, className)(config);
} else if (typeof className === "object") {
for (const slot in className) {
result[slot] = cnFn(result[slot], className[slot])(config);
}
}
}
return result;
};
const getCompoundSlotClassNameBySlot = (slotProps2) => {
if (compoundSlots.length < 1) return null;
const result = {};
const completeProps = getCompleteProps(null, slotProps2);
for (let i = 0; i < compoundSlots.length; i++) {
const {
slots: slots2 = [],
class: slotClass,
className: slotClassName,
...slotVariants
} = compoundSlots[i];
if (!isEmptyObject(slotVariants)) {
let isValid = true;
for (const key in slotVariants) {
const completePropsValue = completeProps[key];
const slotVariantValue = slotVariants[key];
if (completePropsValue === void 0 || (Array.isArray(slotVariantValue) ? !slotVariantValue.includes(completePropsValue) : slotVariantValue !== completePropsValue)) {
isValid = false;
break;
}
}
if (!isValid) continue;
}
for (let j = 0; j < slots2.length; j++) {
const slotName = slots2[j];
if (!result[slotName]) result[slotName] = [];
result[slotName].push([slotClass, slotClassName]);
}
}
return result;
};
if (!isEmptyObject(slotProps) || !isExtendedSlotsEmpty) {
const slotsFns = {};
if (typeof slots === "object" && !isEmptyObject(slots)) {
const cnFn = cn;
for (const slotKey in slots) {
slotsFns[slotKey] = (slotProps2) => {
const compoundVariantClasses = getCompoundVariantClassNamesBySlot(slotProps2);
const compoundSlotClasses = getCompoundSlotClassNameBySlot(slotProps2);
return cnFn(
slots[slotKey],
getVariantClassNamesBySlotKey(slotKey, slotProps2),
compoundVariantClasses ? compoundVariantClasses[slotKey] : void 0,
compoundSlotClasses ? compoundSlotClasses[slotKey] : void 0,
slotProps2?.class,
slotProps2?.className
)(config);
};
}
}
return slotsFns;
}
return cn(
base,
getVariantClassNames(),
getCompoundVariantsValue(compoundVariants),
props?.class,
props?.className
)(config);
};
const getVariantKeys = () => {
if (!variants || typeof variants !== "object") return;
return Object.keys(variants);
};
component.variantKeys = getVariantKeys();
component.extend = extend;
component.base = base;
component.slots = slots;
component.variants = variants;
component.defaultVariants = defaultVariants;
component.compoundSlots = compoundSlots;
component.compoundVariants = compoundVariants;
return component;
};
const createTV = (configProp) => {
return (options, config) => tv(options, config ? mergeObjects(configProp, config) : configProp);
};
return {
tv,
createTV
};
};
export { defaultConfig, getTailwindVariants, state };
+23
View File
@@ -0,0 +1,23 @@
import type {extendTailwindMerge} from "tailwind-merge";
type MergeConfig = Parameters<typeof extendTailwindMerge>[0];
type LegacyMergeConfig = Extract<MergeConfig, {extend?: unknown}>["extend"];
export type TWMergeConfig = MergeConfig & LegacyMergeConfig;
export type TWMConfig = {
/**
* Whether to merge the class names with `tailwind-merge` library.
* It's avoid to have duplicate tailwind classes. (Recommended)
* @see https://github.com/dcastil/tailwind-merge/blob/v2.2.0/README.md
* @default true
*/
twMerge?: boolean;
/**
* The config object for `tailwind-merge` library.
* @see https://github.com/dcastil/tailwind-merge/blob/v2.2.0/docs/configuration.md
*/
twMergeConfig?: TWMergeConfig;
};
export type TVConfig = TWMConfig;
+49
View File
@@ -0,0 +1,49 @@
'use strict';
var chunk52Z2HSI4_cjs = require('./chunk-52Z2HSI4.cjs');
var chunk2JY7EID6_cjs = require('./chunk-2JY7EID6.cjs');
var tailwindMerge = require('tailwind-merge');
var createTwMerge = (cachedTwMergeConfig) => {
return chunk2JY7EID6_cjs.isEmptyObject(cachedTwMergeConfig) ? tailwindMerge.twMerge : tailwindMerge.extendTailwindMerge({
...cachedTwMergeConfig,
extend: {
theme: cachedTwMergeConfig.theme,
classGroups: cachedTwMergeConfig.classGroups,
conflictingClassGroupModifiers: cachedTwMergeConfig.conflictingClassGroupModifiers,
conflictingClassGroups: cachedTwMergeConfig.conflictingClassGroups,
...cachedTwMergeConfig.extend
}
});
};
var executeMerge = (classnames, config) => {
const base = chunk2JY7EID6_cjs.cx(classnames);
if (!base || !(config?.twMerge ?? true)) return base;
if (!chunk52Z2HSI4_cjs.state.cachedTwMerge || chunk52Z2HSI4_cjs.state.didTwMergeConfigChange) {
chunk52Z2HSI4_cjs.state.didTwMergeConfigChange = false;
chunk52Z2HSI4_cjs.state.cachedTwMerge = createTwMerge(chunk52Z2HSI4_cjs.state.cachedTwMergeConfig);
}
return chunk52Z2HSI4_cjs.state.cachedTwMerge(base) || void 0;
};
var cn = (...classnames) => {
return executeMerge(classnames, {});
};
var cnMerge = (...classnames) => {
return (config) => executeMerge(classnames, config);
};
// src/index.js
var { createTV, tv } = chunk52Z2HSI4_cjs.getTailwindVariants(cnMerge);
Object.defineProperty(exports, "defaultConfig", {
enumerable: true,
get: function () { return chunk52Z2HSI4_cjs.defaultConfig; }
});
Object.defineProperty(exports, "cx", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.cx; }
});
exports.cn = cn;
exports.cnMerge = cnMerge;
exports.createTV = createTV;
exports.tv = tv;
+139
View File
@@ -0,0 +1,139 @@
import type {TVConfig, TWMConfig, TWMergeConfig} from "./config.d.ts";
import type {CnOptions, CnReturn, TV} from "./types.d.ts";
export type * from "./types.d.ts";
/**
* Combines class names into a single string. Similar to `clsx` - simple concatenation without merging conflicting classes.
* @param classes - Class names to combine. Accepts strings, numbers, arrays, objects, and nested structures.
* @returns A space-separated string of class names, or `undefined` if no valid classes are provided
* @example
* ```ts
* // Simple concatenation
* cx('text-xl', 'font-bold') // => 'text-xl font-bold'
*
* // Handles arrays and objects
* cx(['px-4', 'py-2'], { 'bg-blue-500': true, 'text-white': false }) // => 'px-4 py-2 bg-blue-500'
*
* // Ignores falsy values (except 0)
* cx('text-xl', false && 'font-bold', null, undefined) // => 'text-xl'
* ```
*/
export declare const cx: <T extends CnOptions>(...classes: T) => CnReturn;
/**
* Combines class names and merges conflicting Tailwind CSS classes using `tailwind-merge`.
* Uses default twMerge config. For custom config, use `cnMerge` instead.
* @param classes - Class names to combine (strings, arrays, objects, etc.)
* @returns A merged class string, or `undefined` if no valid classes are provided
* @example
* ```ts
* // Simple usage with default twMerge config
* cn('bg-red-500', 'bg-blue-500') // => 'bg-blue-500'
* cn('px-2', 'px-4', 'py-2') // => 'px-4 py-2'
*
* // For custom twMerge config, use cnMerge instead
* cnMerge('px-2', 'px-4')({twMerge: false}) // => 'px-2 px-4'
* ```
*/
export declare const cn: <T extends CnOptions>(...classes: T) => CnReturn;
/**
* Combines class names and merges conflicting Tailwind CSS classes using `tailwind-merge`.
* Supports custom twMerge config via the second function call.
* @param classes - Class names to combine (strings, arrays, objects, etc.)
* @returns A function that accepts optional twMerge config and returns the merged class string
* @example
* ```ts
* // With custom config
* cnMerge('bg-red-500', 'bg-blue-500')({twMerge: true}) // => 'bg-blue-500'
* cnMerge('px-2', 'px-4')({twMerge: false}) // => 'px-2 px-4'
*
* // With twMergeConfig
* cnMerge('px-2', 'px-4')({twMergeConfig: {...}}) // => merged with custom config
* ```
*/
export declare const cnMerge: <T extends CnOptions>(
...classes: T
) => (config?: TWMConfig) => CnReturn;
/**
* Creates a variant-aware component function with Tailwind CSS classes.
* Supports variants, slots, compound variants, and component composition.
* @example
* ```ts
* const button = tv({
* base: "font-medium rounded-full",
* variants: {
* color: {
* primary: "bg-blue-500 text-white",
* secondary: "bg-purple-500 text-white",
* },
* size: {
* sm: "text-sm px-3 py-1",
* md: "text-base px-4 py-2",
* },
* },
* defaultVariants: {
* color: "primary",
* size: "md",
* },
* });
*
* button({ color: "secondary", size: "sm" }) // => 'font-medium rounded-full bg-purple-500 text-white text-sm px-3 py-1'
* ```
* @see https://www.tailwind-variants.org/docs/getting-started
*/
export declare const tv: TV;
/**
* Creates a configured `tv` instance with custom default configuration.
* Useful when you want to set default `twMerge` or `twMergeConfig` options for all components.
* @param config - Configuration object with default settings for `twMerge` and `twMergeConfig`
* @returns A configured `tv` function that uses the provided defaults
* @example
* ```ts
* // Create a tv instance with twMerge disabled by default
* const tv = createTV({ twMerge: false });
*
* const button = tv({
* base: "px-4 py-2",
* variants: {
* color: {
* primary: "bg-blue-500",
* },
* },
* });
*
* // Can still override config per component
* const buttonWithMerge = tv(
* {
* base: "px-4 py-2",
* variants: { color: { primary: "bg-blue-500" } },
* },
* { twMerge: true }
* );
* ```
*/
export declare function createTV(config: TVConfig): TV;
/**
* Default configuration object for tailwind-variants.
* Can be modified to set global defaults for all components.
* @example
* ```ts
* import { defaultConfig } from "tailwind-variants";
*
* defaultConfig.twMergeConfig = {
* extend: {
* theme: {
* spacing: ["medium", "large"],
* },
* },
* };
* ```
*/
export declare const defaultConfig: TVConfig;
// types
export type {TVConfig, TWMConfig, TWMergeConfig};
+38
View File
@@ -0,0 +1,38 @@
import { getTailwindVariants, state } from './chunk-RZF76H2U.js';
export { defaultConfig } from './chunk-RZF76H2U.js';
import { cx, isEmptyObject } from './chunk-LQJYWU4O.js';
export { cx } from './chunk-LQJYWU4O.js';
import { twMerge, extendTailwindMerge } from 'tailwind-merge';
var createTwMerge = (cachedTwMergeConfig) => {
return isEmptyObject(cachedTwMergeConfig) ? twMerge : extendTailwindMerge({
...cachedTwMergeConfig,
extend: {
theme: cachedTwMergeConfig.theme,
classGroups: cachedTwMergeConfig.classGroups,
conflictingClassGroupModifiers: cachedTwMergeConfig.conflictingClassGroupModifiers,
conflictingClassGroups: cachedTwMergeConfig.conflictingClassGroups,
...cachedTwMergeConfig.extend
}
});
};
var executeMerge = (classnames, config) => {
const base = cx(classnames);
if (!base || !(config?.twMerge ?? true)) return base;
if (!state.cachedTwMerge || state.didTwMergeConfigChange) {
state.didTwMergeConfigChange = false;
state.cachedTwMerge = createTwMerge(state.cachedTwMergeConfig);
}
return state.cachedTwMerge(base) || void 0;
};
var cn = (...classnames) => {
return executeMerge(classnames, {});
};
var cnMerge = (...classnames) => {
return (config) => executeMerge(classnames, config);
};
// src/index.js
var { createTV, tv } = getTailwindVariants(cnMerge);
export { cn, cnMerge, createTV, tv };
+26
View File
@@ -0,0 +1,26 @@
'use strict';
var chunk52Z2HSI4_cjs = require('./chunk-52Z2HSI4.cjs');
var chunk2JY7EID6_cjs = require('./chunk-2JY7EID6.cjs');
// src/lite.js
var cnAdapter = (...classnames) => {
return (_config) => {
const base = chunk2JY7EID6_cjs.cx(classnames);
return base || void 0;
};
};
var { createTV, tv } = chunk52Z2HSI4_cjs.getTailwindVariants(cnAdapter);
Object.defineProperty(exports, "defaultConfig", {
enumerable: true,
get: function () { return chunk52Z2HSI4_cjs.defaultConfig; }
});
Object.defineProperty(exports, "cx", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.cx; }
});
exports.cn = cnAdapter;
exports.cnAdapter = cnAdapter;
exports.createTV = createTV;
exports.tv = tv;
+13
View File
@@ -0,0 +1,13 @@
import type {CnOptions, CnReturn, TVLite} from "./types.d.ts";
export type * from "./types.d.ts";
// util function
export declare const cx: <T extends CnOptions>(...classes: T) => CnReturn;
export declare const cn: <T extends CnOptions>(...classes: T) => (config?: any) => CnReturn;
// main function
export declare const tv: TVLite;
export declare function createTV(): TVLite;
+15
View File
@@ -0,0 +1,15 @@
import { getTailwindVariants } from './chunk-RZF76H2U.js';
export { defaultConfig } from './chunk-RZF76H2U.js';
import { cx } from './chunk-LQJYWU4O.js';
export { cx } from './chunk-LQJYWU4O.js';
// src/lite.js
var cnAdapter = (...classnames) => {
return (_config) => {
const base = cx(classnames);
return base || void 0;
};
};
var { createTV, tv } = getTailwindVariants(cnAdapter);
export { cnAdapter as cn, cnAdapter, createTV, tv };
+337
View File
@@ -0,0 +1,337 @@
import type {ClassNameValue as ClassValue} from "tailwind-merge";
import type {TVConfig} from "./config.d.ts";
/**
* ----------------------------------------
* Base Types
* ----------------------------------------
*/
export type {ClassValue};
export type ClassProp<V extends unknown = ClassValue> =
| {class?: V; className?: never}
| {class?: never; className?: V};
type TVBaseName = "base";
type TVScreens = "initial";
type TVSlots = Record<string, ClassValue> | undefined;
/**
* ----------------------------------------------------------------------
* Utils
* ----------------------------------------------------------------------
*/
export type OmitUndefined<T> = T extends undefined ? never : T;
export type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
type CnClassValue =
| string
| number
| bigint
| boolean
| null
| undefined
| CnClassDictionary
| CnClassArray;
interface CnClassDictionary {
[key: string]: any;
}
interface CnClassArray extends Array<CnClassValue> {}
export type CnOptions = CnClassValue[];
export type CnReturn = string | undefined;
// compare if the value is true or array of values
export type isTrueOrArray<T> = T extends true | unknown[] ? true : false;
export type WithInitialScreen<T extends Array<string>> = ["initial", ...T];
/**
* ----------------------------------------------------------------------
* TV Types
* ----------------------------------------------------------------------
*/
type TVSlotsWithBase<S extends TVSlots, B extends ClassValue> = B extends undefined
? keyof S
: keyof S | TVBaseName;
type SlotsClassValue<S extends TVSlots, B extends ClassValue> = {
[K in TVSlotsWithBase<S, B>]?: ClassValue;
};
type TVVariantsDefault<S extends TVSlots, B extends ClassValue> = S extends undefined
? {}
: {
[key: string]: {
[key: string]: S extends TVSlots ? SlotsClassValue<S, B> | ClassValue : ClassValue;
};
};
export 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
? TVVariantsDefault<S, B>
:
| {
[K in keyof EV]: {
[K2 in keyof EV[K]]: S extends TVSlots
? SlotsClassValue<S, B> | ClassValue
: ClassValue;
};
}
| TVVariantsDefault<S, B>;
export type TVCompoundVariants<
V extends TVVariants<S>,
S extends TVSlots,
B extends ClassValue,
EV extends TVVariants<ES>,
ES extends TVSlots,
> = Array<
{
[K in keyof V | keyof EV]?:
| (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
| (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never)
| (K extends keyof V ? StringToBoolean<keyof V[K]>[] : never);
} & ClassProp<SlotsClassValue<S, B> | ClassValue>
>;
export type TVCompoundSlots<
V extends TVVariants<S>,
S extends TVSlots,
B extends ClassValue,
> = Array<
V extends undefined
? {
slots: Array<TVSlotsWithBase<S, B>>;
} & ClassProp
: {
slots: Array<TVSlotsWithBase<S, B>>;
} & {
[K in keyof V]?: StringToBoolean<keyof V[K]> | StringToBoolean<keyof V[K]>[];
} & ClassProp
>;
export type TVDefaultVariants<
V extends TVVariants<S>,
S extends TVSlots,
EV extends TVVariants<ES>,
ES extends TVSlots,
> = {
[K in keyof V | keyof EV]?:
| (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
| (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never);
};
export type TVScreenPropsValue<V extends TVVariants<S>, S extends TVSlots, K extends keyof V> = {
[Screen in TVScreens]?: StringToBoolean<keyof V[K]>;
};
export type TVProps<
V extends TVVariants<S>,
S extends TVSlots,
EV extends TVVariants<ES>,
ES extends TVSlots,
> = EV extends undefined
? V extends undefined
? ClassProp<ClassValue>
: {
[K in keyof V]?: StringToBoolean<keyof V[K]> | undefined;
} & ClassProp<ClassValue>
: V extends undefined
? {
[K in keyof EV]?: StringToBoolean<keyof EV[K]> | undefined;
} & ClassProp<ClassValue>
: {
[K in keyof V | keyof EV]?:
| (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
| (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never)
| undefined;
} & ClassProp<ClassValue>;
export type TVVariantKeys<V extends TVVariants<S>, S extends TVSlots> = V extends Object
? Array<keyof V>
: undefined;
export type TVReturnProps<
V extends TVVariants<S>,
S extends TVSlots,
B extends ClassValue,
EV extends TVVariants<ES>,
ES extends TVSlots,
// @ts-expect-error
E extends TVReturnType = undefined,
> = {
extend: E;
base: B;
slots: S;
variants: V;
defaultVariants: TVDefaultVariants<V, S, EV, ES>;
compoundVariants: TVCompoundVariants<V, S, B, EV, ES>;
compoundSlots: TVCompoundSlots<V, S, B>;
variantKeys: TVVariantKeys<V, S>;
};
type HasSlots<S extends TVSlots, ES extends TVSlots> = S extends undefined
? ES extends undefined
? false
: true
: true;
export type TVReturnType<
V extends TVVariants<S>,
S extends TVSlots,
B extends ClassValue,
EV extends TVVariants<ES>,
ES extends TVSlots,
// @ts-expect-error
E extends TVReturnType = undefined,
> = {
(props?: TVProps<V, S, EV, ES>): HasSlots<S, ES> extends true
? {
[K in keyof (ES extends undefined ? {} : ES)]: (
slotProps?: TVProps<V, S, EV, ES>,
) => string;
} & {
[K in keyof (S extends undefined ? {} : S)]: (slotProps?: TVProps<V, S, EV, ES>) => string;
} & {
[K in TVSlotsWithBase<{}, B>]: (slotProps?: TVProps<V, S, EV, ES>) => string;
}
: string;
} & TVReturnProps<V, S, B, EV, ES, E>;
export type 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,
// @ts-expect-error
E extends TVReturnType = TVReturnType<
V,
S,
B,
// @ts-expect-error
EV extends undefined ? {} : EV,
// @ts-expect-error
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 allows for easy composition of components.
* @see https://www.tailwind-variants.org/docs/composing-components
*/
extend?: E;
/**
* Base allows you to set a base class for a component.
*/
base?: B;
/**
* Slots allow you to separate a component into multiple parts.
* @see https://www.tailwind-variants.org/docs/slots
*/
slots?: S;
/**
* Variants allow you to create multiple versions of the same component.
* @see https://www.tailwind-variants.org/docs/variants#adding-variants
*/
variants?: V;
/**
* Compound variants allow you to apply classes to multiple variants at once.
* @see https://www.tailwind-variants.org/docs/variants#compound-variants
*/
compoundVariants?: CV;
/**
* Compound slots allow you to apply classes to multiple slots at once.
*/
compoundSlots?: TVCompoundSlots<V, S, B>;
/**
* Default variants allow you to set default variants for a component.
* @see https://www.tailwind-variants.org/docs/variants#default-variants
*/
defaultVariants?: DV;
},
/**
* The config object allows you to modify the default configuration.
* @see https://www.tailwind-variants.org/docs/api-reference#config-optional
*/
config?: TVConfig,
): TVReturnType<V, S, B, EV, ES, E>;
};
export type TVLite = {
<
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,
// @ts-expect-error
E extends TVReturnType = TVReturnType<
V,
S,
B,
// @ts-expect-error
EV extends undefined ? {} : EV,
// @ts-expect-error
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 allows for easy composition of components.
* @see https://www.tailwind-variants.org/docs/composing-components
*/
extend?: E;
/**
* Base allows you to set a base class for a component.
*/
base?: B;
/**
* Slots allow you to separate a component into multiple parts.
* @see https://www.tailwind-variants.org/docs/slots
*/
slots?: S;
/**
* Variants allow you to create multiple versions of the same component.
* @see https://www.tailwind-variants.org/docs/variants#adding-variants
*/
variants?: V;
/**
* Compound variants allow you to apply classes to multiple variants at once.
* @see https://www.tailwind-variants.org/docs/variants#compound-variants
*/
compoundVariants?: CV;
/**
* Compound slots allow you to apply classes to multiple slots at once.
*/
compoundSlots?: TVCompoundSlots<V, S, B>;
/**
* Default variants allow you to set default variants for a component.
* @see https://www.tailwind-variants.org/docs/variants#default-variants
*/
defaultVariants?: DV;
}): TVReturnType<V, S, B, EV, ES, E>;
};
export type VariantProps<Component extends (...args: any) => any> = Omit<
OmitUndefined<Parameters<Component>[0]>,
"class" | "className"
>;
+50
View File
@@ -0,0 +1,50 @@
'use strict';
var chunk2JY7EID6_cjs = require('./chunk-2JY7EID6.cjs');
Object.defineProperty(exports, "cx", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.cx; }
});
Object.defineProperty(exports, "falsyToString", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.falsyToString; }
});
Object.defineProperty(exports, "flat", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.flat; }
});
Object.defineProperty(exports, "flatArray", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.flatArray; }
});
Object.defineProperty(exports, "flatMergeArrays", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.flatMergeArrays; }
});
Object.defineProperty(exports, "isBoolean", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.isBoolean; }
});
Object.defineProperty(exports, "isEmptyObject", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.isEmptyObject; }
});
Object.defineProperty(exports, "isEqual", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.isEqual; }
});
Object.defineProperty(exports, "joinObjects", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.joinObjects; }
});
Object.defineProperty(exports, "mergeObjects", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.mergeObjects; }
});
Object.defineProperty(exports, "removeExtraSpaces", {
enumerable: true,
get: function () { return chunk2JY7EID6_cjs.removeExtraSpaces; }
});
+32
View File
@@ -0,0 +1,32 @@
import type {CnOptions, CnReturn} from "./types.d.ts";
export declare const falsyToString: <T>(value: T) => T | string;
export declare const isEmptyObject: (obj: unknown) => boolean;
export declare const flatArray: <T>(array: unknown[]) => T[];
export declare const flatMergeArrays: <T>(...arrays: unknown[][]) => T[];
export declare const mergeObjects: <T extends object, U extends object>(
obj1: T,
obj2: U,
) => Record<string, unknown>;
export declare const removeExtraSpaces: (str: string) => string;
export declare const isEqual: (obj1: object, obj2: object) => boolean;
export declare const isBoolean: (value: unknown) => boolean;
export declare const joinObjects: <
T extends Record<string, unknown>,
U extends Record<string, unknown>,
>(
obj1: T,
obj2: U,
) => T & U;
export declare const flat: <T>(arr: unknown[], target: T[]) => void;
export declare const cx: <T extends CnOptions>(...classes: T) => CnReturn;
+1
View File
@@ -0,0 +1 @@
export { cx, falsyToString, flat, flatArray, flatMergeArrays, isBoolean, isEmptyObject, isEqual, joinObjects, mergeObjects, removeExtraSpaces } from './chunk-LQJYWU4O.js';
+122
View File
@@ -0,0 +1,122 @@
{
"name": "tailwind-variants",
"version": "3.2.2",
"description": "🦄 Tailwindcss first-class variant API",
"keywords": [
"tailwindcss",
"classes",
"responsive",
"variants",
"styled",
"styles"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/heroui-inc/tailwind-variants"
},
"bugs": {
"url": "https://github.com/heroui-inc/tailwind-variants/issues"
},
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./lite": {
"types": "./dist/lite.d.ts",
"import": "./dist/lite.js",
"require": "./dist/lite.cjs"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.js",
"require": "./dist/utils.cjs"
},
"./dist/*": "./dist/*",
"./package.json": "./package.json"
},
"sideEffects": false,
"files": [
"dist",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"dev": "tsup --watch",
"build": "tsup && node copy-types.cjs",
"typecheck": "tsc --noEmit",
"prepack": "clean-package",
"benchmark": "node benchmark.js",
"postpack": "clean-package restore",
"lint": "eslint . src/**/*.{js,ts}",
"lint:fix": "eslint --fix . src/**/*.{js,ts}",
"test": "jest --verbose",
"test:watch": "jest --watch --no-verbose",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path .",
"release": "bumpp --execute='pnpm run changelog' --all",
"prepublishOnly": "pnpm run build",
"release:check": "pnpm run lint && pnpm run typecheck && pnpm run test && pnpm run build"
},
"devDependencies": {
"@commitlint/cli": "19.5.0",
"@commitlint/config-conventional": "19.5.0",
"@jest/globals": "29.7.0",
"@swc-node/jest": "1.8.12",
"@swc/cli": "0.5.0",
"@swc/core": "1.9.2",
"@swc/helpers": "0.5.15",
"@swc/jest": "0.2.37",
"@types/jest": "29.5.14",
"@types/node": "22.9.0",
"@typescript-eslint/eslint-plugin": "8.14.0",
"@typescript-eslint/parser": "8.14.0",
"benchmark": "2.1.4",
"bumpp": "10.2.0",
"changelogithub": "13.16.0",
"class-variance-authority": "0.7.0",
"clean-package": "2.2.0",
"conventional-changelog-cli": "5.0.0",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-config-ts-lambdas": "1.2.3",
"eslint-import-resolver-typescript": "3.6.3",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jest": "28.9.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-promise": "7.1.0",
"expect": "29.7.0",
"jest": "29.7.0",
"postcss": "8.5.6",
"prettier": "3.3.3",
"prettier-eslint": "16.3.0",
"prettier-eslint-cli": "8.0.1",
"tailwindcss": "4.1.11",
"ts-node": "10.9.2",
"tsup": "8.5.0",
"typescript": "5.6.3"
},
"peerDependencies": {
"tailwind-merge": ">=3.0.0",
"tailwindcss": "*"
},
"peerDependenciesMeta": {
"tailwind-merge": {
"optional": true
}
},
"engines": {
"node": ">=16.x",
"pnpm": ">=7.x"
}
}