@vueland/utils-jit 0.2.0 → 0.2.1

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 (2) hide show
  1. package/README.md +32 -1069
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,1105 +4,68 @@
4
4
 
5
5
  # @vueland/utils-jit
6
6
 
7
- A lightweight Vite JIT utility engine for generating CSS from arbitrary utility classes.
7
+ Vite JIT utility engine for the Vueland platform.
8
8
 
9
- `@vueland/utils-jit` scans your project files, detects utility tokens such as `w-[320px]`, `px-[16px]`, `hover:bg-[#fff]`, and generates only the CSS that is actually used in your source code.
9
+ @vueland/utils-jit generates CSS utilities on demand from arbitrary utility classes used in your source files. It is designed for Vue/Vite projects that need a lightweight utility layer without shipping a large predefined CSS bundle.
10
10
 
11
- It is designed for Vueland and Vue 3 projects, but can be used in any Vite-based application.
11
+ ## Documentation
12
12
 
13
- ## Features
14
- - JIT CSS generation for arbitrary utility classes
15
- - Vite plugin integration
16
- - Incremental updates during development
17
- - File reference counting to remove unused generated rules
18
- - Built-in utilities for sizing, spacing, radius, position, z-index, opacity and colors
19
- - Built-in pseudo variants such as `hover:`, `focus:` and `active:`
20
- - Responsive variants through configurable breakpoints
21
- - Custom selector, attribute and media variants
22
- - Custom rules through `defineRule`
23
- - Safe value validation before CSS generation
24
- - Configurable include / exclude patterns
25
- - Configurable output file
13
+ Full documentation and examples are available here:
14
+
15
+ https://vueland.github.io/vueland/en/plugins/utils-jit/getting-started
26
16
 
27
17
  ## Installation
28
18
 
29
- ```bash
30
- pnpm add -D @vueland/utils-jit
31
- ```
19
+ bash pnpm add -D @vueland/utils-jit
32
20
 
33
- ```bash
34
- npm install -D @vueland/utils-jit
35
- ```
21
+ You also need Vite installed in your project:
36
22
 
37
- ```bash
38
- yarn add -D @vueland/utils-jit
39
- ```
23
+ bash pnpm add -D vite
40
24
 
41
25
  ## Usage
42
26
 
