@bookklik/senangstart-css 0.2.8 → 0.2.10

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 (72) hide show
  1. package/dist/senangstart-css.js +2751 -1952
  2. package/dist/senangstart-css.min.js +266 -225
  3. package/dist/senangstart-tw.js +440 -77
  4. package/dist/senangstart-tw.min.js +1 -1
  5. package/docs/SYNTAX-REFERENCE.md +1731 -1590
  6. package/docs/guide/configuration.md +2 -2
  7. package/docs/guide/preflight.md +20 -1
  8. package/docs/guide/states.md +60 -0
  9. package/docs/ms/guide/configuration.md +2 -2
  10. package/docs/ms/guide/preflight.md +19 -0
  11. package/docs/ms/guide/states.md +60 -0
  12. package/docs/ms/reference/breakpoints.md +14 -0
  13. package/docs/ms/reference/colors.md +2 -2
  14. package/docs/ms/reference/space/height.md +10 -10
  15. package/docs/ms/reference/space/width.md +12 -12
  16. package/docs/ms/reference/visual/border-radius.md +50 -10
  17. package/docs/ms/reference/visual/contain.md +57 -0
  18. package/docs/ms/reference/visual/content-visibility.md +53 -0
  19. package/docs/ms/reference/visual/placeholder-color.md +92 -0
  20. package/docs/ms/reference/visual/writing-mode.md +53 -0
  21. package/docs/ms/reference/visual.md +6 -0
  22. package/docs/public/assets/senangstart-css.min.js +266 -225
  23. package/docs/public/llms.txt +63 -2
  24. package/docs/reference/breakpoints.md +14 -0
  25. package/docs/reference/colors.md +2 -2
  26. package/docs/reference/space/height.md +10 -10
  27. package/docs/reference/space/width.md +12 -12
  28. package/docs/reference/visual/border-radius.md +50 -10
  29. package/docs/reference/visual/contain.md +57 -0
  30. package/docs/reference/visual/content-visibility.md +53 -0
  31. package/docs/reference/visual/placeholder-color.md +92 -0
  32. package/docs/reference/visual/writing-mode.md +53 -0
  33. package/docs/reference/visual.md +7 -0
  34. package/docs/syntax-reference.json +2185 -2009
  35. package/package.json +1 -1
  36. package/public/senangstart.css +1 -1
  37. package/scripts/convert-tailwind.js +486 -89
  38. package/scripts/generate-docs.js +403 -403
  39. package/scripts/generate-llms-txt.js +28 -0
  40. package/src/cdn/senangstart-engine.js +37 -1927
  41. package/src/cdn/tw-conversion-engine.js +504 -78
  42. package/src/cli/commands/build.js +10 -0
  43. package/src/compiler/generators/css.js +400 -67
  44. package/src/compiler/generators/preflight.js +26 -13
  45. package/src/compiler/generators/typescript.js +3 -1
  46. package/src/compiler/index.js +27 -3
  47. package/src/compiler/parser.js +24 -7
  48. package/src/config/defaults.js +4 -1
  49. package/src/core/constants.js +5 -3
  50. package/src/definitions/index.js +7 -3
  51. package/src/definitions/layout.js +2 -2
  52. package/src/definitions/space.js +45 -19
  53. package/src/definitions/visual-performance.js +126 -0
  54. package/src/definitions/visual.js +25 -9
  55. package/src/index.js +47 -0
  56. package/src/utils/common.js +17 -5
  57. package/templates/senangstart.config.js +1 -1
  58. package/tests/helpers/test-utils.js +1 -1
  59. package/tests/integration/compiler.test.js +12 -1
  60. package/tests/unit/compiler/generators/css.coverage.test.js +833 -0
  61. package/tests/unit/compiler/generators/css.test.js +1520 -6
  62. package/tests/unit/compiler/generators/preflight.test.js +31 -0
  63. package/tests/unit/compiler/parser.test.js +26 -0
  64. package/tests/unit/config/defaults.test.js +2 -2
  65. package/tests/unit/convert-tailwind.cli.test.js +95 -0
  66. package/tests/unit/convert-tailwind.coverage.test.js +225 -0
  67. package/tests/unit/convert-tailwind.test.js +61 -21
  68. package/tests/unit/core/tokenizer-core.test.js +102 -0
  69. package/tests/unit/definitions/index.test.js +108 -0
  70. package/tests/unit/definitions/layout_definitions.test.js +40 -0
  71. package/tests/unit/utils/common.test.js +26 -0
  72. package/scripts/bundle-jit.js +0 -45
@@ -142,6 +142,76 @@
142
142
  "9xl": "hero"
143
143
  // 8rem → hero
144
144
  };
