@cwcss/crosswind 0.1.5 → 0.2.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.
Files changed (87) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +52 -0
  3. package/dist/bin/cli.js +14615 -0
  4. package/dist/build.d.ts +24 -0
  5. package/dist/config.d.ts +5 -0
  6. package/dist/generator.d.ts +31 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/parser.d.ts +42 -0
  9. package/dist/plugin.d.ts +22 -0
  10. package/dist/preflight-forms.d.ts +5 -0
  11. package/dist/preflight.d.ts +2 -0
  12. package/dist/rules-advanced.d.ts +27 -0
  13. package/dist/rules-effects.d.ts +25 -0
  14. package/dist/rules-forms.d.ts +7 -0
  15. package/dist/rules-grid.d.ts +13 -0
  16. package/dist/rules-interactivity.d.ts +41 -0
  17. package/dist/rules-layout.d.ts +26 -0
  18. package/dist/rules-transforms.d.ts +33 -0
  19. package/dist/rules-typography.d.ts +41 -0
  20. package/dist/rules.d.ts +39 -0
  21. package/dist/scanner.d.ts +18 -0
  22. package/dist/src/index.js +12848 -0
  23. package/dist/transformer-compile-class.d.ts +37 -0
  24. package/{src/types.ts → dist/types.d.ts} +17 -86
  25. package/package.json +2 -16
  26. package/PLUGIN.md +0 -235
  27. package/benchmark/framework-comparison.bench.ts +0 -850
  28. package/bin/cli.ts +0 -365
  29. package/bin/crosswind +0 -0
  30. package/bin/headwind +0 -0
  31. package/build.ts +0 -8
  32. package/crosswind.config.ts +0 -9
  33. package/example/comprehensive.html +0 -70
  34. package/example/index.html +0 -21
  35. package/example/output.css +0 -236
  36. package/examples/plugin/README.md +0 -112
  37. package/examples/plugin/build.ts +0 -32
  38. package/examples/plugin/src/index.html +0 -34
  39. package/examples/plugin/src/index.ts +0 -7
  40. package/headwind +0 -2
  41. package/src/build.ts +0 -101
  42. package/src/config.ts +0 -529
  43. package/src/generator.ts +0 -2173
  44. package/src/index.ts +0 -10
  45. package/src/parser.ts +0 -1471
  46. package/src/plugin.ts +0 -118
  47. package/src/preflight-forms.ts +0 -229
  48. package/src/preflight.ts +0 -388
  49. package/src/rules-advanced.ts +0 -477
  50. package/src/rules-effects.ts +0 -461
  51. package/src/rules-forms.ts +0 -103
  52. package/src/rules-grid.ts +0 -241
  53. package/src/rules-interactivity.ts +0 -525
  54. package/src/rules-layout.ts +0 -385
  55. package/src/rules-transforms.ts +0 -412
  56. package/src/rules-typography.ts +0 -486
  57. package/src/rules.ts +0 -809
  58. package/src/scanner.ts +0 -84
  59. package/src/transformer-compile-class.ts +0 -275
  60. package/test/advanced-features.test.ts +0 -911
  61. package/test/arbitrary.test.ts +0 -396
  62. package/test/attributify.test.ts +0 -592
  63. package/test/bracket-syntax.test.ts +0 -1133
  64. package/test/build.test.ts +0 -99
  65. package/test/colors.test.ts +0 -934
  66. package/test/flexbox.test.ts +0 -669
  67. package/test/generator.test.ts +0 -597
  68. package/test/grid.test.ts +0 -584
  69. package/test/layout.test.ts +0 -404
  70. package/test/modifiers.test.ts +0 -417
  71. package/test/parser.test.ts +0 -564
  72. package/test/performance-regression.test.ts +0 -376
  73. package/test/performance.test.ts +0 -568
  74. package/test/plugin.test.ts +0 -160
  75. package/test/scanner.test.ts +0 -94
  76. package/test/sizing.test.ts +0 -481
  77. package/test/spacing.test.ts +0 -394
  78. package/test/transformer-compile-class.test.ts +0 -287
  79. package/test/transforms.test.ts +0 -448
  80. package/test/typography.test.ts +0 -632
  81. package/test/variants-form-states.test.ts +0 -225
  82. package/test/variants-group-peer.test.ts +0 -66
  83. package/test/variants-media.test.ts +0 -213
  84. package/test/variants-positional.test.ts +0 -58
  85. package/test/variants-pseudo-elements.test.ts +0 -47
  86. package/test/variants-state.test.ts +0 -62
  87. package/tsconfig.json +0 -18