43
- Add `utilsJIT()` to your `vite.config.ts`.
44
-
45
- ```ts
46
- import { defineConfig } from 'vite'
47
- import vue from '@vitejs/plugins-vue'
48
- import { utilsJIT } from '@vueland/utils-jit'
49
-
50
- export default defineConfig({
51
- plugins: [
52
- vue(),
53
- utilsJIT(),
54
- ],
55
- })
56
- ```
57
-
58
- By default, the plugin generates this file:
59
-
60
- ```txt
61
- src/.generated/utils-jit.css
62
- ```
63
-
64
- Import it in your application entry file:
65
-
66
- ```ts
67
- import './.generated/utils-jit.css'
68
- ```
69
-
70
- Now you can use arbitrary utility classes in your components:
71
-
72
- ```vue
73
- <template>
74
- <div class="w-[320px] px-[16px] radius-[12px] hover:w-[360px] md:px-[24px]">
75
- Card content
76
- </div>
77
- </template>
78
- ```
79
-
80
- The generated CSS will contain only the rules used in your source files.
81
-
82
- ## Quick example
83
-
84
- ```vue
85
- <template>
86
- <div class="w-[300px] h-[200px] px-[16px] radius-[12px] z-[10]">
87
- Hello Vueland
88
- </div>
89
- </template>
90
- ```
91
-
92
- Generated CSS example:
93
-
94
- ```css
95
- /* @vueland/utils-jit: generated utilities */
96
- .h-\[200px\]{height: 200px !important;}
97
- .px-\[16px\]{padding-left: 16px !important;padding-right: 16px !important;}
98
- .radius-\[12px\]{border-radius: 12px !important;}
99
- .w-\[300px\]{width: 300px !important;}
100
- .z-\[10\]{z-index: 10 !important;}
101
- ```
102
-
103
- Generated rules are sorted by utility token for stable output.
104
-
105
- ## Built-in utilities
106
-
107
- | Utility | CSS property | Example |
108
- | --- | --- | --- |
109
- | `w-[value]` | `width` | `w-[320px]` |
110
- | `h-[value]` | `height` | `h-[200px]` |
111
- | `min-w-[value]` | `min-width` | `min-w-[240px]` |
112
- | `max-w-[value]` | `max-width` | `max-w-[1200px]` |
113
- | `min-h-[value]` | `min-height` | `min-h-[100vh]` |
114
- | `max-h-[value]` | `max-height` | `max-h-[600px]` |
115
- | `ma-[value]` | `margin` | `ma-[16px]` |
116
- | `mx-[value]` | `margin-left`, `margin-right` | `mx-[auto]` |
117
- | `my-[value]` | `margin-top`, `margin-bottom` | `my-[24px]` |
118
- | `mt-[value]` | `margin-top` | `mt-[16px]` |
119
- | `mr-[value]` | `margin-right` | `mr-[12px]` |
120
- | `mb-[value]` | `margin-bottom` | `mb-[24px]` |
121
- | `ml-[value]` | `margin-left` | `ml-[12px]` |
122
- | `pa-[value]` | `padding` | `pa-[20px]` |
123
- | `px-[value]` | `padding-left`, `padding-right` | `px-[16px]` |
124
- | `py-[value]` | `padding-top`, `padding-bottom` | `py-[12px]` |
125
- | `pt-[value]` | `padding-top` | `pt-[10px]` |
126
- | `pr-[value]` | `padding-right` | `pr-[8px]` |
127
- | `pb-[value]` | `padding-bottom` | `pb-[20px]` |
128
- | `pl-[value]` | `padding-left` | `pl-[16px]` |
129
- | `left-[value]` | `left` | `left-[12px]` |
130
- | `right-[value]` | `right` | `right-[12px]` |
131
- | `top-[value]` | `top` | `top-[12px]` |
132
- | `bottom-[value]` | `bottom` | `bottom-[12px]` |
133
- | `inset-[value]` | `inset` | `inset-[0px]` |
134
- | `radius-[value]` | `border-radius` | `radius-[12px]` |
135
- | `radius-tl-[value]` | `border-top-left-radius` | `radius-tl-[8px]` |
136
- | `radius-tr-[value]` | `border-top-right-radius` | `radius-tr-[8px]` |
137
- | `radius-bl-[value]` | `border-bottom-left-radius` | `radius-bl-[8px]` |
138
- | `radius-br-[value]` | `border-bottom-right-radius` | `radius-br-[8px]` |
139
- | `z-[value]` | `z-index` | `z-[100]` |
140
- | `opacity-[value]` | `opacity` | `opacity-[0.64]` |
141
- | `color-[value]` | `color` | `color-[#111]` |
142
- | `bg-[value]` | `background-color` | `bg-[#fff]` |
143
-
144
- Built-in rules generate declarations with `!important`.
145
-
146
- ## Supported values
147
-
148
- Values are validated before CSS is generated. Invalid or unsafe values are ignored.
149
-
150
- ### Size, padding, radius and position
151
-
152
- The following utilities support length-like values:
153
-
154
- - `w`
155
- - `h`
156
- - `min-w`
157
- - `max-w`
158
- - `min-h`
159
- - `max-h`
160
- - `pa`
161
- - `px`
162
- - `py`
163
- - `pt`
164
- - `pr`
165
- - `pb`
166
- - `pl`
167
- - `radius`
168
- - `left`
169
- - `right`
170
- - `top`
171
- - `bottom`
172
- - `inset`
173
-
174
- Examples:
175
-
176
- ```html
177
- <div class="w-[320px]"></div>
178
- <div class="h-[50%]"></div>
179
- <div class="px-[1rem]"></div>
180
- <div class="radius-[12px]"></div>
181
- <div class="left-[10vw]"></div>
182
- <div class="w-[calc(100%-32px)]"></div>
183
- <div class="max-w-[clamp(320px,50vw,960px)]"></div>
184
- <div class="h-[var(--panel-height)]"></div>
185
- ```
186
-
187
- Supported units:
27
+ Add the plugin to your Vite config:
188
28
 
189
- ```txt
190
- px, em, rem, %, vw, vh, svw, svh, lvw, lvh, dvw, dvh, vmin, vmax, ch, ex, cm, mm, in, pt, pc
191
- ```
29
+ ts import { defineConfig } from 'vite' import { utilsJit } from '@vueland/utils-jit' export default defineConfig({ plugins: [ utilsJit(), ], })
192
30
 
193
- Supported functions:
31
+ Then use arbitrary utility classes in your templates:
194
32
 
195
- ```txt
196
- calc(), min(), max(), clamp(), var()
197
- ```
33
+ vue <template> <button class="w-[160px] px-[20px] py-[12px] radius-[8px] bg-[#42b883] color-[#fff]"> Button </button> </template>
198
34
 