145
+ var lineHeightScale = {
146
+ none: "none",
147
+ // line-height: 1
148
+ tight: "tight",
149
+ // line-height: 1.25
150
+ snug: "snug",
151
+ // line-height: 1.375
152
+ normal: "normal",
153
+ // line-height: 1.5
154
+ relaxed: "relaxed",
155
+ // line-height: 1.625
156
+ loose: "loose"
157
+ // line-height: 2
158
+ };
159
+ var letterSpacingScale = {
160
+ tighter: "tighter",
161
+ // letter-spacing: -0.05em
162
+ tight: "tight",
163
+ // letter-spacing: -0.025em
164
+ normal: "normal",
165
+ // letter-spacing: 0
166
+ wide: "wide",
167
+ // letter-spacing: 0.025em
168
+ wider: "wider",
169
+ // letter-spacing: 0.05em
170
+ widest: "widest"
171
+ // letter-spacing: 0.1em
172
+ };
173
+ var zIndexScale = {
174
+ 0: "base",
175
+ // z-index: 0
176
+ 10: "low",
177
+ // z-index: 10
178
+ 20: "low",
179
+ // z-index: 20
180
+ 30: "low",
181
+ // z-index: 30
182
+ 40: "low",
183
+ // z-index: 40
184
+ 50: "mid",
185
+ // z-index: 50
186
+ 60: "high",
187
+ // z-index: 60
188
+ 70: "high",
189
+ // z-index: 70
190
+ 80: "high",
191
+ // z-index: 80
192
+ 90: "high",
193
+ // z-index: 90
194
+ 100: "high",
195
+ // z-index: 100
196
+ auto: "auto"
197
+ // z-index: auto
198
+ };
199
+ var fractionScale = {
200
+ "1/2": "half",
201
+ // 50%
202
+ "1/3": "third",
203
+ // 33.33%
204
+ "2/3": "third-2x",
205
+ // 66.67%
206
+ "1/4": "quarter",
207
+ // 25%
208
+ "2/4": "half",
209
+ // 50% (alias)
210
+ "3/4": "quarter-3x",
211
+ // 75%
212
+ "full": "full"
213
+ // 100%
214
+ };
145
215
  var layoutMappings = {
146
216
  container: "container",
147
217
  flex: "flex",
@@ -202,23 +272,93 @@
202
272
  "object-scale-down": "object:scale-down"
203
273
  };
204
274
  var visualKeywords = {
275
+ // Font style
205
276
  italic: "italic",
206
277
  "not-italic": "not-italic",
278
+ // Font smoothing
207
279
  antialiased: "antialiased",
280
+ "subpixel-antialiased": "subpixel-antialiased",
281
+ // Text transform
208
282
  uppercase: "uppercase",
209
283
  lowercase: "lowercase",
210
284
  capitalize: "capitalize",
211
285
  "normal-case": "normal-case",
286
+ // Text decoration
212
287
  underline: "underline",
288
+ overline: "overline",
213
289
  "line-through": "line-through",
214
290
  "no-underline": "no-underline",
291
+ // Text decoration style
292
+ "decoration-solid": "decoration-solid",
293
+ "decoration-double": "decoration-double",
294
+ "decoration-dotted": "decoration-dotted",
295
+ "decoration-dashed": "decoration-dashed",
296
+ "decoration-wavy": "decoration-wavy",
297
+ // Text overflow
215
298
  truncate: "truncate",
216
- "cursor-pointer": "cursor:pointer",
299
+ "text-ellipsis": "text-ellipsis",
300
+ "text-clip": "text-clip",
301
+ // Text wrap
302
+ "text-wrap": "text-wrap",
303
+ "text-nowrap": "text-nowrap",
304
+ "text-balance": "text-balance",
305
+ "text-pretty": "text-pretty",
306
+ // Whitespace
307
+ "whitespace-normal": "whitespace-normal",
308
+ "whitespace-nowrap": "whitespace-nowrap",
309
+ "whitespace-pre": "whitespace-pre",
310
+ "whitespace-pre-line": "whitespace-pre-line",
311
+ "whitespace-pre-wrap": "whitespace-pre-wrap",
312
+ "whitespace-break-spaces": "whitespace-break-spaces",
313
+ // Word break
314
+ "break-normal": "break-normal",
315
+ "break-words": "break-words",
316
+ "break-all": "break-all",
317
+ "break-keep": "break-keep",
318
+ // Hyphens
319
+ "hyphens-none": "hyphens-none",
320
+ "hyphens-manual": "hyphens-manual",
321
+ "hyphens-auto": "hyphens-auto",
322
+ // List style
323
+ "list-none": "list-none",
324
+ "list-disc": "list-disc",
325
+ "list-decimal": "list-decimal",
326
+ "list-inside": "list-inside",
327
+ "list-outside": "list-outside",
328
+ // Cursor
329
+ "cursor-auto": "cursor:auto",
217
330
  "cursor-default": "cursor:default",
331
+ "cursor-pointer": "cursor:pointer",
332
+ "cursor-wait": "cursor:wait",
333
+ "cursor-text": "cursor:text",
334
+ "cursor-move": "cursor:move",
218
335
  "cursor-not-allowed": "cursor:not-allowed",
336
+ "cursor-grab": "cursor:grab",
337
+ "cursor-grabbing": "cursor:grabbing",
338
+ // User select
219
339
  "select-none": "select:none",
220
340
  "select-text": "select:text",
221
- "select-all": "select:all"
341
+ "select-all": "select:all",
342
+ "select-auto": "select:auto",
343
+ // Pointer events
344
+ "pointer-events-none": "pointer-events:none",
345
+ "pointer-events-auto": "pointer-events:auto",
346
+ // Appearance
347
+ "appearance-none": "appearance:none",
348
+ "appearance-auto": "appearance:auto",
349
+ // 3D Transforms
350
+ perspective: "perspective",
351
+ "perspective-origin": "perspective-origin",
352
+ "transform-style": "transform-style",
353
+ "backface-visibility": "backface",
354
+ mask: "mask",
355
+ "mask-image": "mask-image",
356
+ "mask-mode": "mask-mode",
357
+ "mask-origin": "mask-origin",
358
+ "mask-position": "mask-position",
359
+ "mask-repeat": "mask-repeat",
360
+ "mask-size": "mask-size",
361
+ "mask-type": "mask-type"
222
362
  };
