twirlwind 0.1.0 → 0.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 +49 -124
- package/dist/index.d.mts +16 -29
- package/dist/index.mjs +196 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,100 +10,58 @@ Converts style objects, CSS declaration strings, and `CSSStyleDeclaration` value
|
|
|
10
10
|
npm install twirlwind
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Usage
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import {
|
|
16
|
+
import { twirl } from 'twirlwind'
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
justifyContent: 'center',
|
|
21
|
-
padding: '16px 8px',
|
|
22
|
-
backgroundColor: 'oklch(62.3% 0.214 259.815)'
|
|
23
|
-
})
|
|
24
|
-
// → "flex py-4 px-2 justify-center bg-blue-500"
|
|
18
|
+
twirl({ display: 'flex', padding: '16px 8px', color: '#ef4444' })
|
|
19
|
+
// → "flex py-4 px-2 text-red-500"
|
|
25
20
|
```
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
### `styleToTailwind(input, options?)`
|
|
22
|
+
### Inputs
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
`twirl()` accepts any of these and returns a class string:
|
|
32
25
|
|
|
33
26
|
```ts
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const result = styleToTailwind({ display: 'flex', width: '37px' })
|
|
37
|
-
|
|
38
|
-
result.className // "flex w-[37px]"
|
|
39
|
-
result.classes // ["flex", "w-[37px]"]
|
|
40
|
-
result.exact // ConvertedDeclaration[] — exact utility matches
|
|
41
|
-
result.arbitrary // ConvertedDeclaration[] — arbitrary value/property fallbacks
|
|
42
|
-
result.unmatched // CssDeclaration[] — declarations that couldn't be converted
|
|
43
|
-
result.warnings // ConversionWarning[]
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### `styleToClassName(input, options?)`
|
|
27
|
+
// Style object
|
|
28
|
+
twirl({ backgroundColor: 'white', fontSize: '16px' })
|
|
47
29
|
|
|
48
|
-
|
|
30
|
+
// CSS string
|
|
31
|
+
twirl('display: flex; padding: 16px')
|
|
49
32
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// → "p-4 text-white"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### `styleToClasses(input, options?)`
|
|
56
|
-
|
|
57
|
-
Returns an array of class strings.
|
|
33
|
+
// CSSStyleDeclaration (browser)
|
|
34
|
+
twirl(element.style)
|
|
58
35
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// → ["my-2", "mx-4"]
|
|
36
|
+
// Computed styles
|
|
37
|
+
twirl(getComputedStyle(element))
|
|
62
38
|
```
|
|
63
39
|
|
|
64
|
-
###
|
|
40
|
+
### Detailed result
|
|
65
41
|
|
|
66
|
-
|
|
42
|
+
Use `twirl.convert()` when you need metadata:
|
|
67
43
|
|
|
68
44
|
```ts
|
|
69
|
-
|
|
70
|
-
// → { className: "flex p-2 gap-4", ... }
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Input formats
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
// Style object (camelCase or kebab-case)
|
|
77
|
-
styleToClassName({ backgroundColor: 'white', 'font-size': '16px' })
|
|
78
|
-
|
|
79
|
-
// CSS declaration string
|
|
80
|
-
styleToClassName('display: flex; padding: 16px')
|
|
45
|
+
const result = twirl.convert({ display: 'flex', width: '37px' })
|
|
81
46
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
new Map([
|
|
88
|
-
['display', 'flex'],
|
|
89
|
-
['padding', '16px']
|
|
90
|
-
])
|
|
91
|
-
)
|
|
47
|
+
result.className // "flex w-[37px]"
|
|
48
|
+
result.classes // ["flex", "w-[37px]"]
|
|
49
|
+
result.exact // exact utility matches
|
|
50
|
+
result.arbitrary // arbitrary value/property fallbacks
|
|
51
|
+
result.unmatched // declarations that couldn't be converted
|
|
92
52
|
```
|
|
93
53
|
|
|
94
54
|
## Features
|
|
95
55
|
|
|
96
56
|
### Color matching
|
|
97
57
|
|
|
98
|
-
Matches
|
|
58
|
+
Matches across formats — OKLCH, hex, `rgb()`, keywords, opacity modifiers.
|
|
99
59
|
|
|
100
60
|
```ts
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
styleToClassName({ color: 'inherit' }) // "text-inherit"
|
|
106
|
-
styleToClassName({ color: 'currentColor' }) // "text-current"
|
|
61
|
+
twirl({ color: '#ef4444' }) // "text-red-500"
|
|
62
|
+
twirl({ color: 'rgb(59 130 246)' }) // "text-blue-500"
|
|
63
|
+
twirl({ color: 'oklch(62.3% 0.214 259.815 / 50%)' }) // "text-blue-500/50"
|
|
64
|
+
twirl({ color: 'currentColor' }) // "text-current"
|
|
107
65
|
```
|
|
108
66
|
|
|
109
67
|
### Shorthand expansion
|
|
@@ -111,49 +69,39 @@ styleToClassName({ color: 'currentColor' }) // "text-current"
|
|
|
111
69
|
CSS shorthands decompose into Tailwind longhands.
|
|
112
70
|
|
|
113
71
|
```ts
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
styleToClassName({ font: 'bold 16px/1.5 sans-serif' })
|
|
118
|
-
// → "font-bold text-base leading-normal font-sans"
|
|
119
|
-
|
|
120
|
-
styleToClassName({ background: 'white center no-repeat' })
|
|
121
|
-
// → "bg-white bg-center bg-no-repeat"
|
|
122
|
-
|
|
123
|
-
styleToClassName({ transition: 'all 200ms ease-in' })
|
|
124
|
-
// → "transition-all duration-200 ease-in"
|
|
72
|
+
twirl({ border: '2px solid #ef4444' }) // "border-2 border-red-500 border-solid"
|
|
73
|
+
twirl({ font: 'bold 16px/1.5 sans-serif' }) // "font-bold text-base leading-normal font-sans"
|
|
74
|
+
twirl({ background: 'white center no-repeat' }) // "bg-white bg-center bg-no-repeat"
|
|
125
75
|
```
|
|
126
76
|
|
|
127
|
-
### Multi-value
|
|
77
|
+
### Multi-value parsing
|
|
128
78
|
|
|
129
|
-
Compound `transform` and `filter` declarations decompose into individual
|
|
79
|
+
Compound `transform` and `filter` declarations decompose into individual classes.
|
|
130
80
|
|
|
131
81
|
```ts
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
styleToClassName({ filter: 'blur(8px) brightness(0.75)' })
|
|
136
|
-
// → "blur brightness-75"
|
|
82
|
+
twirl({ transform: 'translateX(8px) rotate(45deg)' }) // "rotate-45 translate-x-2"
|
|
83
|
+
twirl({ filter: 'blur(8px) brightness(0.75)' }) // "blur brightness-75"
|
|
84
|
+
twirl({ scrollSnapType: 'x mandatory' }) // "snap-x snap-mandatory"
|
|
137
85
|
```
|
|
138
86
|
|
|
139
87
|
### Compression
|
|
140
88
|
|
|
141
|
-
Expanded longhands compress to shorthand utilities
|
|
89
|
+
Expanded longhands compress to shorthand utilities.
|
|
142
90
|
|
|
143
91
|
```ts
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
92
|
+
twirl({ margin: '8px' }) // "m-2"
|
|
93
|
+
twirl({ inset: '0' }) // "inset-0"
|
|
94
|
+
twirl({ padding: '8px 16px' }) // "py-2 px-4"
|
|
95
|
+
twirl({ borderRadius: '8px' }) // "rounded-lg"
|
|
96
|
+
twirl({ gap: '12px 12px' }) // "gap-3"
|
|
149
97
|
```
|
|
150
98
|
|
|
151
99
|
### Variants
|
|
152
100
|
|
|
153
|
-
Nested objects map to Tailwind variants
|
|
101
|
+
Nested objects map to Tailwind variants.
|
|
154
102
|
|
|
155
103
|
```ts
|
|
156
|
-
|
|
104
|
+
twirl({
|
|
157
105
|
color: 'white',
|
|
158
106
|
':hover': { color: '#3b82f6' },
|
|
159
107
|
'@media (min-width: 768px)': { display: 'grid' },
|
|
@@ -163,48 +111,25 @@ styleToClassName({
|
|
|
163
111
|
// → "text-white hover:text-blue-500 md:grid dark:bg-black @lg:flex"
|
|
164
112
|
```
|
|
165
113
|
|
|
166
|
-
### Scroll snap decomposition
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
styleToClassName({ scrollSnapType: 'x mandatory' })
|
|
170
|
-
// → "snap-x snap-mandatory"
|
|
171
|
-
```
|
|
172
|
-
|
|
173
114
|
### Arbitrary fallback
|
|
174
115
|
|
|
175
|
-
Every CSS property produces valid output.
|
|
116
|
+
Every CSS property produces valid output.
|
|
176
117
|
|
|
177
118
|
```ts
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
styleToClassName({ width: '37px' })
|
|
182
|
-
// → "w-[37px]"
|
|
119
|
+
twirl({ scrollTimelineName: '--main' }) // "[scroll-timeline-name:--main]"
|
|
120
|
+
twirl({ width: '37px' }) // "w-[37px]"
|
|
183
121
|
```
|
|
184
122
|
|
|
185
123
|
## Options
|
|
186
124
|
|
|
187
125
|
```ts
|
|
188
|
-
|
|
189
|
-
// Allow arbitrary value utilities like w-[37px]
|
|
126
|
+
twirl(input, {
|
|
190
127
|
allowArbitraryValues: true, // default: true
|
|
191
|
-
|
|
192
|
-
// Allow arbitrary property fallbacks like [scroll-timeline-name:--x]
|
|
193
128
|
allowArbitraryProperties: true, // default: true
|
|
194
|
-
|
|
195
|
-
// Compress matching longhands to shorthands
|
|
196
129
|
compression: 'safe', // "none" | "safe" | "aggressive"
|
|
197
|
-
|
|
198
|
-
// Sort output classes
|
|
199
130
|
sort: 'grouped', // "input" | "tailwind" | "grouped"
|
|
200
|
-
|
|
201
|
-
// Color matching strategy
|
|
202
131
|
colorMatch: 'exact', // "exact" | "nearest" | "none"
|
|
203
|
-
|
|
204
|
-
// Spacing token multiplier mode
|
|
205
132
|
numericMultipliers: 'integer', // "all" | "integer" | "never"
|
|
206
|
-
|
|
207
|
-
// Custom theme colors/spacing
|
|
208
133
|
theme: {
|
|
209
134
|
colors: { brand: '#ff6600' },
|
|
210
135
|
spacing: { '18': '4.5rem' }
|
|
@@ -215,9 +140,9 @@ styleToTailwind(input, {
|
|
|
215
140
|
## How it works
|
|
216
141
|
|
|
217
142
|
1. **Normalize** — camelCase → kebab-case, numeric → px, vendor prefixes, `!important`
|
|
218
|
-
2. **Expand
|
|
143
|
+
2. **Expand** — `margin`, `border`, `font`, `background`, `transition`, `overflow`, `gap`, etc.
|
|
219
144
|
3. **Convert** — exact utility → value alias → spacing token → color match → arbitrary value → arbitrary property
|
|
220
|
-
4. **Compress** — merge
|
|
145
|
+
4. **Compress** — merge longhands back to shorthand utilities
|
|
221
146
|
5. **Sort** — deterministic output ordering
|
|
222
147
|
|
|
223
148
|
## License
|
package/dist/index.d.mts
CHANGED
|
@@ -4,55 +4,42 @@ interface StyleObject {
|
|
|
4
4
|
[property: string]: StylePrimitive | StyleObject;
|
|
5
5
|
}
|
|
6
6
|
type StyleInput = string | StyleObject | CSSStyleDeclaration | Iterable<[string, StylePrimitive]>;
|
|
7
|
-
type
|
|
8
|
-
type SortMode = 'input' | 'tailwind' | 'grouped';
|
|
9
|
-
type CompressionMode = 'none' | 'safe' | 'aggressive';
|
|
10
|
-
type ColorMatchMode = 'exact' | 'nearest' | 'none';
|
|
11
|
-
type NumericMultiplierMode = 'all' | 'integer' | 'never';
|
|
12
|
-
type TwirlwindTheme = {
|
|
7
|
+
type Theme = {
|
|
13
8
|
spacing?: Record<string, string>;
|
|
14
9
|
colors?: Record<string, string>;
|
|
15
10
|
};
|
|
16
|
-
type
|
|
17
|
-
|
|
18
|
-
tailwindVersion?: '4';
|
|
19
|
-
theme?: TwirlwindTheme;
|
|
11
|
+
type Options = {
|
|
12
|
+
theme?: Theme;
|
|
20
13
|
allowArbitraryValues?: boolean;
|
|
21
14
|
allowArbitraryProperties?: boolean;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
sort?: SortMode;
|
|
15
|
+
compression?: 'none' | 'safe' | 'aggressive';
|
|
16
|
+
sort?: 'input' | 'tailwind' | 'grouped';
|
|
25
17
|
important?: boolean;
|
|
26
|
-
colorMatch?:
|
|
27
|
-
numericMultipliers?:
|
|
18
|
+
colorMatch?: 'exact' | 'nearest' | 'none';
|
|
19
|
+
numericMultipliers?: 'all' | 'integer' | 'never';
|
|
28
20
|
};
|
|
29
|
-
type
|
|
21
|
+
type Declaration = {
|
|
30
22
|
property: string;
|
|
31
23
|
value: string;
|
|
32
24
|
important: boolean;
|
|
33
25
|
variants: string[];
|
|
34
26
|
};
|
|
35
|
-
type ConvertedDeclaration =
|
|
27
|
+
type ConvertedDeclaration = Declaration & {
|
|
36
28
|
className: string;
|
|
37
29
|
kind: 'exact' | 'arbitrary';
|
|
38
30
|
};
|
|
39
|
-
type
|
|
40
|
-
declaration: CssDeclaration;
|
|
41
|
-
message: string;
|
|
42
|
-
};
|
|
43
|
-
type StyleToTailwindResult = {
|
|
31
|
+
type Result = {
|
|
44
32
|
className: string;
|
|
45
33
|
classes: string[];
|
|
46
34
|
exact: ConvertedDeclaration[];
|
|
47
35
|
arbitrary: ConvertedDeclaration[];
|
|
48
|
-
unmatched:
|
|
49
|
-
warnings: ConversionWarning[];
|
|
36
|
+
unmatched: Declaration[];
|
|
50
37
|
};
|
|
51
38
|
//#endregion
|
|
52
39
|
//#region src/index.d.ts
|
|
53
|
-
declare function
|
|
54
|
-
declare
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
declare function twirl(input: StyleInput, options?: Options): string;
|
|
41
|
+
declare namespace twirl {
|
|
42
|
+
var convert: (input: StyleInput, options?: Options) => Result;
|
|
43
|
+
}
|
|
57
44
|
//#endregion
|
|
58
|
-
export { type ConvertedDeclaration, type
|
|
45
|
+
export { type ConvertedDeclaration, type Declaration, type Options, type Result, type StyleInput, type StyleObject, type StylePrimitive, type Theme, twirl };
|
package/dist/index.mjs
CHANGED
|
@@ -1316,6 +1316,25 @@ const exactUtilities = {
|
|
|
1316
1316
|
contain: "overscroll-y-contain",
|
|
1317
1317
|
none: "overscroll-y-none"
|
|
1318
1318
|
},
|
|
1319
|
+
"scrollbar-width": {
|
|
1320
|
+
auto: "scrollbar-auto",
|
|
1321
|
+
thin: "scrollbar-thin",
|
|
1322
|
+
none: "scrollbar-none"
|
|
1323
|
+
},
|
|
1324
|
+
"scrollbar-gutter": {
|
|
1325
|
+
stable: "scrollbar-gutter-stable",
|
|
1326
|
+
"stable both-edges": "scrollbar-gutter-both-edges"
|
|
1327
|
+
},
|
|
1328
|
+
"mask-type": {
|
|
1329
|
+
alpha: "mask-type-alpha",
|
|
1330
|
+
luminance: "mask-type-luminance"
|
|
1331
|
+
},
|
|
1332
|
+
"mask-composite": {
|
|
1333
|
+
add: "mask-composite-add",
|
|
1334
|
+
subtract: "mask-composite-subtract",
|
|
1335
|
+
intersect: "mask-composite-intersect",
|
|
1336
|
+
exclude: "mask-composite-exclude"
|
|
1337
|
+
},
|
|
1319
1338
|
"scroll-behavior": {
|
|
1320
1339
|
auto: "scroll-auto",
|
|
1321
1340
|
smooth: "scroll-smooth"
|
|
@@ -1419,6 +1438,24 @@ const exactUtilities = {
|
|
|
1419
1438
|
"0 0": "origin-top-left",
|
|
1420
1439
|
"0% 0%": "origin-top-left"
|
|
1421
1440
|
},
|
|
1441
|
+
"perspective-origin": {
|
|
1442
|
+
center: "perspective-origin-center",
|
|
1443
|
+
top: "perspective-origin-top",
|
|
1444
|
+
"top right": "perspective-origin-top-right",
|
|
1445
|
+
right: "perspective-origin-right",
|
|
1446
|
+
"bottom right": "perspective-origin-bottom-right",
|
|
1447
|
+
bottom: "perspective-origin-bottom",
|
|
1448
|
+
"bottom left": "perspective-origin-bottom-left",
|
|
1449
|
+
left: "perspective-origin-left",
|
|
1450
|
+
"top left": "perspective-origin-top-left",
|
|
1451
|
+
"100% 0": "perspective-origin-top-right",
|
|
1452
|
+
"100% 0%": "perspective-origin-top-right",
|
|
1453
|
+
"100% 100%": "perspective-origin-bottom-right",
|
|
1454
|
+
"0 100%": "perspective-origin-bottom-left",
|
|
1455
|
+
"0% 100%": "perspective-origin-bottom-left",
|
|
1456
|
+
"0 0": "perspective-origin-top-left",
|
|
1457
|
+
"0% 0%": "perspective-origin-top-left"
|
|
1458
|
+
},
|
|
1422
1459
|
"scroll-snap-align": {
|
|
1423
1460
|
start: "snap-start",
|
|
1424
1461
|
end: "snap-end",
|
|
@@ -1668,6 +1705,9 @@ const arbitraryPrefixes = {
|
|
|
1668
1705
|
"flex-shrink": "shrink",
|
|
1669
1706
|
"word-spacing": "word-spacing",
|
|
1670
1707
|
hyphens: "hyphens",
|
|
1708
|
+
"scrollbar-color": "scrollbar-color",
|
|
1709
|
+
"font-feature-settings": "font-feature",
|
|
1710
|
+
"perspective-origin": "perspective-origin",
|
|
1671
1711
|
"column-rule-color": "column-rule",
|
|
1672
1712
|
"column-rule-width": "column-rule",
|
|
1673
1713
|
"column-rule-style": "column-rule",
|
|
@@ -2214,6 +2254,15 @@ function convertDeclaration(declaration, options) {
|
|
|
2214
2254
|
if (Array.isArray(filterResult)) return filterResult.map((cls) => converted(declaration, cls, "exact"));
|
|
2215
2255
|
return converted(declaration, filterResult, "exact");
|
|
2216
2256
|
}
|
|
2257
|
+
const shadowClass = convertShadow(declaration);
|
|
2258
|
+
if (shadowClass) return converted(declaration, shadowClass, "exact");
|
|
2259
|
+
const gradientResult = convertGradient(declaration, options);
|
|
2260
|
+
if (gradientResult) {
|
|
2261
|
+
if (Array.isArray(gradientResult)) return gradientResult.map((cls) => converted(declaration, cls, "exact"));
|
|
2262
|
+
return converted(declaration, gradientResult, "exact");
|
|
2263
|
+
}
|
|
2264
|
+
const varRef = convertVarReference(declaration);
|
|
2265
|
+
if (varRef) return converted(declaration, varRef, "exact");
|
|
2217
2266
|
const prefix = arbitraryPrefixes[declaration.property];
|
|
2218
2267
|
if (prefix && options.allowArbitraryValues) return converted(declaration, arbitraryValue(prefix, declaration.value), "arbitrary");
|
|
2219
2268
|
if (options.allowArbitraryProperties) return converted(declaration, arbitraryProperty(declaration.property, declaration.value), "arbitrary");
|
|
@@ -2522,6 +2571,9 @@ function convertTransform(declaration, options) {
|
|
|
2522
2571
|
if (declaration.property === "scale-x") return scaleClass(declaration.value, "scale-x");
|
|
2523
2572
|
if (declaration.property === "scale-y") return scaleClass(declaration.value, "scale-y");
|
|
2524
2573
|
if (declaration.property === "scale-z") return scaleClass(declaration.value, "scale-z");
|
|
2574
|
+
if (declaration.property === "skew") return angleClass("skew", declaration.value);
|
|
2575
|
+
if (declaration.property === "skew-x") return angleClass("skew-x", declaration.value);
|
|
2576
|
+
if (declaration.property === "skew-y") return angleClass("skew-y", declaration.value);
|
|
2525
2577
|
if (declaration.property === "translate") return translateClass(declaration.value, options);
|
|
2526
2578
|
if (declaration.property === "translate-x") return translateAxisClass("translate-x", declaration.value, options);
|
|
2527
2579
|
if (declaration.property === "translate-y") return translateAxisClass("translate-y", declaration.value, options);
|
|
@@ -2623,6 +2675,135 @@ function scaleClass(value, prefix = "scale") {
|
|
|
2623
2675
|
if (!Number.isInteger(percent)) return `${prefix}-[${value.trim()}]`;
|
|
2624
2676
|
return percent < 0 ? `-${prefix}-${Math.abs(percent)}` : `${prefix}-${percent}`;
|
|
2625
2677
|
}
|
|
2678
|
+
const shadowValues = {
|
|
2679
|
+
"0 1px 2px 0 rgb(0 0 0 / 0.05)": "shadow-xs",
|
|
2680
|
+
"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)": "shadow-sm",
|
|
2681
|
+
"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)": "shadow-md",
|
|
2682
|
+
"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)": "shadow-lg",
|
|
2683
|
+
"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)": "shadow-xl",
|
|
2684
|
+
"0 25px 50px -12px rgb(0 0 0 / 0.25)": "shadow-2xl",
|
|
2685
|
+
"inset 0 2px 4px 0 rgb(0 0 0 / 0.05)": "shadow-inner"
|
|
2686
|
+
};
|
|
2687
|
+
function convertShadow(declaration) {
|
|
2688
|
+
if (declaration.property !== "box-shadow") return void 0;
|
|
2689
|
+
return shadowValues[declaration.value.trim().replace(/\s+/g, " ")];
|
|
2690
|
+
}
|
|
2691
|
+
const varPrefixes = {
|
|
2692
|
+
color: "text",
|
|
2693
|
+
"background-color": "bg",
|
|
2694
|
+
"border-color": "border",
|
|
2695
|
+
"border-top-color": "border-t",
|
|
2696
|
+
"border-right-color": "border-r",
|
|
2697
|
+
"border-bottom-color": "border-b",
|
|
2698
|
+
"border-left-color": "border-l",
|
|
2699
|
+
"outline-color": "outline",
|
|
2700
|
+
fill: "fill",
|
|
2701
|
+
stroke: "stroke",
|
|
2702
|
+
"caret-color": "caret",
|
|
2703
|
+
"accent-color": "accent",
|
|
2704
|
+
"text-decoration-color": "decoration",
|
|
2705
|
+
width: "w",
|
|
2706
|
+
height: "h",
|
|
2707
|
+
"min-width": "min-w",
|
|
2708
|
+
"min-height": "min-h",
|
|
2709
|
+
"max-width": "max-w",
|
|
2710
|
+
"max-height": "max-h",
|
|
2711
|
+
padding: "p",
|
|
2712
|
+
"padding-top": "pt",
|
|
2713
|
+
"padding-right": "pr",
|
|
2714
|
+
"padding-bottom": "pb",
|
|
2715
|
+
"padding-left": "pl",
|
|
2716
|
+
margin: "m",
|
|
2717
|
+
"margin-top": "mt",
|
|
2718
|
+
"margin-right": "mr",
|
|
2719
|
+
"margin-bottom": "mb",
|
|
2720
|
+
"margin-left": "ml",
|
|
2721
|
+
gap: "gap",
|
|
2722
|
+
"row-gap": "gap-y",
|
|
2723
|
+
"column-gap": "gap-x",
|
|
2724
|
+
"font-size": "text",
|
|
2725
|
+
"font-family": "font",
|
|
2726
|
+
"border-radius": "rounded",
|
|
2727
|
+
"border-width": "border",
|
|
2728
|
+
"box-shadow": "shadow",
|
|
2729
|
+
"line-height": "leading",
|
|
2730
|
+
"letter-spacing": "tracking"
|
|
2731
|
+
};
|
|
2732
|
+
function convertVarReference(declaration) {
|
|
2733
|
+
const match = declaration.value.match(/^var\(\s*(--[\w-]+)\s*\)$/);
|
|
2734
|
+
if (!match?.[1]) return void 0;
|
|
2735
|
+
const prefix = varPrefixes[declaration.property];
|
|
2736
|
+
if (prefix) return `${prefix}-(${match[1]})`;
|
|
2737
|
+
}
|
|
2738
|
+
const gradientDirections = {
|
|
2739
|
+
"to right": "bg-linear-to-r",
|
|
2740
|
+
"to left": "bg-linear-to-l",
|
|
2741
|
+
"to top": "bg-linear-to-t",
|
|
2742
|
+
"to bottom": "bg-linear-to-b",
|
|
2743
|
+
"to top right": "bg-linear-to-tr",
|
|
2744
|
+
"to top left": "bg-linear-to-tl",
|
|
2745
|
+
"to bottom right": "bg-linear-to-br",
|
|
2746
|
+
"to bottom left": "bg-linear-to-bl"
|
|
2747
|
+
};
|
|
2748
|
+
function convertGradient(declaration, options) {
|
|
2749
|
+
if (declaration.property !== "background-image" && declaration.property !== "background") return void 0;
|
|
2750
|
+
const linearMatch = declaration.value.trim().match(/^linear-gradient\((.+)\)$/);
|
|
2751
|
+
if (!linearMatch?.[1]) return void 0;
|
|
2752
|
+
const inner = linearMatch[1];
|
|
2753
|
+
const firstComma = findTopLevelComma(inner);
|
|
2754
|
+
if (firstComma === -1) return void 0;
|
|
2755
|
+
const directionPart = inner.slice(0, firstComma).trim();
|
|
2756
|
+
const colorsPart = inner.slice(firstComma + 1).trim();
|
|
2757
|
+
const dirClass = gradientDirections[directionPart] ?? (directionPart.match(/^\d+deg$/) ? `bg-linear-${directionPart.replace("deg", "")}` : void 0);
|
|
2758
|
+
if (!dirClass) return void 0;
|
|
2759
|
+
const colorStops = splitGradientStops(colorsPart);
|
|
2760
|
+
if (colorStops.length < 2 || colorStops.length > 3) return void 0;
|
|
2761
|
+
const classes = [dirClass];
|
|
2762
|
+
const fromColor = matchGradientColor(colorStops[0] ?? "", "from", options);
|
|
2763
|
+
if (!fromColor) return void 0;
|
|
2764
|
+
classes.push(fromColor);
|
|
2765
|
+
if (colorStops.length === 3) {
|
|
2766
|
+
const viaColor = matchGradientColor(colorStops[1] ?? "", "via", options);
|
|
2767
|
+
if (!viaColor) return void 0;
|
|
2768
|
+
classes.push(viaColor);
|
|
2769
|
+
}
|
|
2770
|
+
const toColor = matchGradientColor(colorStops[colorStops.length - 1] ?? "", "to", options);
|
|
2771
|
+
if (!toColor) return void 0;
|
|
2772
|
+
classes.push(toColor);
|
|
2773
|
+
return classes;
|
|
2774
|
+
}
|
|
2775
|
+
function matchGradientColor(stop, prefix, options) {
|
|
2776
|
+
const color = stop.trim().split(/\s+/)[0];
|
|
2777
|
+
if (!color) return void 0;
|
|
2778
|
+
const normalized = normalizeColor(color);
|
|
2779
|
+
const keyword = colorKeywords[normalized];
|
|
2780
|
+
if (keyword) return `${prefix}-${keyword}`;
|
|
2781
|
+
const hexToken = lookupHexToken(normalized);
|
|
2782
|
+
if (hexToken) return `${prefix}-${hexToken}`;
|
|
2783
|
+
const token = Object.entries(options.theme.colors).find(([, c]) => normalizeColor(c) === normalized)?.[0];
|
|
2784
|
+
if (token) return `${prefix}-${token}`;
|
|
2785
|
+
return `${prefix}-[${color}]`;
|
|
2786
|
+
}
|
|
2787
|
+
function findTopLevelComma(value) {
|
|
2788
|
+
let depth = 0;
|
|
2789
|
+
for (let i = 0; i < value.length; i++) if (value[i] === "(") depth++;
|
|
2790
|
+
else if (value[i] === ")") depth--;
|
|
2791
|
+
else if (value[i] === "," && depth === 0) return i;
|
|
2792
|
+
return -1;
|
|
2793
|
+
}
|
|
2794
|
+
function splitGradientStops(value) {
|
|
2795
|
+
const stops = [];
|
|
2796
|
+
let depth = 0;
|
|
2797
|
+
let start = 0;
|
|
2798
|
+
for (let i = 0; i < value.length; i++) if (value[i] === "(") depth++;
|
|
2799
|
+
else if (value[i] === ")") depth--;
|
|
2800
|
+
else if (value[i] === "," && depth === 0) {
|
|
2801
|
+
stops.push(value.slice(start, i).trim());
|
|
2802
|
+
start = i + 1;
|
|
2803
|
+
}
|
|
2804
|
+
stops.push(value.slice(start).trim());
|
|
2805
|
+
return stops.filter(Boolean);
|
|
2806
|
+
}
|
|
2626
2807
|
function converted(declaration, className, kind) {
|
|
2627
2808
|
const important = declaration.important ? "!" : "";
|
|
2628
2809
|
const variants = declaration.variants.length > 0 ? `${declaration.variants.join(":")}:` : "";
|
|
@@ -3482,8 +3663,6 @@ const defaultTheme = {
|
|
|
3482
3663
|
};
|
|
3483
3664
|
function resolveOptions(options = {}) {
|
|
3484
3665
|
return {
|
|
3485
|
-
mode: options.mode ?? "pretty",
|
|
3486
|
-
tailwindVersion: "4",
|
|
3487
3666
|
theme: {
|
|
3488
3667
|
spacing: {
|
|
3489
3668
|
...defaultTheme.spacing,
|
|
@@ -3496,7 +3675,6 @@ function resolveOptions(options = {}) {
|
|
|
3496
3675
|
},
|
|
3497
3676
|
allowArbitraryValues: options.allowArbitraryValues ?? true,
|
|
3498
3677
|
allowArbitraryProperties: options.allowArbitraryProperties ?? true,
|
|
3499
|
-
preferThemeTokens: options.preferThemeTokens ?? true,
|
|
3500
3678
|
compression: options.compression ?? "safe",
|
|
3501
3679
|
sort: options.sort ?? "grouped",
|
|
3502
3680
|
important: options.important ?? false,
|
|
@@ -3506,40 +3684,27 @@ function resolveOptions(options = {}) {
|
|
|
3506
3684
|
}
|
|
3507
3685
|
//#endregion
|
|
3508
3686
|
//#region src/index.ts
|
|
3509
|
-
function
|
|
3510
|
-
const
|
|
3511
|
-
const
|
|
3687
|
+
function convert(input, options) {
|
|
3688
|
+
const resolved = resolveOptions(options);
|
|
3689
|
+
const pairs = expandShorthands(normalizeInput(input)).map((declaration) => ({
|
|
3512
3690
|
declaration,
|
|
3513
|
-
converted: convertDeclaration(declaration,
|
|
3691
|
+
converted: convertDeclaration(declaration, resolved)
|
|
3514
3692
|
}));
|
|
3515
|
-
const
|
|
3693
|
+
const sorted = sortConverted(compressConverted(pairs.flatMap(({ converted }) => {
|
|
3516
3694
|
if (!converted) return [];
|
|
3517
3695
|
return Array.isArray(converted) ? converted : [converted];
|
|
3518
|
-
}),
|
|
3519
|
-
const exact = convertedDeclarations.filter((converted) => converted.kind === "exact");
|
|
3520
|
-
const arbitrary = convertedDeclarations.filter((converted) => converted.kind === "arbitrary");
|
|
3521
|
-
const unmatched = conversionPairs.flatMap(({ declaration, converted }) => converted ? [] : [declaration]);
|
|
3522
|
-
const classes = convertedDeclarations.map(({ className }) => className);
|
|
3696
|
+
}), resolved), resolved);
|
|
3523
3697
|
return {
|
|
3524
|
-
className:
|
|
3525
|
-
classes,
|
|
3526
|
-
exact,
|
|
3527
|
-
arbitrary,
|
|
3528
|
-
unmatched,
|
|
3529
|
-
warnings: unmatched.map((declaration) => ({
|
|
3530
|
-
declaration,
|
|
3531
|
-
message: "No Tailwind utility or fallback could be emitted."
|
|
3532
|
-
}))
|
|
3698
|
+
className: sorted.map(({ className }) => className).join(" "),
|
|
3699
|
+
classes: sorted.map(({ className }) => className),
|
|
3700
|
+
exact: sorted.filter((c) => c.kind === "exact"),
|
|
3701
|
+
arbitrary: sorted.filter((c) => c.kind === "arbitrary"),
|
|
3702
|
+
unmatched: pairs.flatMap(({ declaration, converted }) => converted ? [] : [declaration])
|
|
3533
3703
|
};
|
|
3534
3704
|
}
|
|
3535
|
-
function
|
|
3536
|
-
return
|
|
3537
|
-
}
|
|
3538
|
-
function styleToClasses(input, options) {
|
|
3539
|
-
return styleToTailwind(input, options).classes;
|
|
3540
|
-
}
|
|
3541
|
-
function cssTextToTailwind(cssText, options) {
|
|
3542
|
-
return styleToTailwind(cssText, options);
|
|
3705
|
+
function twirl(input, options) {
|
|
3706
|
+
return convert(input, options).className;
|
|
3543
3707
|
}
|
|
3708
|
+
twirl.convert = convert;
|
|
3544
3709
|
//#endregion
|
|
3545
|
-
export {
|
|
3710
|
+
export { twirl };
|