tailwind-variants 3.2.2 → 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 +91 -80
- 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 +3312 -32
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +17 -121
- package/dist/index.js +3309 -25
- 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,73 +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"
|
|
105
79
|
```
|
|
106
80
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Tailwind Variants provides several utility functions for combining and merging class names:
|
|
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.
|
|
110
83
|
|
|
111
|
-
|
|
84
|
+
## Conflict Resolution
|
|
112
85
|
|
|
113
|
-
|
|
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).
|
|
114
88
|
|
|
115
89
|
```js
|
|
116
|
-
import {
|
|
90
|
+
import { tv, cn } from "tailwind-variants";
|
|
117
91
|
|
|
118
|
-
|
|
119
|
-
|
|
92
|
+
cn("px-2", "px-4"); // => "px-4"
|
|
93
|
+
|
|
94
|
+
tv({ base: "px-2", variants: { size: { lg: "px-4" } } })({ size: "lg" }); // => "px-4"
|
|
120
95
|
```
|
|
121
96
|
|
|
122
|
-
###
|
|
97
|
+
### Custom configuration
|
|
123
98
|
|
|
124
|
-
|
|
99
|
+
Pass `twMergeConfig` to teach the resolver about custom utilities. Prefer `{ extend, override }` —
|
|
100
|
+
`extend` appends to the defaults, `override` replaces them:
|
|
125
101
|
|
|
126
|
-
|
|
102
|
+
```ts
|
|
103
|
+
import { cnMerge, createTV, tv, type TWMergeConfig } from "tailwind-variants";
|
|
127
104
|
|
|
128
|
-
|
|
129
|
-
|
|
105
|
+
const twMergeConfig = {
|
|
106
|
+
extend: {
|
|
107
|
+
classGroups: {
|
|
108
|
+
elevation: ["elevation-low", "elevation-high"],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
} satisfies TWMergeConfig;
|
|
130
112
|
|
|
131
|
-
|
|
132
|
-
|
|
113
|
+
tv({ base: "elevation-low", variants: { raised: { true: "elevation-high" } } }, { twMergeConfig });
|
|
114
|
+
createTV({ twMergeConfig });
|
|
115
|
+
cnMerge("elevation-low", "elevation-high")({ twMergeConfig }); // => "elevation-high"
|
|
133
116
|
```
|
|
134
117
|
|
|
135
|
-
|
|
118
|
+
Disable merging with `{ twMerge: false }` on `tv`, `createTV`, or `cnMerge`.
|
|
136
119
|
|
|
137
|
-
|
|
120
|
+
### Reusing a `tailwind-merge` config
|
|
138
121
|
|
|
139
|
-
|
|
122
|
+
If you already configure `extendTailwindMerge`, reuse the same config object as `twMergeConfig`
|
|
123
|
+
(pass the object — not the returned merge function):
|
|
140
124
|
|
|
141
|
-
```
|
|
142
|
-
import {
|
|
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
143
|
|
|
144
|
-
|
|
145
|
-
cnMerge("px-2", "px-4")({twMerge: false}) // => "px-2 px-4"
|
|
144
|
+
## Utility Functions
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
|
|
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";
|
|
149
154
|
|
|
150
|
-
//
|
|
151
|
-
|
|
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"
|
|
152
158
|
```
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
## Documentation
|
|
155
161
|
|
|
156
|
-
|
|
157
|
-
- Use `cn` for most cases when you want automatic conflict resolution with default settings
|
|
158
|
-
- Use `cnMerge` when you need to customize the merge behavior (disable merging, custom config, etc.)
|
|
162
|
+
For full documentation, visit [tailwind-variants.org](https://tailwind-variants.org).
|
|
159
163
|
|
|
160
164
|
## Acknowledgements
|
|
161
165
|
|
|
162
166
|
- [**cva**](https://github.com/joe-bell/cva) ([Joe Bell](https://github.com/joe-bell))
|
|
163
|
-
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`.
|
|
172
|
+
|
|
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.
|
|
164
176
|
|
|
165
|
-
- [**
|
|
166
|
-
|
|
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.
|
|
167
179
|
|
|
168
180
|
## Community
|
|
169
181
|
|
|
170
|
-
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!
|
|
171
184
|
|
|
172
185
|
- [Discord](https://discord.gg/9b6yyZKmH4)
|
|
173
186
|
- [Twitter](https://twitter.com/getnextui)
|
|
@@ -177,17 +190,15 @@ We're excited to see the community adopt HeroUI, raise issues, and provide feedb
|
|
|
177
190
|
|
|
178
191
|
Contributions are always welcome!
|
|
179
192
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
193
|
+
- [Contributing guidelines](./CONTRIBUTING.md)
|
|
194
|
+
- [Code of conduct](./CODE_OF_CONDUCT.md)
|
|
195
|
+
- [Security policy](./SECURITY.md)
|
|
183
196
|
|
|
184
197
|
## Authors
|
|
185
198
|
|
|
186
|
-
- Junior
|
|
199
|
+
- Junior Garcia ([@jrgarciadev](https://github.com/jrgarciadev))
|
|
187
200
|
- Tianen Pang ([@tianenpang](https://github.com/tianenpang))
|
|
188
201
|
|
|
189
202
|
## License
|
|
190
203
|
|
|
191
|
-
Licensed under the MIT License.
|
|
192
|
-
|
|
193
|
-
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;
|