223
363
  function getSpacing(value, exact) {
224
364
  if (value.startsWith("[") && value.endsWith("]")) {
@@ -252,40 +392,83 @@
252
392
  }
253
393
  function convertClass(twClass, exact) {
254
394
  const prefixMatch = twClass.match(
255
- /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|focus-visible:|active:|disabled:|dark:)(.+)$/
395
+ /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|focus-visible:|active:|disabled:|dark:|group-hover:|peer-hover:|group-focus:|peer-focus:|group-active:|peer-active:|peer-check:|group-open:|peer-open:)(.+)$/
256
396
  );
257
- let prefix = "", baseClass = twClass;
397
+ let prefix = "", baseClass = twClass, extraAttr = null;
258
398
  if (prefixMatch) {
259
399
  const rawPrefix = prefixMatch[1].slice(0, -1);
260
400
  if (["sm", "md", "lg", "xl", "2xl"].includes(rawPrefix)) {
261
401
  prefix = `tw-${rawPrefix}:`;
402
+ } else if (rawPrefix.startsWith("group-") || rawPrefix.startsWith("peer-")) {
403
+ const stateMap = {
404
+ "hover": "hover",
405
+ "focus": "focus",
406
+ // or focus-within if we strictly follow group logic, but SenangStart group logic handles mapped state triggers
407
+ "active": "active",
408
+ "open": "expanded",
409
+ // map open -> expanded
410
+ "check": "checked"
411
+ // map check -> checked
412
+ };
413
+ const variant = rawPrefix.split("-")[1];
414
+ const mappedState = stateMap[variant] || variant;
415
+ prefix = `${mappedState}:`;
416
+ if (rawPrefix.startsWith("peer-")) {
417
+ extraAttr = { cat: "listens", val: "peer" };
418
+ }
262
419
  } else {
263
420
  prefix = prefixMatch[1];
264
421
  }
265
422
  baseClass = prefixMatch[2];
266
423
  }
424
+ if (baseClass === "group") {
425
+ return { cat: "layout", val: "hoverable focusable pressable expandable" };
426
+ }
427
+ if (baseClass === "peer") {
428
+ return [
429
+ { cat: "layout", val: "hoverable focusable pressable expandable" },
430
+ { cat: "interact", val: "peer" }
431
+ ];
432
+ }
433
+ const attachExtra = (result) => {
434
+ if (!result) return null;
435
+ if (extraAttr) {
436
+ return Array.isArray(result) ? [...result, extraAttr] : [result, extraAttr];
437
+ }
438
+ return result;
439
+ };
267
440
  if (layoutMappings[baseClass])
268
- return { cat: "layout", val: prefix + layoutMappings[baseClass] };
441
+ return attachExtra({ cat: "layout", val: prefix + layoutMappings[baseClass] });
269
442
  if (visualKeywords[baseClass])
270
- return { cat: "visual", val: prefix + visualKeywords[baseClass] };
443
+ return attachExtra({ cat: "visual", val: prefix + visualKeywords[baseClass] });
271
444
  const textColorMatch = baseClass.match(
272
445
  /^text-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/
273
446
  );
274
447
  if (textColorMatch)
275
- return { cat: "visual", val: prefix + "text:" + textColorMatch[1] };
448
+ return attachExtra({ cat: "visual", val: prefix + "text:" + textColorMatch[1] });
276
449
  if (["text-left", "text-center", "text-right", "text-justify"].includes(
277
450
  baseClass
278
451
  ))
279
- return {
452
+ return attachExtra({
280
453
  cat: "visual",
281
454
  val: prefix + "text:" + baseClass.replace("text-", "")
282
- };
455
+ });
283
456
  const textSizeMatch = baseClass.match(
284
457
  /^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/
285
458
  );
286
459
  if (textSizeMatch) {
287
460
  const size = exact ? `tw-${textSizeMatch[1]}` : fontSizeScale[textSizeMatch[1]] || textSizeMatch[1];
288
- return { cat: "visual", val: prefix + "text-size:" + size };
461
+ return attachExtra({ cat: "visual", val: prefix + "text-size:" + size });
462
+ }
463
+ const leadingMatch = baseClass.match(/^leading-(\[.+\]|none|tight|snug|normal|relaxed|loose)$/);
464
+ if (leadingMatch) {
465
+ const val = leadingMatch[1];
466
+ return attachExtra({ cat: "visual", val: prefix + "leading:" + (lineHeightScale[val] || val) });
467
+ }
468
+ const trackingMatch = baseClass.match(/^tracking-(\[.+\]|tighter|tight|normal|wide|wider|widest)$/);
469
+ if (trackingMatch) {
470
+ const val = trackingMatch[1];
471
+ return attachExtra({ cat: "visual", val: prefix + "tracking:" + (letterSpacingScale[val] || val) });
289
472
  }
290
473
  const bgMatch = baseClass.match(
291
474
  /^bg-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?|transparent|current|inherit)$/