199
- ### Margin
200
-
201
- Margin utilities support length-like values and `auto`.
202
-
203
- ```html
204
- <div class="ma-[16px]"></div>
205
- <div class="mx-[auto]"></div>
206
- <div class="mt-[2rem]"></div>
207
- <div class="mb-[calc(100%-20px)]"></div>
208
- <div class="ma-[10px 20px]"></div>
209
- ```
210
-
211
- ### Padding
212
-
213
- Padding utilities support length-like values.
214
-
215
- ```html
216
- <div class="pa-[16px]"></div>
217
- <div class="px-[12px]"></div>
218
- <div class="py-[8px 12px]"></div>
219
- ```
220
-
221
- `auto` is not valid for padding and will be ignored.
222
-
223
- ### Radius
224
-
225
- Radius utilities support length-like values.
226
-
227
- ```html
228
- <div class="radius-[8px]"></div>
229
- <div class="radius-[8px 12px]"></div>
230
- <div class="radius-tl-[16px]"></div>
231
- ```
232
-
233
- ### Z-index
234
-
235
- `z-[value]` supports numbers, `auto` and CSS variables.
236
-
237
- ```html
238
- <div class="z-[1]"></div>
239
- <div class="z-[999]"></div>
240
- <div class="z-[auto]"></div>
241
- <div class="z-[var(--z-modal)]"></div>
242
- ```
243
-
244
- ### Opacity
245
-
246
- `opacity-[value]` supports values from `0` to `1`, CSS variables and global CSS values.
247
-
248
- ```html
249
- <div class="opacity-[0]"></div>
250
- <div class="opacity-[0.64]"></div>
251
- <div class="opacity-[1]"></div>
252
- <div class="opacity-[var(--opacity)]"></div>
253
- ```
254
-
255
- ### Color and background-color
256
-
257
- `color-[value]` and `bg-[value]` support hex colors, CSS color functions, CSS variables and selected keyword values.
258
-
259
- ```html
260
- <div class="color-[#111]"></div>
261
- <div class="bg-[#fff]"></div>
262
- <div class="bg-[rgb(255,255,255)]"></div>
263
- <div class="color-[oklch(60% 0.2 20)]"></div>
264
- <div class="bg-[var(--vl-surface)]"></div>
265
- <div class="color-[currentColor]"></div>
266
- ```
267
-
268
- Invalid values are ignored:
269
-
270
- ```html
271
- <div class="w-[;]"></div>
272
- <div class="radius-[.]"></div>
273
- <div class="px-[auto]"></div>
274
- <div class="z-[10px]"></div>
275
- <div class="opacity-[2]"></div>
276
- ```
35
+ The plugin scans your project files and generates only the CSS utilities that are actually used.
277
36
 
278
37
  ## Variants
279
38
 
