tailwind-variants 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -50
- package/dist/chunk-2BFDQGZN.cjs +160 -0
- package/dist/chunk-AUQ4UGQK.js +673 -0
- package/dist/chunk-ICHZ2N3R.cjs +677 -0
- package/dist/chunk-OYFAXDFZ.js +147 -0
- package/dist/config-bO3A8WhU.d.cts +112 -0
- package/dist/config-bO3A8WhU.d.ts +112 -0
- package/dist/config.cjs +2 -0
- package/dist/config.d.cts +1 -0
- package/dist/config.d.ts +1 -23
- package/dist/config.js +1 -0
- package/dist/index.cjs +3314 -53
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +17 -115
- package/dist/index.js +3313 -49
- package/dist/lite.cjs +17 -12
- package/dist/lite.d.cts +10 -0
- package/dist/lite.d.ts +8 -11
- package/dist/lite.js +15 -8
- package/dist/types.cjs +2 -0
- package/dist/types.d.cts +168 -0
- package/dist/types.d.ts +122 -291
- package/dist/types.js +1 -0
- package/dist/utils.cjs +12 -12
- package/dist/utils.d.cts +16 -0
- package/dist/utils.d.ts +16 -32
- package/dist/utils.js +1 -1
- package/package.json +62 -66
- package/dist/chunk-2JY7EID6.cjs +0 -124
- package/dist/chunk-52Z2HSI4.cjs +0 -273
- package/dist/chunk-LQJYWU4O.js +0 -112
- package/dist/chunk-RZF76H2U.js +0 -269
package/README.md
CHANGED
|
@@ -24,57 +24,30 @@
|
|
|
24
24
|
- Composition support
|
|
25
25
|
- Fully typed
|
|
26
26
|
- Framework agnostic
|
|
27
|
-
-
|
|
28
|
-
-
|
|
27
|
+
- Built-in conflict resolution
|
|
28
|
+
- Tailwind CSS v4 support
|
|
29
29
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
For full documentation, visit [tailwind-variants.org](https://tailwind-variants.org)
|
|
33
|
-
|
|
34
|
-
> ❕ Note: `Tailwindcss V4` no longer supports the `config.content.transform` so we remove the `responsive variants` feature
|
|
35
|
-
>
|
|
36
|
-
> If you want to use `responsive variants`, you need to add it manually to your classname.
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
1. Installation:
|
|
41
|
-
To use Tailwind Variants in your project, you can install it as a dependency:
|
|
30
|
+
## Installation
|
|
42
31
|
|
|
43
32
|
```bash
|
|
44
|
-
yarn add tailwind-variants
|
|
45
|
-
# or
|
|
46
33
|
npm i tailwind-variants
|
|
47
34
|
# or
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**Optional:** If you want automatic conflict resolution, also install `tailwind-merge`:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
yarn add tailwind-merge
|
|
55
|
-
# or
|
|
56
|
-
npm i tailwind-merge
|
|
35
|
+
yarn add tailwind-variants
|
|
57
36
|
# or
|
|
58
|
-
pnpm add tailwind-
|
|
37
|
+
pnpm add tailwind-variants
|
|
59
38
|
```
|
|
60
39
|
|
|
61
|
-
|
|
40
|
+
**Lite mode:** import from `tailwind-variants/lite` for a smaller bundle without conflict resolution.
|
|
62
41
|
|
|
63
|
-
|
|
64
|
-
>
|
|
65
|
-
> ```js
|
|
66
|
-
> import {tv} from "tailwind-variants/lite";
|
|
67
|
-
> ```
|
|
42
|
+
**Upgrading?**
|
|
68
43
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
> - From v2 to v3: See the [v3 migration guide](./MIGRATION-V3.md)
|
|
72
|
-
> - From v1 to v2: See the [v2 migration guide](./MIGRATION-V2.md)
|
|
44
|
+
- v2 → v3: [migration guide](./.docs/migrations/v2-to-v3.md)
|
|
45
|
+
- v1 → v2: [migration guide](./.docs/migrations/v1-to-v2.md)
|
|
73
46
|
|
|
74
|
-
|
|
47
|
+
## Quick Start
|
|
75
48
|
|
|
76
49
|
```js
|
|
77
|
-
import {tv} from "tailwind-variants";
|
|
50
|
+
import { tv } from "tailwind-variants";
|
|
78
51
|
|
|
79
52
|
const button = tv({
|
|
80
53
|
base: "font-medium bg-blue-500 text-white rounded-full active:opacity-80",
|
|
@@ -101,20 +74,113 @@ const button = tv({
|
|
|
101
74
|
},
|
|
102
75
|
});
|
|
103
76
|
|
|
104
|
-
|
|
77
|
+
button({ size: "sm", color: "secondary" });
|
|
78
|
+
// => "font-medium rounded-full active:opacity-80 bg-purple-500 text-white text-sm px-3 py-1"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **Note:** Tailwind CSS v4 no longer supports `config.content.transform`, so responsive variants
|
|
82
|
+
> were removed. Add responsive classes to your class names manually if needed.
|
|
83
|
+
|
|
84
|
+
## Conflict Resolution
|
|
85
|
+
|
|
86
|
+
Conflict resolution is built into the default entry — no extra package is required. It is available
|
|
87
|
+
on `tv`, `createTV`, `cn`, and `cnMerge` (`cx` and `/lite` do not merge).
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import { tv, cn } from "tailwind-variants";
|
|
91
|
+
|
|
92
|
+
cn("px-2", "px-4"); // => "px-4"
|
|
93
|
+
|
|
94
|
+
tv({ base: "px-2", variants: { size: { lg: "px-4" } } })({ size: "lg" }); // => "px-4"
|
|
105
95
|
```
|
|
106
96
|
|
|
97
|
+
### Custom configuration
|
|
98
|
+
|
|
99
|
+
Pass `twMergeConfig` to teach the resolver about custom utilities. Prefer `{ extend, override }` —
|
|
100
|
+
`extend` appends to the defaults, `override` replaces them:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { cnMerge, createTV, tv, type TWMergeConfig } from "tailwind-variants";
|
|
104
|
+
|
|
105
|
+
const twMergeConfig = {
|
|
106
|
+
extend: {
|
|
107
|
+
classGroups: {
|
|
108
|
+
elevation: ["elevation-low", "elevation-high"],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
} satisfies TWMergeConfig;
|
|
112
|
+
|
|
113
|
+
tv({ base: "elevation-low", variants: { raised: { true: "elevation-high" } } }, { twMergeConfig });
|
|
114
|
+
createTV({ twMergeConfig });
|
|
115
|
+
cnMerge("elevation-low", "elevation-high")({ twMergeConfig }); // => "elevation-high"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Disable merging with `{ twMerge: false }` on `tv`, `createTV`, or `cnMerge`.
|
|
119
|
+
|
|
120
|
+
### Reusing a `tailwind-merge` config
|
|
121
|
+
|
|
122
|
+
If you already configure `extendTailwindMerge`, reuse the same config object as `twMergeConfig`
|
|
123
|
+
(pass the object — not the returned merge function):
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
127
|
+
import { createTV, type TWMergeConfig } from "tailwind-variants";
|
|
128
|
+
|
|
129
|
+
const mergeConfig = {
|
|
130
|
+
extend: {
|
|
131
|
+
classGroups: {
|
|
132
|
+
elevation: ["elevation-low", "elevation-high"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
} satisfies TWMergeConfig;
|
|
136
|
+
|
|
137
|
+
extendTailwindMerge(mergeConfig);
|
|
138
|
+
createTV({ twMergeConfig: mergeConfig });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For `createTailwindMerge`, move the custom parts of the factory into `{ extend, override }` and pass
|
|
142
|
+
that object. Merge functions and full default configs cannot be passed directly.
|
|
143
|
+
|
|
144
|
+
## Utility Functions
|
|
145
|
+
|
|
146
|
+
| Function | Behavior |
|
|
147
|
+
| --------- | ---------------------------------------------------- |
|
|
148
|
+
| `cx` | Concatenate class names (no merging) |
|
|
149
|
+
| `cn` | Concatenate and merge with the default config |
|
|
150
|
+
| `cnMerge` | Concatenate and merge, with optional per-call config |
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import { cx, cn, cnMerge } from "tailwind-variants";
|
|
154
|
+
|
|
155
|
+
cx("px-2", "px-4"); // => "px-2 px-4"
|
|
156
|
+
cn("px-2", "px-4"); // => "px-4"
|
|
157
|
+
cnMerge("px-2", "px-4")({ twMerge: false }); // => "px-2 px-4"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Documentation
|
|
161
|
+
|
|
162
|
+
For full documentation, visit [tailwind-variants.org](https://tailwind-variants.org).
|
|
163
|
+
|
|
107
164
|
## Acknowledgements
|
|
108
165
|
|
|
109
166
|
- [**cva**](https://github.com/joe-bell/cva) ([Joe Bell](https://github.com/joe-bell))
|
|
110
|
-
This project
|
|
167
|
+
This project started as an extension of Joe's work on `cva` — a great tool for generating variants
|
|
168
|
+
for a single element with Tailwind CSS. Big shoutout to [Joe Bell](https://github.com/joe-bell) and
|
|
169
|
+
[contributors](https://github.com/joe-bell/cva/graphs/contributors)! If you don't need the
|
|
170
|
+
**Tailwind Variants** features listed
|
|
171
|
+
[here](https://www.tailwind-variants.org/docs/comparison), we recommend `cva`.
|
|
111
172
|
|
|
112
|
-
- [**Stitches**](https://stitches.dev/) ([Modulz](https://modulz.app))
|
|
113
|
-
The pioneers of the `variants` API movement.
|
|
173
|
+
- [**Stitches**](https://stitches.dev/) ([Modulz](https://modulz.app))
|
|
174
|
+
The pioneers of the `variants` API movement. Immense thanks to [Modulz](https://modulz.app) for
|
|
175
|
+
their work on Stitches and the community around it.
|
|
176
|
+
|
|
177
|
+
- [**tailwind-merge**](https://github.com/dcastil/tailwind-merge), [**clsx**](https://github.com/lukeed/clsx), and [**cnfast**](https://github.com/aidenybai/cnfast)
|
|
178
|
+
Conflict resolution draws on ideas and MIT-licensed work from these projects.
|
|
114
179
|
|
|
115
180
|
## Community
|
|
116
181
|
|
|
117
|
-
We're excited to see the community adopt HeroUI, raise issues, and provide feedback. Whether it's a
|
|
182
|
+
We're excited to see the community adopt HeroUI, raise issues, and provide feedback. Whether it's a
|
|
183
|
+
feature request, bug report, or a project to showcase, please get involved!
|
|
118
184
|
|
|
119
185
|
- [Discord](https://discord.gg/9b6yyZKmH4)
|
|
120
186
|
- [Twitter](https://twitter.com/getnextui)
|
|
@@ -124,17 +190,15 @@ We're excited to see the community adopt HeroUI, raise issues, and provide feedb
|
|
|
124
190
|
|
|
125
191
|
Contributions are always welcome!
|
|
126
192
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
193
|
+
- [Contributing guidelines](./CONTRIBUTING.md)
|
|
194
|
+
- [Code of conduct](./CODE_OF_CONDUCT.md)
|
|
195
|
+
- [Security policy](./SECURITY.md)
|
|
130
196
|
|
|
131
197
|
## Authors
|
|
132
198
|
|
|
133
|
-
- Junior
|
|
199
|
+
- Junior Garcia ([@jrgarciadev](https://github.com/jrgarciadev))
|
|
134
200
|
- Tianen Pang ([@tianenpang](https://github.com/tianenpang))
|
|
135
201
|
|
|
136
202
|
## License
|
|
137
203
|
|
|
138
|
-
Licensed under the MIT License.
|
|
139
|
-
|
|
140
|
-
See [LICENSE](./LICENSE.md) for more information.
|
|
204
|
+
Licensed under the MIT License. See [LICENSE](./LICENSE.md) for more information.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/internal/join-class-value.ts
|
|
4
|
+
var isArray = Array.isArray;
|
|
5
|
+
var joinClassValue = (value) => {
|
|
6
|
+
if (!value && value !== 0 && value !== 0n) return "";
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
if (typeof value === "number") {
|
|
9
|
+
if (value !== value) return "";
|
|
10
|
+
return "" + value;
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === "bigint") return "" + value;
|
|
13
|
+
let result = "";
|
|
14
|
+
if (isArray(value)) {
|
|
15
|
+
const length = value.length;
|
|
16
|
+
for (let index = 0; index < length; index++) {
|
|
17
|
+
const item = value[index];
|
|
18
|
+
if (!item && item !== 0 && item !== 0n) continue;
|
|
19
|
+
const resolved = typeof item === "string" ? item : joinClassValue(item);
|
|
20
|
+
if (resolved) {
|
|
21
|
+
if (result) result += " ";
|
|
22
|
+
result += resolved;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === "object") {
|
|
28
|
+
for (const key in value) {
|
|
29
|
+
if (value[key]) {
|
|
30
|
+
if (result) result += " ";
|
|
31
|
+
result += key;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/utils.ts
|
|
39
|
+
var SPACE_REGEX = /\s+/g;
|
|
40
|
+
var isArray2 = Array.isArray;
|
|
41
|
+
var removeExtraSpaces = (str) => {
|
|
42
|
+
if (typeof str !== "string" || !str) return str;
|
|
43
|
+
return str.replace(SPACE_REGEX, " ").trim();
|
|
44
|
+
};
|
|
45
|
+
var stringNeedsNormalize = (str) => {
|
|
46
|
+
const len = str.length;
|
|
47
|
+
if (len === 0) return false;
|
|
48
|
+
const first = str.charCodeAt(0);
|
|
49
|
+
const last = str.charCodeAt(len - 1);
|
|
50
|
+
if (first === 32 || last === 32 || first >= 9 && first <= 13 || first === 160 || last >= 9 && last <= 13 || last === 160) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
for (let i = 0; i < len; i++) {
|
|
54
|
+
const code = str.charCodeAt(i);
|
|
55
|
+
if (code >= 9 && code <= 13 || code === 160) return true;
|
|
56
|
+
if (code === 32 && i + 1 < len && str.charCodeAt(i + 1) === 32) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
var cx = (...classnames) => {
|
|
61
|
+
const result = joinClassValue(classnames);
|
|
62
|
+
if (!result) return void 0;
|
|
63
|
+
return stringNeedsNormalize(result) ? removeExtraSpaces(result) : result;
|
|
64
|
+
};
|
|
65
|
+
var falsyToString = (value) => value === false ? "false" : value === true ? "true" : value === 0 ? "0" : value;
|
|
66
|
+
var isEmptyObject = (obj) => {
|
|
67
|
+
if (!obj || typeof obj !== "object") return true;
|
|
68
|
+
for (const _ in obj) return false;
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
var isEqual = (obj1, obj2) => {
|
|
72
|
+
if (obj1 === obj2) return true;
|
|
73
|
+
if (!obj1 || !obj2) return false;
|
|
74
|
+
const record1 = obj1;
|
|
75
|
+
const record2 = obj2;
|
|
76
|
+
const keys1 = Object.keys(record1);
|
|
77
|
+
const keys2 = Object.keys(record2);
|
|
78
|
+
if (keys1.length !== keys2.length) return false;
|
|
79
|
+
for (let i = 0; i < keys1.length; i++) {
|
|
80
|
+
const key = keys1[i];
|
|
81
|
+
if (!keys2.includes(key)) return false;
|
|
82
|
+
if (record1[key] !== record2[key]) return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
var isBoolean = (value) => value === true || value === false;
|
|
87
|
+
var joinObjects = (obj1, obj2) => {
|
|
88
|
+
const target = obj1;
|
|
89
|
+
for (const key in obj2) {
|
|
90
|
+
if (Object.hasOwn(obj2, key)) {
|
|
91
|
+
const val2 = obj2[key];
|
|
92
|
+
if (key in target) {
|
|
93
|
+
target[key] = cx(target[key], val2);
|
|
94
|
+
} else {
|
|
95
|
+
target[key] = val2;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return obj1;
|
|
100
|
+
};
|
|
101
|
+
var flat = (arr, target) => {
|
|
102
|
+
for (let i = 0; i < arr.length; i++) {
|
|
103
|
+
const el = arr[i];
|
|
104
|
+
if (isArray2(el)) flat(el, target);
|
|
105
|
+
else if (el) target.push(el);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function flatArray(arr) {
|
|
109
|
+
const flattened = [];
|
|
110
|
+
flat(arr, flattened);
|
|
111
|
+
return flattened;
|
|
112
|
+
}
|
|
113
|
+
var flatMergeArrays = (...arrays) => {
|
|
114
|
+
const result = [];
|
|
115
|
+
flat(arrays, result);
|
|
116
|
+
const filtered = [];
|
|
117
|
+
for (let i = 0; i < result.length; i++) {
|
|
118
|
+
if (result[i]) filtered.push(result[i]);
|
|
119
|
+
}
|
|
120
|
+
return filtered;
|
|
121
|
+
};
|
|
122
|
+
var mergeObjects = (obj1, obj2) => {
|
|
123
|
+
const record1 = obj1;
|
|
124
|
+
const record2 = obj2;
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const key in record1) {
|
|
127
|
+
const val1 = record1[key];
|
|
128
|
+
if (key in record2) {
|
|
129
|
+
const val2 = record2[key];
|
|
130
|
+
if (isArray2(val1) || isArray2(val2)) {
|
|
131
|
+
result[key] = flatMergeArrays(val2, val1);
|
|
132
|
+
} else if (typeof val1 === "object" && typeof val2 === "object" && val1 && val2) {
|
|
133
|
+
result[key] = mergeObjects(val1, val2);
|
|
134
|
+
} else {
|
|
135
|
+
result[key] = val2 + " " + val1;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
result[key] = val1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const key in record2) {
|
|
142
|
+
if (!(key in record1)) {
|
|
143
|
+
result[key] = record2[key];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
exports.cx = cx;
|
|
150
|
+
exports.falsyToString = falsyToString;
|
|
151
|
+
exports.flat = flat;
|
|
152
|
+
exports.flatArray = flatArray;
|
|
153
|
+
exports.flatMergeArrays = flatMergeArrays;
|
|
154
|
+
exports.isBoolean = isBoolean;
|
|
155
|
+
exports.isEmptyObject = isEmptyObject;
|
|
156
|
+
exports.isEqual = isEqual;
|
|
157
|
+
exports.joinClassValue = joinClassValue;
|
|
158
|
+
exports.joinObjects = joinObjects;
|
|
159
|
+
exports.mergeObjects = mergeObjects;
|
|
160
|
+
exports.removeExtraSpaces = removeExtraSpaces;
|