@@ -293,31 +476,46 @@
293
476
  if (bgMatch) {
294
477
  const colorVal = bgMatch[1];
295
478
  if (colorVal === "transparent") {
296
- return { cat: "visual", val: prefix + "bg:[transparent]" };
479
+ return attachExtra({ cat: "visual", val: prefix + "bg:transparent" });
297
480
  }
298
481
  if (colorVal === "current") {
299
- return { cat: "visual", val: prefix + "bg:[currentColor]" };
482
+ return attachExtra({ cat: "visual", val: prefix + "bg:currentColor" });
300
483
  }
301
484
  if (colorVal === "inherit") {
302
- return { cat: "visual", val: prefix + "bg:[inherit]" };
485
+ return attachExtra({ cat: "visual", val: prefix + "bg:inherit" });
303
486
  }
304
- return { cat: "visual", val: prefix + "bg:" + colorVal };
487
+ return attachExtra({ cat: "visual", val: prefix + "bg:" + colorVal });
305
488
  }
306
489
  const borderColorMatch = baseClass.match(
307
- /^border-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/
490
+ /^border-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black|transparent|current|inherit)(?:-\d+)?)$/
308
491
  );
309
- if (borderColorMatch)
310
- return {
492
+ if (borderColorMatch) {
493
+ let colorVal = borderColorMatch[1];
494
+ if (colorVal === "current") colorVal = "currentColor";
495
+ return attachExtra({
311
496
  cat: "visual",
312
- val: prefix + "border:" + borderColorMatch[1]
313
- };
497
+ val: prefix + "border:" + colorVal
498
+ });
499
+ }
500
+ const borderSideColorMatch = baseClass.match(
501
+ /^border-([trbl])-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black|transparent|current|inherit)(?:-\d+)?)$/
502
+ );
503
+ if (borderSideColorMatch) {
504
+ const side = borderSideColorMatch[1];
505
+ let colorVal = borderSideColorMatch[2];
506
+ if (colorVal === "current") colorVal = "currentColor";
507
+ return attachExtra({
508
+ cat: "visual",
509
+ val: prefix + `border-${side}:${colorVal}`
510
+ });
511
+ }
314
512
  const paddingMatch = baseClass.match(/^p([trblxy])?-(.+)$/);
315
513
  if (paddingMatch) {
316
514
  const side = paddingMatch[1] ? "-" + paddingMatch[1] : "";
317
- return {
515
+ return attachExtra({
318
516
  cat: "space",
319
517
  val: prefix + "p" + side + ":" + getSpacing(paddingMatch[2], exact)
320
- };
518
+ });
321
519
  }
322
520
  const marginMatch = baseClass.match(
323
521
  /^-?m([trblxy])?-(\[.+\]|\d+\.?\d*|px|auto|full|screen)$/
@@ -334,15 +532,15 @@
334
532
  val = `-${val}`;
335
533
  }
336
534
  }
337
- return { cat: "space", val: prefix + "m" + side + ":" + val };
535
+ return attachExtra({ cat: "space", val: prefix + "m" + side + ":" + val });
338
536
  }
339
537
  const gapMatch = baseClass.match(/^gap-([xy])?-?(.+)$/);
340
538
  if (gapMatch) {
341
539
  const axis = gapMatch[1] ? "-" + gapMatch[1] : "";
342
- return {
540
+ return attachExtra({
343
541
  cat: "space",
344
542
  val: prefix + "g" + axis + ":" + getSpacing(gapMatch[2], exact)
345
- };
543
+ });
346
544
  }
347
545
  const widthMatch = baseClass.match(/^(min-w|max-w|w)-(.+)$/);
348
546
  if (widthMatch) {
@@ -350,7 +548,7 @@
350
548
  const rawVal = widthMatch[2];
351
549
  const specialWidthVals = { "max": "[max-content]", "min": "[min-content]", "fit": "[fit-content]", "prose": "[65ch]" };
352
550
  const val = specialWidthVals[rawVal] || getSpacing(rawVal, exact);
353
- return { cat: "space", val: prefix + prop + ":" + val };
551
+ return attachExtra({ cat: "space", val: prefix + prop + ":" + val });
354
552
  }
355
553
  const heightMatch = baseClass.match(/^(min-h|max-h|h)-(.+)$/);
356
554
  if (heightMatch) {
@@ -358,95 +556,139 @@
358
556
  const rawVal = heightMatch[2];
359
557
  const specialHeightVals = { "screen": "[100vh]", "svh": "[100svh]", "lvh": "[100lvh]", "dvh": "[100dvh]", "max": "[max-content]", "min": "[min-content]", "fit": "[fit-content]" };
360
558
  const val = specialHeightVals[rawVal] || getSpacing(rawVal, exact);
361
- return { cat: "space", val: prefix + prop + ":" + val };
559
+ return attachExtra({ cat: "space", val: prefix + prop + ":" + val });
362
560
  }
363
561
  const roundedMatch = baseClass.match(/^rounded(?:-(.+))?$/);
364
562
  if (roundedMatch) {
365
563
  const size = roundedMatch[1] || "";
366
564
  const scale = exact ? size === "" ? "tw-DEFAULT" : `tw-${size}` : radiusScale[size] || "medium";
367
- return { cat: "visual", val: prefix + "rounded:" + scale };
565
+ return attachExtra({ cat: "visual", val: prefix + "rounded:" + scale });
368
566
  }
369
567
  const shadowMatch = baseClass.match(/^shadow(?:-(.+))?$/);