280
- Variants are added before the utility name using `:`.
281
-
282
- ```html
283
- <div class="hover:w-[320px] md:px-[24px]"></div>
284
- ```
285
-
286
- ### Built-in pseudo variants
287
-
288
- The following pseudo variants are available by default:
289
-
290
- ```txt
291
- hover
292
- focus
293
- focus-visible
294
- focus-within
295
- active
296
- disabled
297
- checked
298
- visited
299
- first
300
- last
301
- odd
302
- even
303
- ```
304
-
305
- Example:
306
-
307
- ```vue
308
- <template>
309
- <button class="w-[160px] hover:w-[180px] focus:px-[20px] active:radius-[10px]">
310
- Button
311
- </button>
312
- </template>
313
- ```
314
-
315
- Generated CSS:
316
-
317
- ```css
318
- .hover\:w-\[180px\]:hover{width: 180px !important;}
319
- .focus\:px-\[20px\]:focus{padding-left: 20px !important;padding-right: 20px !important;}
320
- .active\:radius-\[10px\]:active{border-radius: 10px !important;}
321
- ```
322
-
323
- ## Responsive variants
324
-
325
- The default breakpoints are:
326
-
327
- ```ts
328
- {
329
- sm: 640,
330
- md: 768,
331
- lg: 1024,
332
- xl: 1280,
333
- '2xl': 1536,
334
- }
335
- ```
336
-
337
- Example:
338
-
339
- ```vue
340
- <template>
341
- <div class="w-[100%] md:w-[720px] lg:w-[960px] xl:w-[1200px] 2xl:w-[1440px]">
342
- Container
343
- </div>
344
- </template>
345
- ```
346
-
347
- Generated CSS:
348
-
349
- ```css
350
- @media (min-width: 768px) { .md\:w-\[720px\]{width: 720px !important;} }
351
- @media (min-width: 1024px) { .lg\:w-\[960px\]{width: 960px !important;} }
352
- @media (min-width: 1280px) { .xl\:w-\[1200px\]{width: 1200px !important;} }
353
- @media (min-width: 1536px) { .2xl\:w-\[1440px\]{width: 1440px !important;} }
354
- ```
355
-
356
- ## Custom variants
357
-
358
- You can add custom selector, attribute and media variants.
359
-
360
- ```ts
361
- utilsJIT({
362
- variants: {
363
- hocus: {
364
- kind: 'selector',
365
- value: '&:hover,&:focus',
366
- },
367
- selected: {
368
- kind: 'attribute',
369
- value: '[aria-selected="true"]',
370
- },
371
- tablet: {
372
- kind: 'media',
373
- value: 900,
374
- },
375
- },
376
- })
377
- ```
378
-
379
- Usage:
380
-
381
- ```html
382
- <div class="hocus:w-[320px] selected:bg-[#eee] tablet:px-[24px]"></div>
383
- ```
384
-
385
- Generated CSS:
386
-
387
- ```css
388
- .hocus\:w-\[320px\]:hover,.hocus\:w-\[320px\]:focus{width: 320px !important;}
389
- .selected\:bg-\[\#eee\][aria-selected="true"]{background-color: #eee !important;}
390
- @media (min-width: 900px) { .tablet\:px-\[24px\]{padding-left: 24px !important;padding-right: 24px !important;} }
391
- ```
392
-
393
- ### Theme variants
394
- Dark mode is part of the application theme strategy. Different projects may implement it through `.dark`, `data-theme`, CSS variables, a provider, or a custom theme layer. The plugin does not enforce one specific approach.
395
-
396
- If you need `dark:`, add it explicitly.
397
-
398
- Using `data-theme`:
399
-
400
- ```ts
401
- utilsJIT({
402
- variants: {
403
- dark: {
404
- kind: 'selector',
405
- value: '[data-theme="dark"] &',
406
- },
407
- },
408
- })
409
- ```
410
-
411
- Usage:
412
-
413
- ```html
414
- <div class="bg-[#fff] dark:bg-[#111] color-[#111] dark:color-[#fff]"></div>
415
- ```
416
-
417
- Generated CSS:
418
-
419
- ```css
420
- [data-theme="dark"] .dark\:bg-\[\#111\]{background-color: #111 !important;}
421
- [data-theme="dark"] .dark\:color-\[\#fff\]{color: #fff !important;}
422
- ```
423
-
424
- Using `.dark`:
425
-
426
- ```ts
427
- utilsJIT({
428
- variants: {
429
- dark: {
430
- kind: 'selector',
431
- value: '.dark &',
432
- },
433
- },
434
- })
435
- ```
436
-
437
- Usage:
438
-
439
- ```html
440
- <div class="dark:bg-[#111]"></div>
441
- ```
442
-
443
- Generated CSS:
444
-
445
- ```css
446
- .dark .dark\:bg-\[\#111\]{background-color: #111 !important;}
447
- ```
448
-
449
- ## Combining variants
450
-
451
- Pseudo variants, custom selector variants and responsive variants can be combined.
452
-
453
- `hocus:` is not built in. Add it first:
454
-
455
- ```ts
456
- utilsJIT({
457
- variants: {
458
- hocus: {
459
- kind: 'selector',
460
- value: '&:hover,&:focus',
461
- },
462
- },
463
- })
464
- ```
465
-
466
- Then it can be combined with responsive variants:
467
-
468
- ```vue
469
- <template>
470
- <button class="hover:md:w-[240px] focus:lg:px-[32px] hocus:xl:bg-[#eee]">
471
- Responsive button
472
- </button>
473
- </template>
474
- ```
475
-
476
- Generated CSS:
477
-
478
- ```css
479
- @media (min-width: 768px) { .hover\:md\:w-\[240px\]:hover{width: 240px !important;} }
480
- @media (min-width: 1024px) { .focus\:lg\:px-\[32px\]:focus{padding-left: 32px !important;padding-right: 32px !important;} }
481
- @media (min-width: 1280px) { .hocus\:xl\:bg-\[\#eee\]:hover,.hocus\:xl\:bg-\[\#eee\]:focus{background-color: #eee !important;} }
482
- ```
483
-
484
- ## Configuration
485
-
486
- `utilsJIT` accepts an options object.
487
-
488
- ```ts
489
- import { defineConfig } from 'vite'
490
- import vue from '@vitejs/plugins-vue'
491
- import { utilsJIT } from '@vueland/utils-jit'
492
-
493
- export default defineConfig({
494
- plugins: [
495
- vue(),
496
- utilsJIT({
497
- outFile: 'src/.generated/utils-jit.css',
498
- include: [/\.(vue|js|ts|jsx|tsx|html)$/],
499
- exclude: [/src\/fixtures/],
500
- breakpoints: {
501
- xs: 480,
502
- sm: 640,
503
- md: 768,
504
- lg: 1024,
505
- xl: 1280,
506
- '2xl': 1536,
507
- },
508
- debug: false,
509
- }),
510
- ],
511
- })
512
- ```
513
-
514
- ## Options
515
-
516
- | Option | Type | Default | Description |
517
- | --- | --- | --- | --- |
518
- | `include` | `Array<string \| RegExp>` | `[/\.(vue\|js\|ts\|jsx\|tsx\|html)$/]` | Files to scan. |
519
- | `exclude` | `Array<string \| RegExp>` | Internal service directories | Files and directories to ignore. |
520
- | `outFile` | `string` | `src/.generated/utils-jit.css` | Generated CSS path relative to the Vite root. |
521
- | `breakpoints` | `Record<string, number>` | `sm`, `md`, `lg`, `xl`, `2xl` | Responsive variants. |
522
- | `rules` | `UtilityRule[]` | `[]` | Custom utility rules. |
523
- | `variants` | `VariantMap` | Built-in variants | Custom variants. |
524
- | `banner` | `string` | `/* @vueland/utils-jit: generated utilities */` | Banner at the top of the generated CSS file. |
525
- | `emitEmptyFile` | `boolean` | `true` | Create a file with a comment when no utilities are found. |
526
- | `debug` | `boolean` | `false` | Print diagnostic messages. |
527
-
528
- ### outFile
529
-
530
- Path to the generated CSS file relative to the Vite project root.
531
-
532
- ```ts
533
- utilsJIT({
534
- outFile: 'src/styles/generated/utils.css',
535
- })
536
- ```
537
-
538
- Then update your import accordingly:
539
-
540
- ```ts
541
- import './styles/generated/utils.css'
542
- ```
543
-
544
- ### include
545
-
546
- Patterns for files that should be scanned.
547
-
548
- Default:
549
-
550
- ```ts
551
- [/\.(vue|js|ts|jsx|tsx|html)$/]
552
- ```
553
-
554
- Example:
555
-
556
- ```ts
557
- utilsJIT({
558
- include: [/\.(vue|ts)$/],
559
- })
560
- ```
561
-
562
- ### exclude
563
-
564
- Patterns for files and directories that should be excluded from the initial scan, transform and HMR.
565
-
566
- The following directories are excluded by default:
567
-
568
- ```txt
569
- node_modules
570
- .git
571
- dist
572
- build
573
- coverage
574
- .output
575
- .nuxt
576
- .turbo
577
- .generated
578
- storybook-static
579
- playwright-report
580
- ```
581
-
582
- Example:
583
-
584
- ```ts
585
- utilsJIT({
586
- exclude: [
587
- /src\/fixtures/,
588
- /src\/legacy/,
589
- 'storybook-static',
590
- ],
591
- })
592
- ```
593
-
594
- ### breakpoints
595
-
596
- Responsive variants. The key is used as the class prefix, and the value is used as `min-width` in pixels.
597
-
598
- ```ts
599
- utilsJIT({
600
- breakpoints: {
601
- xs: 480,
602
- sm: 640,
603
- md: 768,
604
- lg: 1024,
605
- xl: 1280,
606
- '2xl': 1536,
607
- '3xl': 1920,
608
- },
609
- })
610
- ```
611
-
612
- Usage:
613
-
614
- ```html
615
- <div class="xs:w-[320px] 3xl:w-[1600px]"></div>
616
- ```
617
-
618
- ### variants
619
-
620
- Custom variants extend the state and selector syntax.
621
-
622
- ```ts
623
- utilsJIT({
624
- variants: {
625
- hocus: {
626
- kind: 'selector',
627
- value: '&:hover,&:focus',
628
- },
629
- selected: {
630
- kind: 'attribute',
631
- value: '[aria-selected="true"]',
632
- },
633
- tablet: {
634
- kind: 'media',
635
- value: 900,
636
- },
637
- },
638
- })
639
- ```
640
-
641
- ### emitEmptyFile
642
-
643
- When `emitEmptyFile` is `true`, the plugin creates a file with this content if no utilities are found:
644
-
645
- ```css
646
- /* @vueland/utils-jit: no utilities found */
647
- ```
648
-
649
- When `emitEmptyFile` is `false`, the file is not created until at least one utility class is found.
650
-
651
- ```ts
652
- utilsJIT({
653
- emitEmptyFile: false,
654
- })
655
- ```
656
-
657
- ## Custom utility rules
658
-
659
- The plugin can be extended with custom rules through `rules` and `defineRule`.
660
-
661
- ```ts
662
- import { defineConfig } from 'vite'
663
- import vue from '@vitejs/plugins-vue'
664
- import {
665
- defineRule,
666
- isColorValue,
667
- isSizeValue,
668
- utilsJIT,
669
- } from '@vueland/utils-jit'
670
-
671
- export default defineConfig({
672
- plugins: [
673
- vue(),
674
- utilsJIT({
675
- rules: [
676
- defineRule({
677
- name: 'surface',
678
- matcher: /^surface-\[(.+)\]$/,
679
- validate: isColorValue,
680
- declaration: (value) => ({
681
- backgroundColor: value,
682
- }),
683
- important: false,
684
- }),
685
-
686
- defineRule({
687
- name: 'size',
688
- matcher: /^size-\[(.+)\]$/,
689
- validate: isSizeValue,
690
- declaration: (value) => ({
691
- width: value,
692
- height: value,
693
- }),
694
- }),
695
- ],
696
- }),
697
- ],
698
- })
699
- ```
700
-
701
- Usage:
702
-
703
- ```vue
704
- <template>
705
- <div class="surface-[#fff] size-[40px] hover:size-[48px]">
706
- Custom utilities
707
- </div>
708
- </template>
709
- ```
710
-
711
- Generated CSS:
712
-
713
- ```css
714
- .surface-\[\#fff\]{background-color: #fff;}
715
- .size-\[40px\]{width: 40px !important;height: 40px !important;}
716
- .hover\:size-\[48px\]:hover{width: 48px !important;height: 48px !important;}
717
- ```
718
-
719
- ## defineRule API
720
-
721
- ```ts
722
- defineRule({
723
- name: 'rule-name',
724
- matcher: /^rule-name-\[(.+)\]$/,
725
- validate: (value) => true,
726
- declaration: (value) => ({
727
- cssProperty: value,
728
- }),
729
- important: true,
730
- })
731
- ```
732
-
733
- | Field | Type | Description |
734
- | --- | --- | --- |
735
- | `name` | `string` | Rule name for readability and debugging. |
736
- | `matcher` | `RegExp` | Matcher for the utility part without variants. |
737
- | `validate` | `(value: string) => boolean` | Validates the value inside `[]`. |
738
- | `declaration` | `(value: string) => Record<string, string \| number> \| string[]` | Generates CSS declarations. |
739
- | `important` | `boolean` | Adds `!important` to object-based declarations. Defaults to `true`. |
740
-
741
- `declaration` usually returns a JS-style object:
742
-
743
- ```ts
744
- defineRule({
745
- name: 'bg',
746
- matcher: /^bg-\[(.+)\]$/,
747
- validate: isColorValue,
748
- declaration: (value) => ({
749
- backgroundColor: value,
750
- }),
751
- })
752
- ```
753
-
754
- CamelCase CSS properties are converted to kebab-case:
755
-
756
- ```ts
757
- {
758
- backgroundColor: '#fff',
759
- borderTopLeftRadius: '8px',
760
- }
761
- ```
762
-
763
- Result:
764
-
765
- ```css
766
- background-color: #fff !important;
767
- border-top-left-radius: 8px !important;
768
- ```
769
-
770
- CSS variables are preserved:
771
-
772
- ```ts
773
- defineRule({
774
- name: 'token',
775
- matcher: /^token-\[(.+)\]$/,
776
- declaration: (value) => ({
777
- '--vl-token': value,
778
- }),
779
- important: false,
780
- })
781
- ```
782
-
783
- Result:
784
-
785
- ```css
786
- --vl-token: #fff;
787
- ```
788
-
789
- If `declaration` returns `string[]`, the strings are treated as final CSS declarations. In this case, `!important` is not added automatically.
790
-
791
- ```ts
792
- defineRule({
793
- name: 'raw',
794
- matcher: /^raw-\[(.+)\]$/,
795
- declaration: (value) => [
796
- `--raw-value: ${value};`,
797
- ],
798
- })
799
- ```
800
-
801
- ## Validators
802
-
803
- The package exports validators that can be reused in custom rules.
804
-
805
- ```ts
806
- import {
807
- isColorValue,
808
- isMarginValue,
809
- isOpacityValue,
810
- isPaddingValue,
811
- isPositionValue,
812
- isRadiusValue,
813
- isSizeValue,
814
- isZIndexValue,
815
- } from '@vueland/utils-jit'
816
- ```
817
-
818
- Example:
819
-
820
- ```ts
821
- defineRule({
822
- name: 'text',
823
- matcher: /^text-\[(.+)\]$/,
824
- validate: isColorValue,
825
- declaration: (value) => ({
826
- color: value,
827
- }),
828
- })
829
- ```
830
-
831
- For production rules, prefer strict validation.
832
-
833
- ```ts
834
- import { defineRule } from '@vueland/utils-jit'
835
-
836
- const gridColsRule = defineRule({
837
- name: 'grid-cols',
838
- matcher: /^grid-cols-\[(.+)\]$/,
839
- validate: (value) => /^\d+$/.test(value),
840
- declaration: (value) => ({
841
- gridTemplateColumns: `repeat(${value}, minmax(0, 1fr))`,
842
- }),
843
- })
844
- ```
845
-
846
- Usage:
847
-
848
- ```html
849
- <div class="grid-cols-[3]"></div>
850
- ```
851
-
852
- Generated CSS:
853
-
854
- ```css
855
- .grid-cols-\[3\]{grid-template-columns: repeat(3, minmax(0, 1fr)) !important;}
856
- ```
857
-
858
- ## Vue class scanning
859
-
860
- The plugin first tries to extract content from `class="..."` and `:class="..."`, then tokenizes the found chunks.
861
-
862
- Static strings inside `:class` are supported:
863
-
864
- ```vue
865
- <template>
866
- <div :class="['w-[200px]', active && 'px-[16px]']"></div>
867
- <div :class="{ 'radius-[12px]': rounded }"></div>
868
- </template>
869
- ```
870
-
871
- Runtime-generated class names are not evaluated. The class must exist as a static token in the source code.
872
-
873
- This will not work:
874
-
875
- ```vue
876
- <script setup lang="ts">
877
- const width = 320
878
- </script>
879
-
880
- <template>
881
- <div :class="`w-[${width}px]`"></div>
882
- </template>
883
- ```
884
-
885
- This will work:
886
-
887
- ```vue
888
- <template>
889
- <div :class="isWide ? 'w-[320px]' : 'w-[240px]'"></div>
890
- </template>
891
- ```
892
-
893
- ## How generation works
894
-
895
- During startup, the plugin:
896
-
897
- 1. Walks through project files.
898
- 2. Skips service directories like `node_modules`, `.git`, `dist`, `build` and `.generated`.
899
- 3. Scans only files that match `include`.
900
- 4. Extracts utility tokens.
901
- 5. Validates values.
902
- 6. Generates the final CSS file.
903
-
904
- During development, the plugin updates CSS incrementally:
905
-
906
- - adds rules for new tokens;
907
- - removes rules when a token is no longer used anywhere;
908
- - keeps a rule when the same token is still used in another file;
909
- - reuses token parsing and CSS rule caches;
910
- - notifies the Vite watcher when the generated CSS changes.
911
-
912
- ## Safety limits
913
-
914
- To avoid unsafe or invalid CSS, the plugin limits arbitrary values:
915
-
916
- - minimum token length: `5`;
917
- - maximum token length: `180`;
918
- - maximum value length: `160`;
919
- - forbidden characters: `;`, `{`, `}`, `<`, `>`;
920
- - CSS comments inside values are forbidden;
921
- - the value must contain at least one letter or digit;
922
- - only a safe subset of CSS value characters is allowed.
923
-
924
- The following classes are ignored:
925
-
926
- ```html
927
- <div class="w-[;]"></div>
928
- <div class="w-[{}]"></div>
929
- <div class="w-[<script>]"></div>
930
- <div class="w-[...........................................]"></div>
931
- ```
932
-
933
- ## Recommendations
934
-
935
- Use Utils JIT for precise one-off arbitrary values, not as a replacement for a design system.
936
-
937
- Good:
938
-
939
- ```vue
940
- <template>
941
- <c-card class="max-w-[720px] px-[24px] radius-[16px]">
942
- Content
943
- </c-card>
944
- </template>
945
- ```
946
-
947
- If a value is repeated across the project, prefer moving it into a theme, preset, token or component variant.
948
-
949
- ## Troubleshooting
950
-
951
- ### The CSS file was not created
952
-
953
- Check that:
954
-
955
- - `utilsJIT()` is added to `vite.config.ts`;
956
- - there is at least one supported utility class in the project;
957
- - `outFile` is correct;
958
- - the generated CSS file is imported by the application;
959
- - the file matches `include`;
960
- - the file is not ignored by `exclude`.
961
-
962
- If no utilities are found and `emitEmptyFile` is `true`, the file is created with this content:
963
-
964
- ```css
965
- /* @vueland/utils-jit: no utilities found */
966
- ```
967
-
968
- If `emitEmptyFile` is `false`, the file appears only after at least one utility class is found.
969
-
970
- ### A class exists, but CSS is not generated
971
-
972
- Check that:
973
-
974
- - the file matches `include`;
975
- - the file is not ignored by `exclude`;
976
- - the class is written statically and is not generated at runtime;
977
- - the value passes validation;
978
- - the utility is supported by built-in rules or added through `rules`;
979
- - the variant exists in `breakpoints` or `variants`.
980
-
981
- ### `xs:` or `3xl:` does not work
982
-
983
- Add the breakpoint manually:
984
-
985
- ```ts
986
- utilsJIT({
987
- breakpoints: {
988
- xs: 480,
989
- sm: 640,
990
- md: 768,
991
- lg: 1024,
992
- xl: 1280,
993
- '2xl': 1536,
994
- '3xl': 1920,
995
- },
996
- })
997
- ```
998
-
999
- ### A custom rule does not work
1000
-
1001
- Make sure `matcher` matches only the utility part without variants.
1002
-
1003
- For this class:
1004
-
1005
- ```html
1006
- <div class="hover:surface-[#fff]"></div>
1007
- ```
1008
-
1009
- The matcher receives:
1010
-
1011
- ```txt
1012
- surface-[#fff]
1013
- ```
1014
-
1015
- Correct rule:
1016
-
1017
- ```ts
1018
- defineRule({
1019
- name: 'surface',
1020
- matcher: /^surface-\[(.+)\]$/,
1021
- validate: isColorValue,
1022
- declaration: (value) => ({
1023
- backgroundColor: value,
1024
- }),
1025
- })
1026
- ```
39
+ Utility classes can be combined with variants:
1027
40
 