@@ -1,850 +0,0 @@
1
- /* eslint-disable no-console */
2
- import { bench, group, run } from 'mitata'
3
- import { CSSGenerator } from '../src/generator'
4
- import { defaultConfig } from '../src/config'
5
-
6
- // UnoCSS setup
7
- import { createGenerator } from '@unocss/core'
8
- import presetWind from '@unocss/preset-wind'
9
-
10
- // Tailwind CSS v3 setup
11
- import postcss from 'postcss'
12
- import tailwindcss from 'tailwindcss'
13
-
14
- // Tailwind CSS v4 setup
15
- import tailwindcssV4 from '@tailwindcss/postcss'
16
-
17
- // Initialize UnoCSS generator
18
- const unoGen = await createGenerator({
19
- presets: [presetWind()],
20
- })
21
-
22
- // Initialize Tailwind CSS v3 processor
23
- const tailwindV3Processor = postcss([
24
- tailwindcss({
25
- content: [{ raw: '', extension: 'html' }],
26
- corePlugins: { preflight: false },
27
- }),
28
- ])
29
-
30
- // Initialize Tailwind CSS v4 processor
31
- const tailwindV4Processor = postcss([tailwindcssV4()])
32
-
33
- // =============================================================================
34
- // VALID UTILITY CLASS SETS
35
- // All classes below are valid Tailwind CSS classes
36
- // =============================================================================
37
-
38
- // Simple utilities - basic layout, spacing, colors
39
- const simpleUtilities = [
40
- 'w-4', 'h-4', 'p-4', 'm-4', 'text-lg', 'bg-blue-500',
41
- 'flex', 'items-center', 'justify-between', 'rounded-lg',
42
- ]
43
-
44
- // Complex utilities with responsive and state variants
45
- const complexUtilities = [
46
- 'sm:w-full', 'md:w-1/2', 'lg:w-1/3', 'xl:w-1/4',
47
- 'hover:bg-blue-600', 'focus:ring-2', 'active:scale-95',
48
- 'dark:bg-gray-800', 'dark:text-white',
49
- 'sm:hover:bg-blue-500', 'md:dark:text-gray-200',
50
- ]
51
-
52
- // Arbitrary values - bracket notation
53
- const arbitraryValues = [
54
- 'w-[123px]', 'h-[456px]', 'text-[#ff0000]', 'p-[2rem]',
55
- 'bg-[#1a1a1a]', 'm-[10%]', 'shadow-[0_4px_6px_rgba(0,0,0,0.1)]',
56
- 'top-[50%]', 'left-[calc(100%-2rem)]', 'grid-cols-[1fr_2fr_1fr]',
57
- ]
58
-
59
- // Real-world component class combinations (as arrays for proper testing)
60
- const realWorldComponents = [
61
- // Card component
62
- ['flex', 'items-center', 'justify-between', 'p-4', 'bg-white', 'dark:bg-gray-800', 'rounded-lg', 'shadow-md'],
63
- // Container
64
- ['w-full', 'max-w-md', 'mx-auto', 'space-y-4', 'sm:space-y-6'],
65
- // Heading
66
- ['text-lg', 'font-semibold', 'text-gray-900', 'dark:text-white', 'tracking-tight'],
67
- // Grid layout
68
- ['grid', 'grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3', 'gap-4', 'p-6'],
69
- // Interactive button
70
- ['px-4', 'py-2', 'bg-blue-500', 'hover:bg-blue-600', 'text-white', 'rounded-md', 'transition-colors', 'duration-200'],
71
- // Form input
72
- ['w-full', 'px-3', 'py-2', 'border', 'border-gray-300', 'rounded-md', 'focus:ring-2', 'focus:ring-blue-500', 'focus:border-transparent'],
73
- // Navigation item
74
- ['flex', 'items-center', 'gap-2', 'px-4', 'py-2', 'text-gray-700', 'hover:text-gray-900', 'hover:bg-gray-100', 'rounded-lg'],
75
- // Badge
76
- ['inline-flex', 'items-center', 'px-2', 'py-1', 'text-xs', 'font-medium', 'bg-green-100', 'text-green-800', 'rounded-full'],
77
- ]
78
-
79
- // Valid spacing values (Tailwind's actual spacing scale)
80
- const validSpacingValues = [
81
- '0', 'px', '0.5', '1', '1.5', '2', '2.5', '3', '3.5', '4', '5', '6', '7', '8', '9', '10',
82
- '11', '12', '14', '16', '20', '24', '28', '32', '36', '40', '44', '48', '52', '56', '60',
83
- '64', '72', '80', '96',
84
- ]
85
-
86
- // Valid width/height values (includes fractions and special values)
87
- const validSizeValues = [
88
- ...validSpacingValues,
89
- 'auto', 'full', 'screen', 'min', 'max', 'fit',
90
- '1/2', '1/3', '2/3', '1/4', '2/4', '3/4',
91
- '1/5', '2/5', '3/5', '4/5',
92
- '1/6', '2/6', '3/6', '4/6', '5/6',
93
- ]
94
-
95
- // Valid color shades
96
- const validShades = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950']
97
-
98
- // Color names
99
- const colorNames = ['slate', 'gray', 'zinc', 'neutral', 'stone', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose']
100
-
101
- // Typography utilities
102
- const typographyUtilities = [
103
- // Font sizes
104
- 'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'text-4xl',
105
- // Font weights
106
- 'font-thin', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold',
107
- // Letter spacing
108
- 'tracking-tighter', 'tracking-tight', 'tracking-normal', 'tracking-wide', 'tracking-wider', 'tracking-widest',
109
- // Line height
110
- 'leading-none', 'leading-tight', 'leading-snug', 'leading-normal', 'leading-relaxed', 'leading-loose',
111
- // Text alignment
112
- 'text-left', 'text-center', 'text-right', 'text-justify',
113
- // Text decoration
114
- 'underline', 'overline', 'line-through', 'no-underline',
115
- // Text transform
116
- 'uppercase', 'lowercase', 'capitalize', 'normal-case',
117
- // Whitespace
118
- 'whitespace-normal', 'whitespace-nowrap', 'whitespace-pre', 'whitespace-pre-line', 'whitespace-pre-wrap',
119
- // Word break
120
- 'break-normal', 'break-words', 'break-all',
121
- ]
122
-
123
- // Flexbox utilities
124
- const flexboxUtilities = [
125
- // Display
126
- 'flex', 'inline-flex',
127
- // Direction
128
- 'flex-row', 'flex-row-reverse', 'flex-col', 'flex-col-reverse',
129
- // Wrap
130
- 'flex-wrap', 'flex-wrap-reverse', 'flex-nowrap',
131
- // Flex values
132
- 'flex-1', 'flex-auto', 'flex-initial', 'flex-none',
133
- // Grow/Shrink
134
- 'grow', 'grow-0', 'shrink', 'shrink-0',
135
- // Justify content
136
- 'justify-start', 'justify-end', 'justify-center', 'justify-between', 'justify-around', 'justify-evenly',
137
- // Align items
138
- 'items-start', 'items-end', 'items-center', 'items-baseline', 'items-stretch',
139
- // Align content
140
- 'content-start', 'content-end', 'content-center', 'content-between', 'content-around', 'content-evenly',
141
- // Align self
142
- 'self-auto', 'self-start', 'self-end', 'self-center', 'self-stretch',
143
- // Gap
144
- 'gap-0', 'gap-1', 'gap-2', 'gap-4', 'gap-6', 'gap-8',
145
- 'gap-x-4', 'gap-y-4',
146
- ]
147
-
148
- // Grid utilities
149
- const gridUtilities = [
150
- // Display
151
- 'grid', 'inline-grid',
152
- // Grid template columns
153
- 'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5', 'grid-cols-6',
154
- 'grid-cols-12', 'grid-cols-none',
155
- // Grid template rows
156
- 'grid-rows-1', 'grid-rows-2', 'grid-rows-3', 'grid-rows-4', 'grid-rows-5', 'grid-rows-6', 'grid-rows-none',
157
- // Grid column span
158
- 'col-auto', 'col-span-1', 'col-span-2', 'col-span-3', 'col-span-4', 'col-span-6', 'col-span-12', 'col-span-full',
159
- // Grid row span
160
- 'row-auto', 'row-span-1', 'row-span-2', 'row-span-3', 'row-span-4', 'row-span-6', 'row-span-full',
161
- // Grid flow
162
- 'grid-flow-row', 'grid-flow-col', 'grid-flow-row-dense', 'grid-flow-col-dense',
163
- // Auto columns/rows
164
- 'auto-cols-auto', 'auto-cols-min', 'auto-cols-max', 'auto-cols-fr',
165
- 'auto-rows-auto', 'auto-rows-min', 'auto-rows-max', 'auto-rows-fr',
166
- // Place content/items/self
167
- 'place-content-center', 'place-content-start', 'place-content-end',
168
- 'place-items-center', 'place-items-start', 'place-items-end',
169
- 'place-self-center', 'place-self-start', 'place-self-end',
170
- ]
171
-
172
- // Stacked/chained variants (multiple variants combined)
173
- const stackedVariants = [
174
- // Responsive + state
175
- 'sm:hover:bg-blue-500', 'md:hover:bg-blue-600', 'lg:hover:bg-blue-700',
176
- 'sm:focus:ring-2', 'md:focus:ring-4', 'lg:focus:ring-8',
177
- // Responsive + dark mode
178
- 'sm:dark:bg-gray-800', 'md:dark:bg-gray-900', 'lg:dark:bg-black',
179
- 'sm:dark:text-white', 'md:dark:text-gray-100', 'lg:dark:text-gray-200',
180
- // Dark mode + state
181
- 'dark:hover:bg-gray-700', 'dark:focus:ring-blue-400', 'dark:active:bg-gray-600',
182
- // Triple stacked: responsive + dark + state
183
- 'sm:dark:hover:bg-gray-600', 'md:dark:hover:bg-gray-700', 'lg:dark:hover:bg-gray-800',
184
- 'sm:dark:focus:ring-2', 'md:dark:focus:ring-4',
185
- // Group variants
186
- 'group-hover:opacity-100', 'group-hover:visible', 'group-hover:scale-105',
187
- 'group-focus:ring-2', 'group-focus:border-blue-500',
188
- // Peer variants
189
- 'peer-focus:ring-2', 'peer-checked:bg-blue-500', 'peer-invalid:border-red-500',
190
- // First/last/odd/even
191
- 'first:pt-0', 'last:pb-0', 'odd:bg-gray-50', 'even:bg-white',
192
- 'first:rounded-t-lg', 'last:rounded-b-lg',
193
- // Responsive + first/last
194
- 'sm:first:pt-0', 'md:last:pb-0', 'lg:odd:bg-gray-100',
195
- ]
196
-
197
- // Transform and transition utilities
198
- const transformUtilities = [
199
- // Scale
200
- 'scale-0', 'scale-50', 'scale-75', 'scale-90', 'scale-95', 'scale-100', 'scale-105', 'scale-110', 'scale-125', 'scale-150',
201
- 'scale-x-100', 'scale-y-100',
202
- // Rotate
203
- 'rotate-0', 'rotate-1', 'rotate-2', 'rotate-3', 'rotate-6', 'rotate-12', 'rotate-45', 'rotate-90', 'rotate-180',
204
- '-rotate-1', '-rotate-2', '-rotate-3', '-rotate-6', '-rotate-12', '-rotate-45', '-rotate-90', '-rotate-180',
205
- // Translate
206
- 'translate-x-0', 'translate-x-1', 'translate-x-2', 'translate-x-4', 'translate-x-full', '-translate-x-full',
207
- 'translate-y-0', 'translate-y-1', 'translate-y-2', 'translate-y-4', 'translate-y-full', '-translate-y-full',
208
- // Skew
209
- 'skew-x-0', 'skew-x-1', 'skew-x-2', 'skew-x-3', 'skew-x-6', 'skew-x-12',
210
- 'skew-y-0', 'skew-y-1', 'skew-y-2', 'skew-y-3', 'skew-y-6', 'skew-y-12',
211
- // Origin
212
- 'origin-center', 'origin-top', 'origin-top-right', 'origin-right', 'origin-bottom-right',
213
- 'origin-bottom', 'origin-bottom-left', 'origin-left', 'origin-top-left',
214
- ]
215
-
216
- const transitionUtilities = [
217
- // Transition property
218
- 'transition-none', 'transition-all', 'transition', 'transition-colors', 'transition-opacity', 'transition-shadow', 'transition-transform',
219
- // Duration
220
- 'duration-75', 'duration-100', 'duration-150', 'duration-200', 'duration-300', 'duration-500', 'duration-700', 'duration-1000',
221
- // Timing function
222
- 'ease-linear', 'ease-in', 'ease-out', 'ease-in-out',
223
- // Delay
224
- 'delay-75', 'delay-100', 'delay-150', 'delay-200', 'delay-300', 'delay-500', 'delay-700', 'delay-1000',
225
- // Animation
226
- 'animate-none', 'animate-spin', 'animate-ping', 'animate-pulse', 'animate-bounce',
227
- ]
228
-
229
- // Border and ring utilities
230
- const borderUtilities = [
231
- // Border width
232
- 'border', 'border-0', 'border-2', 'border-4', 'border-8',
233
- 'border-t', 'border-r', 'border-b', 'border-l',
234
- 'border-t-0', 'border-r-0', 'border-b-0', 'border-l-0',
235
- 'border-t-2', 'border-r-2', 'border-b-2', 'border-l-2',
236
- // Border style
237
- 'border-solid', 'border-dashed', 'border-dotted', 'border-double', 'border-none',
238
- // Border radius
239
- 'rounded-none', 'rounded-sm', 'rounded', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-3xl', 'rounded-full',
240
- 'rounded-t-lg', 'rounded-r-lg', 'rounded-b-lg', 'rounded-l-lg',
241
- 'rounded-tl-lg', 'rounded-tr-lg', 'rounded-br-lg', 'rounded-bl-lg',
242
- // Ring
243
- 'ring-0', 'ring-1', 'ring-2', 'ring-4', 'ring-8', 'ring',
244
- 'ring-inset',
245
- // Ring offset
246
- 'ring-offset-0', 'ring-offset-1', 'ring-offset-2', 'ring-offset-4', 'ring-offset-8',
247
- ]
248
-
249
- // Shadow and opacity utilities
250
- const effectUtilities = [
251
- // Box shadow
252
- 'shadow-sm', 'shadow', 'shadow-md', 'shadow-lg', 'shadow-xl', 'shadow-2xl', 'shadow-inner', 'shadow-none',
253
- // Opacity
254
- 'opacity-0', 'opacity-5', 'opacity-10', 'opacity-20', 'opacity-25', 'opacity-30', 'opacity-40', 'opacity-50',
255
- 'opacity-60', 'opacity-70', 'opacity-75', 'opacity-80', 'opacity-90', 'opacity-95', 'opacity-100',
256
- // Mix blend mode
257
- 'mix-blend-normal', 'mix-blend-multiply', 'mix-blend-screen', 'mix-blend-overlay',
258
- // Background blend mode
259
- 'bg-blend-normal', 'bg-blend-multiply', 'bg-blend-screen', 'bg-blend-overlay',
260
- ]
261
-
262
- // =============================================================================
263
- // BENCHMARK HELPERS
264
- // =============================================================================
265
-
266
- async function benchmarkUnoCSS(classes: string[]): Promise<void> {
267
- await unoGen.generate(classes.join(' '))
268
- }
269
-
270
- async function benchmarkTailwindV3(classes: string[]): Promise<void> {
271
- const classString = classes.join(' ')
272
- const css = `
273
- @tailwind utilities;
274
- @layer utilities {
275
- .test { @apply ${classString}; }
276
- }
277
- `
278
- await tailwindV3Processor.process(css, { from: undefined })
279
- }
280
-
281
- async function benchmarkTailwindV4(classes: string[]): Promise<void> {
282
- const selectors = classes.map(cls =>
283
- `.${cls.replace(/:/g, '\\:').replace(/\[/g, '\\[').replace(/]/g, '\\]').replace(/\//g, '\\/').replace(/\./g, '\\.').replace(/-\\/g, '-\\\\')}`
284
- ).join(',\n')
285
-
286
- const css = `
287
- @tailwind utilities;
288
-
289
- ${selectors} {
290
- /* Trigger utility generation */
291
- }
292
- `
293
- await tailwindV4Processor.process(css, { from: undefined })
294
- }
295
-
296
- function benchmarkCrosswind(classes: string[]): void {
297
- const gen = new CSSGenerator(defaultConfig)
298
- for (const cls of classes) {
299
- gen.generate(cls)
300
- }
301
- }
302
-
303
- function benchmarkCrosswindWithOutput(classes: string[]): string {
304
- const gen = new CSSGenerator(defaultConfig)
305
- for (const cls of classes) {
306
- gen.generate(cls)
307
- }
308
- return gen.toCSS(false)
309
- }
310
-
311
- // =============================================================================
312
- // BENCHMARKS
313
- // =============================================================================
314
-
315
- // 1. Simple utilities benchmark
316
- group('Simple Utilities (10 classes)', () => {
317
- bench('Crosswind', () => {
318
- benchmarkCrosswind(simpleUtilities)
319
- })
320
-
321
- bench('UnoCSS', async () => {
322
- await benchmarkUnoCSS(simpleUtilities)
323
- })
324
-
325
- bench('Tailwind CSS v3', async () => {
326
- await benchmarkTailwindV3(simpleUtilities)
327
- })
328
-
329
- bench('Tailwind CSS v4', async () => {
330
- await benchmarkTailwindV4(simpleUtilities)
331
- })
332
- })
333
-
334
- // 2. Complex utilities with variants benchmark
335
- group('Complex Utilities with Variants (11 classes)', () => {
336
- bench('Crosswind', () => {
337
- benchmarkCrosswind(complexUtilities)
338
- })
339
-
340
- bench('UnoCSS', async () => {
341
- await benchmarkUnoCSS(complexUtilities)
342
- })
343
-
344
- bench('Tailwind CSS v3', async () => {
345
- await benchmarkTailwindV3(complexUtilities)
346
- })
347
-
348
- bench('Tailwind CSS v4', async () => {
349
- await benchmarkTailwindV4(complexUtilities)
350
- })
351
- })
352
-
353
- // 3. Arbitrary values benchmark
354
- group('Arbitrary Values (10 classes)', () => {
355
- bench('Crosswind', () => {
356
- benchmarkCrosswind(arbitraryValues)
357
- })
358
-
359
- bench('UnoCSS', async () => {
360
- await benchmarkUnoCSS(arbitraryValues)
361
- })
362
-
363
- bench('Tailwind CSS v3', async () => {
364
- await benchmarkTailwindV3(arbitraryValues)
365
- })
366
-
367
- bench('Tailwind CSS v4', async () => {
368
- await benchmarkTailwindV4(arbitraryValues)
369
- })
370
- })
371
-
372
- // 4. Real-world component classes (FIXED: properly splits class strings)
373
- group('Real-world Components (8 components, ~60 classes)', () => {
374
- const allClasses = realWorldComponents.flat()
375
-
376
- bench('Crosswind', () => {
377
- const gen = new CSSGenerator(defaultConfig)
378
- for (const cls of allClasses) {
379
- gen.generate(cls)
380
- }
381
- })
382
-
383
- bench('UnoCSS', async () => {
384
- await unoGen.generate(allClasses.join(' '))
385
- })
386
-
387
- bench('Tailwind CSS v3', async () => {
388
- await benchmarkTailwindV3(allClasses)
389
- })
390
-
391
- bench('Tailwind CSS v4', async () => {
392
- await benchmarkTailwindV4(allClasses)
393
- })
394
- })
395
-
396
- // 5. Large scale with VALID classes
397
- group('Large Scale: Valid Utilities (500 classes)', () => {
398
- const largeSet: string[] = []
399
-
400
- // Width utilities with valid values
401
- for (const size of validSizeValues.slice(0, 25)) {
402
- largeSet.push(`w-${size}`)
403
- }
404
- // Height utilities with valid values
405
- for (const size of validSizeValues.slice(0, 25)) {
406
- largeSet.push(`h-${size}`)
407
- }
408
- // Padding utilities with valid values
409
- for (const size of validSpacingValues) {
410
- largeSet.push(`p-${size}`)
411
- }
412
- // Margin utilities with valid values
413
- for (const size of validSpacingValues) {
414
- largeSet.push(`m-${size}`)
415
- }
416
- // Gap utilities
417
- for (const size of validSpacingValues.slice(0, 20)) {
418
- largeSet.push(`gap-${size}`)
419
- }
420
- // Padding x/y
421
- for (const size of validSpacingValues.slice(0, 20)) {
422
- largeSet.push(`px-${size}`, `py-${size}`)
423
- }
424
- // Margin x/y
425
- for (const size of validSpacingValues.slice(0, 20)) {
426
- largeSet.push(`mx-${size}`, `my-${size}`)
427
- }
428
- // Top/right/bottom/left
429
- for (const size of validSpacingValues.slice(0, 15)) {
430
- largeSet.push(`top-${size}`, `right-${size}`, `bottom-${size}`, `left-${size}`)
431
- }
432
-
433
- bench('Crosswind', () => {
434
- benchmarkCrosswind(largeSet)
435
- })
436
-
437
- bench('UnoCSS', async () => {
438
- await benchmarkUnoCSS(largeSet)
439
- })
440
-
441
- bench('Tailwind CSS v3', async () => {
442
- await benchmarkTailwindV3(largeSet)
443
- })
444
-
445
- bench('Tailwind CSS v4', async () => {
446
- await benchmarkTailwindV4(largeSet)
447
- })
448
- })
449
-
450
- // 6. CSS output generation benchmark
451
- group('CSS Output Generation (1000 arbitrary values)', () => {
452
- const utilities: string[] = []
453
- for (let i = 0; i < 1000; i++) {
454
- utilities.push(`w-[${i}px]`)
455
- }
456
-
457
- bench('Crosswind', () => {
458
- benchmarkCrosswindWithOutput(utilities)
459
- })
460
-
461
- bench('UnoCSS', async () => {
462
- const result = await unoGen.generate(utilities.join(' '))
463
- result.css
464
- })
465
-
466
- bench('Tailwind CSS v3', async () => {
467
- await benchmarkTailwindV3(utilities)
468
- })
469
-
470
- bench('Tailwind CSS v4', async () => {
471
- await benchmarkTailwindV4(utilities)
472
- })
473
- })
474
-
475
- // 7. Color utilities benchmark (VALID shades)
476
- group('Color Utilities (330 classes)', () => {
477
- const colorUtilities: string[] = []
478
-
479
- // Use subset of colors to keep benchmark reasonable
480
- const colors = ['gray', 'red', 'blue', 'green', 'yellow', 'purple', 'pink', 'indigo', 'cyan', 'emerald']
481
-
482
- for (const color of colors) {
483
- for (const shade of validShades) {
484
- colorUtilities.push(`bg-${color}-${shade}`)
485
- colorUtilities.push(`text-${color}-${shade}`)
486
- colorUtilities.push(`border-${color}-${shade}`)
487
- }
488
- }
489
-
490
- bench('Crosswind', () => {
491
- benchmarkCrosswind(colorUtilities)
492
- })
493
-
494
- bench('UnoCSS', async () => {
495
- await benchmarkUnoCSS(colorUtilities)
496
- })
497
-
498
- bench('Tailwind CSS v3', async () => {
499
- await benchmarkTailwindV3(colorUtilities)
500
- })
501
-
502
- bench('Tailwind CSS v4', async () => {
503
- await benchmarkTailwindV4(colorUtilities)
504
- })
505
- })
506
-
507
- // 8. Responsive utilities with VALID values
508
- group('Responsive Utilities (500 classes)', () => {
509
- const breakpoints = ['sm', 'md', 'lg', 'xl', '2xl']
510
- const responsiveUtilities: string[] = []
511
-
512
- // Valid width values with responsive prefixes
513
- for (const bp of breakpoints) {
514
- for (const size of validSizeValues.slice(0, 20)) {
515
- responsiveUtilities.push(`${bp}:w-${size}`)
516
- }
517
- }
518
- // Valid padding values with responsive prefixes
519
- for (const bp of breakpoints) {
520
- for (const size of validSpacingValues.slice(0, 15)) {
521
- responsiveUtilities.push(`${bp}:p-${size}`)
522
- }
523
- }
524
- // Text sizes with responsive prefixes
525
- const textSizes = ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl']
526
- for (const bp of breakpoints) {
527
- for (const size of textSizes) {
528
- responsiveUtilities.push(`${bp}:text-${size}`)
529
- }
530
- }
531
-
532
- bench('Crosswind', () => {
533
- benchmarkCrosswind(responsiveUtilities)
534
- })
535
-
536
- bench('UnoCSS', async () => {
537
- await benchmarkUnoCSS(responsiveUtilities)
538
- })
539
-
540
- bench('Tailwind CSS v3', async () => {
541
- await benchmarkTailwindV3(responsiveUtilities)
542
- })
543
-
544
- bench('Tailwind CSS v4', async () => {
545
- await benchmarkTailwindV4(responsiveUtilities)
546
- })
547
- })
548
-
549
- // 9. Interactive states with VALID color shades
550
- group('Interactive States (440 classes)', () => {
551
- const states = ['hover', 'focus', 'active', 'disabled', 'first', 'last', 'odd', 'even', 'dark', 'group-hover', 'peer-focus']
552
- const stateUtilities: string[] = []
553
-
554
- // Colors with valid shades
555
- const colors = ['blue', 'gray', 'red', 'green']
556
- for (const state of states) {
557
- for (const color of colors) {
558
- for (const shade of validShades) {
559
- stateUtilities.push(`${state}:bg-${color}-${shade}`)
560
- }
561
- }
562
- }
563
-
564
- bench('Crosswind', () => {
565
- benchmarkCrosswind(stateUtilities)
566
- })
567
-
568
- bench('UnoCSS', async () => {
569
- await benchmarkUnoCSS(stateUtilities)
570
- })
571
-
572
- bench('Tailwind CSS v3', async () => {
573
- await benchmarkTailwindV3(stateUtilities)
574
- })
575
-
576
- bench('Tailwind CSS v4', async () => {
577
- await benchmarkTailwindV4(stateUtilities)
578
- })
579
- })
580
-
581
- // 10. Duplicate handling benchmark
582
- group('Duplicate Handling (6 classes x 1000 times)', () => {
583
- const duplicates = ['w-4', 'h-4', 'p-4', 'm-4', 'text-lg', 'bg-blue-500']
584
- const manyDuplicates: string[] = []
585
- for (let i = 0; i < 1000; i++) {
586
- manyDuplicates.push(...duplicates)
587
- }
588
-
589
- bench('Crosswind', () => {
590
- benchmarkCrosswind(manyDuplicates)
591
- })
592
-
593
- bench('UnoCSS', async () => {
594
- await benchmarkUnoCSS(manyDuplicates)
595
- })
596
-
597
- bench('Tailwind CSS v3', async () => {
598
- await benchmarkTailwindV3(manyDuplicates)
599
- })
600
-
601
- bench('Tailwind CSS v4', async () => {
602
- await benchmarkTailwindV4(manyDuplicates)
603
- })
604
- })
605
-
606
- // 11. Typography utilities benchmark (NEW)
607
- group('Typography Utilities (50 classes)', () => {
608
- bench('Crosswind', () => {
609
- benchmarkCrosswind(typographyUtilities)
610
- })
611
-
612
- bench('UnoCSS', async () => {
613
- await benchmarkUnoCSS(typographyUtilities)
614
- })
615
-
616
- bench('Tailwind CSS v3', async () => {
617
- await benchmarkTailwindV3(typographyUtilities)
618
- })
619
-
620
- bench('Tailwind CSS v4', async () => {
621
- await benchmarkTailwindV4(typographyUtilities)
622
- })
623
- })
624
-
625
- // 12. Flexbox utilities benchmark (NEW)
626
- group('Flexbox Utilities (50 classes)', () => {
627
- bench('Crosswind', () => {
628
- benchmarkCrosswind(flexboxUtilities)
629
- })
630
-
631
- bench('UnoCSS', async () => {
632
- await benchmarkUnoCSS(flexboxUtilities)
633
- })
634
-
635
- bench('Tailwind CSS v3', async () => {
636
- await benchmarkTailwindV3(flexboxUtilities)
637
- })
638
-
639
- bench('Tailwind CSS v4', async () => {
640
- await benchmarkTailwindV4(flexboxUtilities)
641
- })
642
- })
643
-
644
- // 13. Grid utilities benchmark (NEW)
645
- group('Grid Utilities (55 classes)', () => {
646
- bench('Crosswind', () => {
647
- benchmarkCrosswind(gridUtilities)
648
- })
649
-
650
- bench('UnoCSS', async () => {
651
- await benchmarkUnoCSS(gridUtilities)
652
- })
653
-
654
- bench('Tailwind CSS v3', async () => {
655
- await benchmarkTailwindV3(gridUtilities)
656
- })
657
-
658
- bench('Tailwind CSS v4', async () => {
659
- await benchmarkTailwindV4(gridUtilities)
660
- })
661
- })
662
-
663
- // 14. Stacked/chained variants benchmark (NEW)
664
- group('Stacked Variants (40 classes)', () => {
665
- bench('Crosswind', () => {
666
- benchmarkCrosswind(stackedVariants)
667
- })
668
-
669
- bench('UnoCSS', async () => {
670
- await benchmarkUnoCSS(stackedVariants)
671
- })
672
-
673
- bench('Tailwind CSS v3', async () => {
674
- await benchmarkTailwindV3(stackedVariants)
675
- })
676
-
677
- bench('Tailwind CSS v4', async () => {
678
- await benchmarkTailwindV4(stackedVariants)
679
- })
680
- })
681
-
682
- // 15. Transform utilities benchmark (NEW)
683
- group('Transform Utilities (55 classes)', () => {
684
- bench('Crosswind', () => {
685
- benchmarkCrosswind(transformUtilities)
686
- })
687
-
688
- bench('UnoCSS', async () => {
689
- await benchmarkUnoCSS(transformUtilities)
690
- })
691
-
692
- bench('Tailwind CSS v3', async () => {
693
- await benchmarkTailwindV3(transformUtilities)
694
- })
695
-
696
- bench('Tailwind CSS v4', async () => {
697
- await benchmarkTailwindV4(transformUtilities)
698
- })
699
- })
700
-
701
- // 16. Transition utilities benchmark (NEW)
702
- group('Transition & Animation (30 classes)', () => {
703
- bench('Crosswind', () => {
704
- benchmarkCrosswind(transitionUtilities)
705
- })
706
-
707
- bench('UnoCSS', async () => {
708
- await benchmarkUnoCSS(transitionUtilities)
709
- })
710
-
711
- bench('Tailwind CSS v3', async () => {
712
- await benchmarkTailwindV3(transitionUtilities)
713
- })
714
-
715
- bench('Tailwind CSS v4', async () => {
716
- await benchmarkTailwindV4(transitionUtilities)
717
- })
718
- })
719
-
720
- // 17. Border utilities benchmark (NEW)
721
- group('Border & Ring Utilities (45 classes)', () => {
722
- bench('Crosswind', () => {
723
- benchmarkCrosswind(borderUtilities)
724
- })
725
-
726
- bench('UnoCSS', async () => {
727
- await benchmarkUnoCSS(borderUtilities)
728
- })
729
-
730
- bench('Tailwind CSS v3', async () => {
731
- await benchmarkTailwindV3(borderUtilities)
732
- })
733
-
734
- bench('Tailwind CSS v4', async () => {
735
- await benchmarkTailwindV4(borderUtilities)
736
- })
737
- })
738
-
739
- // 18. Effects utilities benchmark (NEW)
740
- group('Shadow & Effects (35 classes)', () => {
741
- bench('Crosswind', () => {
742
- benchmarkCrosswind(effectUtilities)
743
- })
744
-
745
- bench('UnoCSS', async () => {
746
- await benchmarkUnoCSS(effectUtilities)
747
- })
748
-
749
- bench('Tailwind CSS v3', async () => {
750
- await benchmarkTailwindV3(effectUtilities)
751
- })
752
-
753
- bench('Tailwind CSS v4', async () => {
754
- await benchmarkTailwindV4(effectUtilities)
755
- })
756
- })
757
-
758
- // 19. Incremental generation benchmark (NEW - tests caching efficiency)
759
- group('Incremental Generation (add 100 classes to existing 100)', () => {
760
- const initialClasses = simpleUtilities.concat(complexUtilities).concat(typographyUtilities.slice(0, 30))
761
- const additionalClasses = flexboxUtilities.concat(gridUtilities.slice(0, 20)).concat(borderUtilities.slice(0, 30))
762
-
763
- bench('Crosswind', () => {
764
- const gen = new CSSGenerator(defaultConfig)
765
- // Generate initial classes
766
- for (const cls of initialClasses) {
767
- gen.generate(cls)
768
- }
769
- // Add more classes (tests incremental efficiency)
770
- for (const cls of additionalClasses) {
771
- gen.generate(cls)
772
- }
773
- })
774
-
775
- bench('UnoCSS', async () => {
776
- // UnoCSS handles this in one call
777
- await unoGen.generate(initialClasses.concat(additionalClasses).join(' '))
778
- })
779
-
780
- bench('Tailwind CSS v3', async () => {
781
- await benchmarkTailwindV3(initialClasses.concat(additionalClasses))
782
- })
783
-
784
- bench('Tailwind CSS v4', async () => {
785
- await benchmarkTailwindV4(initialClasses.concat(additionalClasses))
786
- })
787
- })
788
-
789
- // 20. Full realistic project simulation (NEW)
790
- group('Full Project Simulation (~800 unique classes)', () => {
791
- // Simulate a real project with diverse utility usage
792
- const projectClasses = [
793
- ...simpleUtilities,
794
- ...complexUtilities,
795
- ...arbitraryValues,
796
- ...realWorldComponents.flat(),
797
- ...typographyUtilities,
798
- ...flexboxUtilities,
799
- ...gridUtilities,
800
- ...stackedVariants,
801
- ...transformUtilities.slice(0, 30),
802
- ...transitionUtilities,
803
- ...borderUtilities,
804
- ...effectUtilities,
805
- ]
806
-
807
- // Add color utilities
808
- const colors = ['gray', 'blue', 'red', 'green']
809
- for (const color of colors) {
810
- for (const shade of validShades) {
811
- projectClasses.push(`bg-${color}-${shade}`, `text-${color}-${shade}`)
812
- }
813
- }
814
-
815
- // Add responsive variants
816
- const breakpoints = ['sm', 'md', 'lg']
817
- for (const bp of breakpoints) {
818
- projectClasses.push(
819
- `${bp}:w-full`, `${bp}:flex`, `${bp}:hidden`, `${bp}:block`,
820
- `${bp}:text-lg`, `${bp}:p-4`, `${bp}:grid-cols-2`
821
- )
822
- }
823
-
824
- bench('Crosswind', () => {
825
- benchmarkCrosswindWithOutput(projectClasses)
826
- })
827
-
828
- bench('UnoCSS', async () => {
829
- const result = await unoGen.generate(projectClasses.join(' '))
830
- result.css
831
- })
832
-
833
- bench('Tailwind CSS v3', async () => {
834
- await benchmarkTailwindV3(projectClasses)
835
- })
836
-
837
- bench('Tailwind CSS v4', async () => {
838
- await benchmarkTailwindV4(projectClasses)
839
- })
840
- })
841
-
842
- console.log('\nšŸš€ Running Comprehensive Framework Comparison Benchmarks...\n')
843
- console.log('Comparing: Crosswind vs UnoCSS vs Tailwind CSS v3 vs Tailwind CSS v4')
844
- console.log('All benchmarks use VALID Tailwind CSS utility classes\n')
845
-
846
- await run({
847
- colors: true,
848
- })
849
-
850
- console.log('\n✨ Benchmark completed!\n')