370
568
  if (shadowMatch) {
371
569
  const size = shadowMatch[1] || "";
372
570
  const scale = exact ? size === "" ? "tw-DEFAULT" : `tw-${size}` : shadowScale[size] || "medium";
373
- return { cat: "visual", val: prefix + "shadow:" + scale };
571
+ return attachExtra({ cat: "visual", val: prefix + "shadow:" + scale });
374
572
  }
375
573
  const fontWeightMatch = baseClass.match(
376
574
  /^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/
377
575
  );
378
576
  if (fontWeightMatch)
379
- return { cat: "visual", val: prefix + "font:tw-" + fontWeightMatch[1] };
577
+ return attachExtra({ cat: "visual", val: prefix + "font:tw-" + fontWeightMatch[1] });
380
578
  const borderWidthMatch = baseClass.match(
381
579
  /^border(?:-([trblxy]))?(?:-(\d+))?$/
382
580
  );
383
581
  if (borderWidthMatch && (borderWidthMatch[2] || !borderWidthMatch[1] && baseClass === "border")) {
384
582
  const side = borderWidthMatch[1] ? "-" + borderWidthMatch[1] + "-w" : "-w";
385
583
  const width = borderWidthMatch[2] || "1";
386
- return {
584
+ return attachExtra({
387
585
  cat: "visual",
388
586
  val: prefix + "border" + side + ":" + getBorderWidth(width, exact)
389
- };
587
+ });
390
588
  }
391
- const positionMatch = baseClass.match(/^(top|right|bottom|left|inset|inset-x|inset-y)-(\d+|px|auto|full|\[.+\])$/);
589
+ const positionMatch = baseClass.match(/^(top|right|bottom|left|inset|inset-x|inset-y)-(\d+|px|auto|full|1\/2|1\/3|2\/3|1\/4|2\/4|3\/4|\[.+\])$/);
392
590
  if (positionMatch) {
393
591
  const prop = positionMatch[1];
394
592
  let val = positionMatch[2];
395
- if (val === "0") {
396
- val = "none";
397
- } else if (val.startsWith("[") && val.endsWith("]")) {
593
+ if (val.startsWith("[") && val.endsWith("]")) {
594
+ } else if (fractionScale[val]) {
595
+ val = fractionScale[val];
596
+ } else if (val === "0") {
597
+ val = "0";
398
598
  } else {
399
599
  val = getSpacing(val, exact);
400
600
  }
401
- return { cat: "layout", val: prefix + prop + ":" + val };
601
+ return attachExtra({ cat: "layout", val: prefix + prop + ":" + val });
602
+ }
603
+ const translateMatch = baseClass.match(/^(-?)translate-([xy])-(\d+|px|full|1\/2|1\/3|2\/3|1\/4|2\/4|3\/4|\[.+\])$/);
604
+ if (translateMatch) {
605
+ const isNeg = translateMatch[1] === "-";
606
+ const axis = translateMatch[2];
607
+ let val = translateMatch[3];
608
+ if (val.startsWith("[") && val.endsWith("]")) {
609
+ if (isNeg) {
610
+ const inner = val.slice(1, -1);
611
+ val = `[-${inner}]`;
612
+ }
613
+ } else if (fractionScale[val]) {
614
+ val = fractionScale[val];
615
+ if (isNeg) val = `-${val}`;
616
+ } else if (val === "0") {
617
+ val = "0";
618
+ } else {
619
+ val = getSpacing(val, exact);
620
+ if (isNeg) val = `-${val}`;
621
+ }
622
+ return attachExtra({ cat: "visual", val: prefix + `translate-${axis}:${val}` });
402
623
  }
403
624
  if (baseClass === "outline-none") {
404
- return { cat: "visual", val: prefix + "outline:none" };
625
+ return attachExtra({ cat: "visual", val: prefix + "outline:none" });
405
626
  }
406
627
  const orderMatch = baseClass.match(/^order-(\d+|first|last|none)$/);
407
628
  if (orderMatch) {
408
- return { cat: "layout", val: prefix + "order:" + orderMatch[1] };
629
+ return attachExtra({ cat: "layout", val: prefix + "order:" + orderMatch[1] });
630
+ }
631
+ const zIndexMatch = baseClass.match(/^-?z-(\d+|auto)$/);
632
+ if (zIndexMatch) {
633
+ const isNeg = baseClass.startsWith("-");
634
+ const val = zIndexMatch[1];
635
+ let zIndexVal = zIndexScale[val] || val;
636
+ if (isNeg) {
637
+ zIndexVal = `-${zIndexVal}`;
638
+ }
639
+ return attachExtra({ cat: "layout", val: prefix + "z:" + zIndexVal });
640
+ }
641
+ const basisMatch = baseClass.match(/^basis-(\[.+\]|\d+\.?\d*|auto|full|1\/2|1\/3|2\/3|1\/4|2\/4|3\/4)$/);
642
+ if (basisMatch) {
643
+ let val = basisMatch[1];
644
+ if (val.startsWith("[") && val.endsWith("]")) {
645
+ } else if (fractionScale[val]) {
646
+ val = fractionScale[val];
647
+ } else if (val === "0") {
648
+ val = "0";
649
+ }
650
+ return attachExtra({ cat: "layout", val: prefix + "basis:" + val });
409
651
  }
410
652
  const gridColsMatch = baseClass.match(/^grid-cols-(\d+|none)$/);
411
653
  if (gridColsMatch) {
412
- return { cat: "layout", val: prefix + "grid-cols:" + gridColsMatch[1] };
654
+ return attachExtra({ cat: "layout", val: prefix + "grid-cols:" + gridColsMatch[1] });
413
655
  }
414
656
  const colSpanMatch = baseClass.match(/^col-span-(\d+|full)$/);
415
657
  if (colSpanMatch) {
416
- return { cat: "layout", val: prefix + "col-span:" + colSpanMatch[1] };
658
+ return attachExtra({ cat: "layout", val: prefix + "col-span:" + colSpanMatch[1] });
417
659
  }
418
660
  const gridRowsMatch = baseClass.match(/^grid-rows-(\d+|none)$/);
419
661
  if (gridRowsMatch) {
420
- return { cat: "layout", val: prefix + "grid-rows:" + gridRowsMatch[1] };
662
+ return attachExtra({ cat: "layout", val: prefix + "grid-rows:" + gridRowsMatch[1] });
421
663
  }
422
664
  const rowSpanMatch = baseClass.match(/^row-span-(\d+|full)$/);
423
665
  if (rowSpanMatch) {
424
- return { cat: "layout", val: prefix + "row-span:" + rowSpanMatch[1] };
666
+ return attachExtra({ cat: "layout", val: prefix + "row-span:" + rowSpanMatch[1] });
425
667
  }
426
668
  const opacityMatch = baseClass.match(/^opacity-(\d+)$/);
427
669
  if (opacityMatch) {
428
- return { cat: "visual", val: prefix + "opacity:" + opacityMatch[1] };
670
+ return attachExtra({ cat: "visual", val: prefix + "opacity:" + opacityMatch[1] });
429
671
  }
430
672
  const bgGradientMatch = baseClass.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);