1028
- ## Full Vite config example
41
+ vue <template> <button class="w-[160px] hover:w-[180px] focus:px-[24px]"> Button </button> </template>
1029
42
 
1030
- ```ts
1031
- import { defineConfig } from 'vite'
1032
- import vue from '@vitejs/plugins-vue'
1033
- import {
1034
- defineRule,
1035
- isColorValue,
1036
- isSizeValue,
1037
- utilsJIT,
1038
- } from '@vueland/utils-jit'
43
+ Example output:
1039
44
 
1040
- export default defineConfig({
1041
- plugins: [
1042
- vue(),
45
+ css .hover\:w-\[180px\]:hover { width: 180px !important; } .focus\:px-\[24px\]:focus { padding-left: 24px !important; padding-right: 24px !important; }
1043
46
 
1044
- utilsJIT({
1045
- outFile: 'src/.generated/utils-jit.css',
47
+ ## Responsive utilities
1046
48
 
1047
- include: [
1048
- /\.(vue|js|ts|jsx|tsx|html)$/,
1049
- ],
49
+ Responsive variants are also supported:
1050
50
 
1051
- exclude: [
1052
- /src\/fixtures/,
1053
- ],
51
+ vue <template> <div class="w-[100%] md:w-[720px] lg:w-[960px]"></div> </template>
1054
52
 
1055
- breakpoints: {
1056
- xs: 480,
1057
- sm: 640,
1058
- md: 768,
1059
- lg: 1024,
1060
- xl: 1280,
1061
- '2xl': 1536,
1062
- },
53
+ ## Why @vueland/utils-jit?
1063
54
 
1064
- variants: {
1065
- hocus: {
1066
- kind: 'selector',
1067
- value: '&:hover,&:focus',
1068
- },
55
+ - Generates utilities on demand
56
+ - Works with Vite
57
+ - Supports arbitrary values
58
+ - Supports pseudo-class and responsive variants
59
+ - Keeps generated CSS close to actual project usage
60
+ - Designed to be used as part of the Vueland ecosystem
1069
61
 
1070
- selected: {
1071
- kind: 'attribute',
1072
- value: '[aria-selected="true"]',
1073
- },
62
+ ## Package
1074
63
 
1075
- dark: {
1076
- kind: 'selector',
1077
- value: '[data-theme="dark"] &',
1078
- },
1079
- },
64
+ bash pnpm add -D @vueland/utils-jit
1080
65
 
1081
- rules: [
1082
- defineRule({
1083
- name: 'surface',
1084
- matcher: /^surface-\[(.+)\]$/,
1085
- validate: isColorValue,
1086
- declaration: (value) => ({
1087
- backgroundColor: value,
1088
- }),
1089
- important: false,
1090
- }),
66
+ npm:
1091
67
 
1092
- defineRule({
1093
- name: 'size',
1094
- matcher: /^size-\[(.+)\]$/,
1095
- validate: isSizeValue,
1096
- declaration: (value) => ({
1097
- width: value,
1098
- height: value,
1099
- }),
1100
- }),
1101
- ],
1102
- }),
1103
- ],
1104
- })
1105
- ```
68
+ https://www.npmjs.com/package/@vueland/utils-jit
1106
69
 
1107
70
  ## License
1108
71
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vueland/utils-jit",
3
3
  "description": "Vite JIT utility engine for Vueland",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "main": "./dist/index.cjs",