431
673
  if (bgGradientMatch) {
432
- return { cat: "visual", val: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] };
674
+ return attachExtra({ cat: "visual", val: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] });
433
675
  }
434
676
  const fromMatch = baseClass.match(/^from-(.+)$/);
435
677
  if (fromMatch) {
436
- return { cat: "visual", val: prefix + "from:" + fromMatch[1] };
678
+ return attachExtra({ cat: "visual", val: prefix + "from:" + fromMatch[1] });
437
679
  }
438
680
  const viaMatch = baseClass.match(/^via-(.+)$/);
439
681
  if (viaMatch) {
440
- return { cat: "visual", val: prefix + "via:" + viaMatch[1] };
682
+ return attachExtra({ cat: "visual", val: prefix + "via:" + viaMatch[1] });
441
683
  }
442
684
  const toMatch = baseClass.match(/^to-(.+)$/);
443
685
  if (toMatch) {
444
- return { cat: "visual", val: prefix + "to:" + toMatch[1] };
686
+ return attachExtra({ cat: "visual", val: prefix + "to:" + toMatch[1] });
445
687
  }
446
688
  const transitionMatch = baseClass.match(/^transition(?:-(all|colors|opacity|shadow|transform|none))?$/);
447
689
  if (transitionMatch) {
448
690
  const type = transitionMatch[1] || "all";
449
- return { cat: "visual", val: prefix + "transition:" + type };
691
+ return attachExtra({ cat: "visual", val: prefix + "transition:" + type });
450
692
  }
451
693
  const durationMatch = baseClass.match(/^duration-(\d+)$/);
452
694
  if (durationMatch) {
@@ -459,17 +701,17 @@
459
701
  else if (ms <= 300) durationVal = "slow";
460
702
  else if (ms <= 500) durationVal = "slower";
461
703
  else durationVal = "lazy";
462
- return { cat: "visual", val: prefix + "duration:" + durationVal };
704
+ return attachExtra({ cat: "visual", val: prefix + "duration:" + durationVal });
463
705
  }
464
706
  const easeMatch = baseClass.match(/^ease-(linear|in|out|in-out)$/);
465
707
  if (easeMatch) {
466
- return { cat: "visual", val: prefix + "ease:" + easeMatch[1] };
708
+ return attachExtra({ cat: "visual", val: prefix + "ease:" + easeMatch[1] });
467
709
  }
468
710
  const ringMatch = baseClass.match(/^ring(?:-(\d+))?$/);
469
711
  if (ringMatch) {
470
712
  const width = ringMatch[1] || "3";
471
713
  if (width === "0") {
472
- return { cat: "visual", val: prefix + "ring:none" };
714
+ return attachExtra({ cat: "visual", val: prefix + "ring:none" });
473
715
  }
474
716
  const ringScale = {
475
717
  "1": "thin",
@@ -479,100 +721,219 @@
479
721
  "8": "big"
480
722
  };
481
723
  const scale = ringScale[width] || `[${width}px]`;
482
- return { cat: "visual", val: prefix + "ring:" + scale };
724
+ return attachExtra({ cat: "visual", val: prefix + "ring:" + scale });
483
725
  }
484
726
  const ringOffsetMatch = baseClass.match(/^ring-offset-(\d+)$/);
485
727
  if (ringOffsetMatch) {
486
- return { cat: "visual", val: prefix + "ring-offset:" + ringOffsetMatch[1] };
728
+ return attachExtra({ cat: "visual", val: prefix + "ring-offset:" + ringOffsetMatch[1] });
487
729
  }
488
730
  const ringColorMatch = baseClass.match(/^ring-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/);
489
731
  if (ringColorMatch) {
490
- return { cat: "visual", val: prefix + "ring-color:" + ringColorMatch[1] };
732
+ return attachExtra({ cat: "visual", val: prefix + "ring-color:" + ringColorMatch[1] });
491
733
  }
492
734
  const divideXMatch = baseClass.match(/^divide-x-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/);
493
735
  if (divideXMatch) {
494
- return {
736
+ return attachExtra({
495
737
  cat: "visual",
496
738
  val: prefix + "divide-x:" + divideXMatch[1]
497
- };
739
+ });
498
740
  }
499
741
  const divideYMatch = baseClass.match(/^divide-y-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/);
500
742
  if (divideYMatch) {
501
- return {
743
+ return attachExtra({
502
744
  cat: "visual",
503
745
  val: prefix + "divide-y:" + divideYMatch[1]
504
- };
746
+ });
505
747
  }
506
748
  const divideColorMatch = baseClass.match(/^divide-((?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|white|black)(?:-\d+)?)$/);
507
749
  if (divideColorMatch) {
508
- return {
750
+ return attachExtra({
509
751
  cat: "visual",
510
752
  val: prefix + "divide:" + divideColorMatch[1]
511
- };
753
+ });
512
754
  }
513
755
  const divideWidthMatch = baseClass.match(/^divide-(\d+)$/);
514
756
  if (divideWidthMatch) {
515
- return {
757
+ return attachExtra({
516
758
  cat: "visual",
517
759
  val: prefix + "divide-w:" + getBorderWidth(divideWidthMatch[1], exact)
518
- };
760
+ });
519
761
  }
520
762
  if (baseClass === "divide-x-reverse") {
521
- return { cat: "visual", val: prefix + "divide-x:reverse" };
763
+ return attachExtra({ cat: "visual", val: prefix + "divide-x:reverse" });
522
764
  }
523
765
  if (baseClass === "divide-y-reverse") {
524
- return { cat: "visual", val: prefix + "divide-y:reverse" };
766
+ return attachExtra({ cat: "visual", val: prefix + "divide-y:reverse" });
525
767
  }
526
768
  const divideXWidthMatch = baseClass.match(/^divide-x-(\d+)$/);
527
769
  if (divideXWidthMatch) {
528
- return {
770
+ return attachExtra({
529
771
  cat: "visual",
530
772
  val: prefix + "divide-x-w:" + getBorderWidth(divideXWidthMatch[1], exact)
531
- };
773
+ });
532
774
  }
533
775
  if (baseClass === "divide-x") {
534
- return { cat: "visual", val: prefix + "divide-x-w:thin" };
776
+ return attachExtra({ cat: "visual", val: prefix + "divide-x-w:thin" });
535
777
  }
536
778
  if (baseClass === "divide-y") {
537
- return { cat: "visual", val: prefix + "divide-y-w:thin" };
779
+ return attachExtra({ cat: "visual", val: prefix + "divide-y-w:thin" });
538
780
  }
539
781
  const divideYWidthMatch = baseClass.match(/^divide-y-(\d+)$/);
540
782
  if (divideYWidthMatch) {
541
- return {
783
+ return attachExtra({
542
784
  cat: "visual",
543
785
  val: prefix + "divide-y-w:" + getBorderWidth(divideYWidthMatch[1], exact)
544
- };
786
+ });
545
787
  }
546
788
  const divideStyleMatch = baseClass.match(/^divide-(solid|dashed|dotted|double|none)$/);
547
789
  if (divideStyleMatch) {
548
- return {
790
+ return attachExtra({
549
791
  cat: "visual",
550
792
  val: prefix + "divide-style:" + divideStyleMatch[1]
551
793
  // Fixed category from 'color' to 'visual'
794
+ });
795
+ }
796
+ const borderStyleMatch = baseClass.match(/^border-(solid|dashed|dotted|double|none)$/);
797
+ if (borderStyleMatch) {
798
+ return attachExtra({
799
+ cat: "visual",
800
+ val: prefix + "border-style:" + borderStyleMatch[1]
801
+ });
802
+ }
803
+ const blurMatch = baseClass.match(/^blur-(0|sm|md|lg|xl|2xl|3xl)$/);
804
+ if (blurMatch) {
805
+ const blurScale = {
806
+ "0": "none",
807
+ "sm": "tiny",
808
+ "md": "small",
809
+ "lg": "medium",
810
+ "xl": "big",
811
+ "2xl": "giant",
812
+ "3xl": "vast"
813
+ };
814
+ return attachExtra({
815
+ cat: "visual",
816
+ val: prefix + "blur:" + blurScale[blurMatch[1]]
817
+ });
818
+ }
819
+ const brightnessMatch = baseClass.match(/^brightness-(0|50|75|90|95|100|105|110|125|150|200)$/);
820
+ if (brightnessMatch) {
821
+ const brightnessScale = {
822
+ "0": "dim",
823
+ "50": "dim",
824
+ "75": "dark",
825
+ "90": "dark",
826
+ "95": "dark",
827
+ "100": "normal",
828
+ "105": "bright",
829
+ "110": "bright",
830
+ "125": "vivid",
831
+ "150": "vivid",
832
+ "200": "vivid"
833
+ };
834
+ return attachExtra({
835
+ cat: "visual",
836
+ val: prefix + "brightness:" + brightnessScale[brightnessMatch[1]]
837
+ });
838
+ }
839
+ const contrastMatch = baseClass.match(/^contrast-(0|50|75|100|125|150|200)$/);
840
+ if (contrastMatch) {
841
+ const contrastScale = {
842
+ "0": "low",
843
+ "50": "low",
844
+ "75": "reduced",
845
+ "100": "normal",
846
+ "125": "high",
847
+ "150": "high",
848
+ "200": "max"
552
849
  };
850
+ return attachExtra({
851
+ cat: "visual",
852
+ val: prefix + "contrast:" + contrastScale[contrastMatch[1]]
853
+ });
854
+ }
855
+ const grayscaleMatch = baseClass.match(/^grayscale(0)?$/);
856
+ if (grayscaleMatch) {
857
+ const val = grayscaleMatch[1] === "0" ? "none" : "full";
858
+ return attachExtra({
859
+ cat: "visual",
860
+ val: prefix + "grayscale:" + val
861
+ });
862
+ }
863
+ const hueRotateMatch = baseClass.match(/^hue-rotate-(0|15|30|60|90|180)$/);
864
+ if (hueRotateMatch) {
865
+ return attachExtra({
866
+ cat: "visual",
867
+ val: prefix + "hue-rotate:" + hueRotateMatch[1]
868
+ });
869
+ }
870
+ const invertMatch = baseClass.match(/^invert(0)?$/);
871
+ if (invertMatch) {
872
+ const val = invertMatch[1] === "0" ? "none" : "full";
873
+ return attachExtra({
874
+ cat: "visual",
875
+ val: prefix + "invert:" + val
876
+ });
877
+ }
878
+ const saturateMatch = baseClass.match(/^saturate-(0|50|100|150|200)$/);
879
+ if (saturateMatch) {
880
+ const saturateScale = {
881
+ "0": "none",
882
+ "50": "low",
883
+ "100": "normal",
884
+ "150": "high",
885
+ "200": "vivid"
886
+ };
887
+ return attachExtra({
888
+ cat: "visual",
889
+ val: prefix + "saturate:" + saturateScale[saturateMatch[1]]
890
+ });
891
+ }
892
+ const sepiaMatch = baseClass.match(/^sepia(0)?$/);
893
+ if (sepiaMatch) {
894
+ const val = sepiaMatch[1] === "0" ? "none" : "full";
895
+ return attachExtra({
896
+ cat: "visual",
897
+ val: prefix + "sepia:" + val
898
+ });
899
+ }
900
+ const animateMatch = baseClass.match(/^animate-(none|spin|ping|pulse|bounce)$/);
901
+ if (animateMatch) {
902
+ return attachExtra({
903
+ cat: "visual",
904
+ val: prefix + "animate:" + animateMatch[1]
905
+ });
553
906
  }
554
907
  return null;
555
908
  }
556
909
  function convertClasses(classString, exact) {
557
910
  const classes = classString.trim().split(/\s+/).filter((c) => c);
558
- const layout = [], space = [], visual = [], unknown = [];
911
+ const layout = [], space = [], visual = [], interact = [], listens = [], unknown = [];
912
+ const pushUnique = (arr, val) => {
913
+ if (!arr.includes(val)) arr.push(val);
914
+ };
559
915
  for (const cls of classes) {
560
916
  const result = convertClass(cls, exact);
561
917
  if (result) {
562
- if (result.cat === "layout") layout.push(result.val);
563
- else if (result.cat === "space") space.push(result.val);
564
- else if (result.cat === "visual") visual.push(result.val);
918
+ const results = Array.isArray(result) ? result : [result];
919
+ for (const res of results) {
920
+ if (res.cat === "layout") pushUnique(layout, res.val);
921
+ else if (res.cat === "space") pushUnique(space, res.val);
922
+ else if (res.cat === "visual") pushUnique(visual, res.val);
923
+ else if (res.cat === "interact") pushUnique(interact, res.val);
924
+ else if (res.cat === "listens") pushUnique(listens, res.val);
925
+ }
565
926
  } else {
566
927
  unknown.push(cls);
567
928
  }
568
929
  }
569
- return { layout, space, visual, unknown };
930
+ return { layout, space, visual, interact, listens, unknown };
570
931
  }
571
932
  function convertHTML(html, exact) {
572
933
  return html.replace(
573
934
  /class=(['"])([^"']+)\1/g,
574
935
  (match, quote, classValue) => {
575
- const { layout, space, visual, unknown } = convertClasses(
936
+ const { layout, space, visual, interact, listens, unknown } = convertClasses(
576
937
  classValue,
577
938
  exact
578
939
  );
@@ -580,6 +941,8 @@
580
941
  if (layout.length) attrs.push(`layout="${layout.join(" ")}"`);
581
942
  if (space.length) attrs.push(`space="${space.join(" ")}"`);
582
943
  if (visual.length) attrs.push(`visual="${visual.join(" ")}"`);
944
+ if (interact.length) attrs.push(`interact="${interact.join(" ")}"`);
945
+ if (listens.length) attrs.push(`listens="${listens.join(" ")}"`);
583
946
  if (unknown.length) attrs.push(`class="${unknown.join(" ")}"`);
584
947
  return attrs.join(" ") || 'class=""';
585
948
  }