@bookklik/senangstart-css 0.2.8 → 0.2.9

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 (46) hide show
  1. package/dist/senangstart-css.js +2510 -1927
  2. package/dist/senangstart-css.min.js +211 -208
  3. package/dist/senangstart-tw.js +170 -73
  4. package/dist/senangstart-tw.min.js +1 -1
  5. package/docs/guide/configuration.md +2 -2
  6. package/docs/guide/states.md +60 -0
  7. package/docs/ms/guide/configuration.md +2 -2
  8. package/docs/ms/guide/states.md +60 -0
  9. package/docs/ms/reference/colors.md +2 -2
  10. package/docs/ms/reference/space/height.md +10 -10
  11. package/docs/ms/reference/space/width.md +12 -12
  12. package/docs/public/assets/senangstart-css.min.js +211 -208
  13. package/docs/public/llms.txt +28 -0
  14. package/docs/reference/colors.md +2 -2
  15. package/docs/reference/space/height.md +10 -10
  16. package/docs/reference/space/width.md +12 -12
  17. package/package.json +1 -1
  18. package/public/senangstart.css +1 -1
  19. package/scripts/convert-tailwind.js +191 -68
  20. package/scripts/generate-llms-txt.js +28 -0
  21. package/src/cdn/senangstart-engine.js +37 -1927
  22. package/src/cdn/tw-conversion-engine.js +203 -74
  23. package/src/compiler/generators/css.js +300 -54
  24. package/src/compiler/parser.js +14 -4
  25. package/src/config/defaults.js +1 -1
  26. package/src/core/constants.js +5 -3
  27. package/src/definitions/index.js +3 -2
  28. package/src/definitions/layout.js +2 -2
  29. package/src/definitions/space.js +45 -19
  30. package/src/index.js +47 -0
  31. package/templates/senangstart.config.js +1 -1
  32. package/tests/helpers/test-utils.js +1 -1
  33. package/tests/integration/compiler.test.js +12 -1
  34. package/tests/unit/compiler/generators/css.coverage.test.js +833 -0
  35. package/tests/unit/compiler/generators/css.test.js +1418 -1
  36. package/tests/unit/compiler/generators/preflight.test.js +31 -0
  37. package/tests/unit/compiler/parser.test.js +26 -0
  38. package/tests/unit/config/defaults.test.js +2 -2
  39. package/tests/unit/convert-tailwind.cli.test.js +95 -0
  40. package/tests/unit/convert-tailwind.coverage.test.js +225 -0
  41. package/tests/unit/convert-tailwind.test.js +49 -20
  42. package/tests/unit/core/tokenizer-core.test.js +102 -0
  43. package/tests/unit/definitions/index.test.js +108 -0
  44. package/tests/unit/definitions/layout_definitions.test.js +40 -0
  45. package/tests/unit/utils/common.test.js +26 -0
  46. package/scripts/bundle-jit.js +0 -45
@@ -142,6 +142,22 @@
142
142
  "9xl": "hero"
143
143
  // 8rem → hero
144
144
  };
145
+ var fractionScale = {
146
+ "1/2": "half",
147
+ // 50%
148
+ "1/3": "third",
149
+ // 33.33%
150
+ "2/3": "third-2x",
151
+ // 66.67%
152
+ "1/4": "quarter",
153
+ // 25%
154
+ "2/4": "half",
155
+ // 50% (alias)
156
+ "3/4": "quarter-3x",
157
+ // 75%
158
+ "full": "full"
159
+ // 100%
160
+ };
145
161
  var layoutMappings = {
146
162
  container: "container",
147
163
  flex: "flex",
@@ -252,40 +268,73 @@
252
268
  }
253
269
  function convertClass(twClass, exact) {
254
270
  const prefixMatch = twClass.match(
255
- /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|focus-visible:|active:|disabled:|dark:)(.+)$/
271
+ /^(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
272
  );
257
- let prefix = "", baseClass = twClass;
273
+ let prefix = "", baseClass = twClass, extraAttr = null;
258
274
  if (prefixMatch) {
259
275
  const rawPrefix = prefixMatch[1].slice(0, -1);
260
276
  if (["sm", "md", "lg", "xl", "2xl"].includes(rawPrefix)) {
261
277
  prefix = `tw-${rawPrefix}:`;
278
+ } else if (rawPrefix.startsWith("group-") || rawPrefix.startsWith("peer-")) {
279
+ const stateMap = {
280
+ "hover": "hover",
281
+ "focus": "focus",
282
+ // or focus-within if we strictly follow group logic, but SenangStart group logic handles mapped state triggers
283
+ "active": "active",
284
+ "open": "expanded",
285
+ // map open -> expanded
286
+ "check": "checked"
287
+ // map check -> checked
288
+ };
289
+ const variant = rawPrefix.split("-")[1];
290
+ const mappedState = stateMap[variant] || variant;
291
+ prefix = `${mappedState}:`;
292
+ if (rawPrefix.startsWith("peer-")) {
293
+ extraAttr = { cat: "listens", val: "peer" };
294
+ }
262
295
  } else {
263
296
  prefix = prefixMatch[1];
264
297
  }
265
298
  baseClass = prefixMatch[2];
266
299
  }
300
+ if (baseClass === "group") {
301
+ return { cat: "layout", val: "hoverable focusable pressable expandable" };
302
+ }
303
+ if (baseClass === "peer") {
304
+ return [
305
+ { cat: "layout", val: "hoverable focusable pressable expandable" },
306
+ { cat: "interact", val: "peer" }
307
+ ];
308
+ }
309
+ const attachExtra = (result) => {
310
+ if (!result) return null;
311
+ if (extraAttr) {
312
+ return Array.isArray(result) ? [...result, extraAttr] : [result, extraAttr];
313
+ }
314
+ return result;
315
+ };
267
316
  if (layoutMappings[baseClass])
268
- return { cat: "layout", val: prefix + layoutMappings[baseClass] };
317
+ return attachExtra({ cat: "layout", val: prefix + layoutMappings[baseClass] });
269
318
  if (visualKeywords[baseClass])
270
- return { cat: "visual", val: prefix + visualKeywords[baseClass] };
319
+ return attachExtra({ cat: "visual", val: prefix + visualKeywords[baseClass] });
271
320
  const textColorMatch = baseClass.match(
272
321
  /^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
322
  );
274
323
  if (textColorMatch)
275
- return { cat: "visual", val: prefix + "text:" + textColorMatch[1] };
324
+ return attachExtra({ cat: "visual", val: prefix + "text:" + textColorMatch[1] });
276
325
  if (["text-left", "text-center", "text-right", "text-justify"].includes(
277
326
  baseClass
278
327
  ))
279
- return {
328
+ return attachExtra({
280
329
  cat: "visual",
281
330
  val: prefix + "text:" + baseClass.replace("text-", "")
282
- };
331
+ });
283
332
  const textSizeMatch = baseClass.match(
284
333
  /^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/
285
334
  );
286
335
  if (textSizeMatch) {
287
336
  const size = exact ? `tw-${textSizeMatch[1]}` : fontSizeScale[textSizeMatch[1]] || textSizeMatch[1];
288
- return { cat: "visual", val: prefix + "text-size:" + size };
337
+ return attachExtra({ cat: "visual", val: prefix + "text-size:" + size });
289
338
  }
290
339
  const bgMatch = baseClass.match(
291
340
  /^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 +342,46 @@
293
342
  if (bgMatch) {
294
343
  const colorVal = bgMatch[1];
295
344
  if (colorVal === "transparent") {
296
- return { cat: "visual", val: prefix + "bg:[transparent]" };
345
+ return attachExtra({ cat: "visual", val: prefix + "bg:transparent" });
297
346
  }
298
347
  if (colorVal === "current") {
299
- return { cat: "visual", val: prefix + "bg:[currentColor]" };
348
+ return attachExtra({ cat: "visual", val: prefix + "bg:currentColor" });
300
349
  }
301
350
  if (colorVal === "inherit") {
302
- return { cat: "visual", val: prefix + "bg:[inherit]" };
351
+ return attachExtra({ cat: "visual", val: prefix + "bg:inherit" });
303
352
  }
304
- return { cat: "visual", val: prefix + "bg:" + colorVal };
353
+ return attachExtra({ cat: "visual", val: prefix + "bg:" + colorVal });
305
354
  }
306
355
  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+)?)$/
356
+ /^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
357
  );
309
- if (borderColorMatch)
310
- return {
358
+ if (borderColorMatch) {
359
+ let colorVal = borderColorMatch[1];
360
+ if (colorVal === "current") colorVal = "currentColor";
361
+ return attachExtra({
311
362
  cat: "visual",
312
- val: prefix + "border:" + borderColorMatch[1]
313
- };
363
+ val: prefix + "border:" + colorVal
364
+ });
365
+ }
366
+ const borderSideColorMatch = baseClass.match(
367
+ /^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+)?)$/
368
+ );
369
+ if (borderSideColorMatch) {
370
+ const side = borderSideColorMatch[1];
371
+ let colorVal = borderSideColorMatch[2];
372
+ if (colorVal === "current") colorVal = "currentColor";
373
+ return attachExtra({
374
+ cat: "visual",
375
+ val: prefix + `border-${side}:${colorVal}`
376
+ });
377
+ }
314
378
  const paddingMatch = baseClass.match(/^p([trblxy])?-(.+)$/);
315
379
  if (paddingMatch) {
316
380
  const side = paddingMatch[1] ? "-" + paddingMatch[1] : "";
317
- return {
381
+ return attachExtra({
318
382
  cat: "space",
319
383
  val: prefix + "p" + side + ":" + getSpacing(paddingMatch[2], exact)
320
- };
384
+ });
321
385
  }
322
386
  const marginMatch = baseClass.match(
323
387
  /^-?m([trblxy])?-(\[.+\]|\d+\.?\d*|px|auto|full|screen)$/
@@ -334,15 +398,15 @@
334
398
  val = `-${val}`;
335
399
  }
336
400
  }
337
- return { cat: "space", val: prefix + "m" + side + ":" + val };
401
+ return attachExtra({ cat: "space", val: prefix + "m" + side + ":" + val });
338
402
  }
339
403
  const gapMatch = baseClass.match(/^gap-([xy])?-?(.+)$/);
340
404
  if (gapMatch) {
341
405
  const axis = gapMatch[1] ? "-" + gapMatch[1] : "";
342
- return {
406
+ return attachExtra({
343
407
  cat: "space",
344
408
  val: prefix + "g" + axis + ":" + getSpacing(gapMatch[2], exact)
345
- };
409
+ });
346
410
  }
347
411
  const widthMatch = baseClass.match(/^(min-w|max-w|w)-(.+)$/);
348
412
  if (widthMatch) {
@@ -350,7 +414,7 @@
350
414
  const rawVal = widthMatch[2];
351
415
  const specialWidthVals = { "max": "[max-content]", "min": "[min-content]", "fit": "[fit-content]", "prose": "[65ch]" };
352
416
  const val = specialWidthVals[rawVal] || getSpacing(rawVal, exact);
353
- return { cat: "space", val: prefix + prop + ":" + val };
417
+ return attachExtra({ cat: "space", val: prefix + prop + ":" + val });
354
418
  }
355
419
  const heightMatch = baseClass.match(/^(min-h|max-h|h)-(.+)$/);
356
420
  if (heightMatch) {
@@ -358,95 +422,118 @@
358
422
  const rawVal = heightMatch[2];
359
423
  const specialHeightVals = { "screen": "[100vh]", "svh": "[100svh]", "lvh": "[100lvh]", "dvh": "[100dvh]", "max": "[max-content]", "min": "[min-content]", "fit": "[fit-content]" };
360
424
  const val = specialHeightVals[rawVal] || getSpacing(rawVal, exact);
361
- return { cat: "space", val: prefix + prop + ":" + val };
425
+ return attachExtra({ cat: "space", val: prefix + prop + ":" + val });
362
426
  }
363
427
  const roundedMatch = baseClass.match(/^rounded(?:-(.+))?$/);
364
428
  if (roundedMatch) {
365
429
  const size = roundedMatch[1] || "";
366
430
  const scale = exact ? size === "" ? "tw-DEFAULT" : `tw-${size}` : radiusScale[size] || "medium";
367
- return { cat: "visual", val: prefix + "rounded:" + scale };
431
+ return attachExtra({ cat: "visual", val: prefix + "rounded:" + scale });
368
432
  }
369
433
  const shadowMatch = baseClass.match(/^shadow(?:-(.+))?$/);
370
434
  if (shadowMatch) {
371
435
  const size = shadowMatch[1] || "";
372
436
  const scale = exact ? size === "" ? "tw-DEFAULT" : `tw-${size}` : shadowScale[size] || "medium";
373
- return { cat: "visual", val: prefix + "shadow:" + scale };
437
+ return attachExtra({ cat: "visual", val: prefix + "shadow:" + scale });
374
438
  }
375
439
  const fontWeightMatch = baseClass.match(
376
440
  /^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/
377
441
  );
378
442
  if (fontWeightMatch)
379
- return { cat: "visual", val: prefix + "font:tw-" + fontWeightMatch[1] };
443
+ return attachExtra({ cat: "visual", val: prefix + "font:tw-" + fontWeightMatch[1] });
380
444
  const borderWidthMatch = baseClass.match(
381
445
  /^border(?:-([trblxy]))?(?:-(\d+))?$/
382
446
  );
383
447
  if (borderWidthMatch && (borderWidthMatch[2] || !borderWidthMatch[1] && baseClass === "border")) {
384
448
  const side = borderWidthMatch[1] ? "-" + borderWidthMatch[1] + "-w" : "-w";
385
449
  const width = borderWidthMatch[2] || "1";
386
- return {
450
+ return attachExtra({
387
451
  cat: "visual",
388
452
  val: prefix + "border" + side + ":" + getBorderWidth(width, exact)
389
- };
453
+ });
390
454
  }
391
- const positionMatch = baseClass.match(/^(top|right|bottom|left|inset|inset-x|inset-y)-(\d+|px|auto|full|\[.+\])$/);
455
+ 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
456
  if (positionMatch) {
393
457
  const prop = positionMatch[1];
394
458
  let val = positionMatch[2];
395
459
  if (val === "0") {
396
460
  val = "none";
397
461
  } else if (val.startsWith("[") && val.endsWith("]")) {
462
+ } else if (fractionScale[val]) {
463
+ val = fractionScale[val];
398
464
  } else {
399
465
  val = getSpacing(val, exact);
400
466
  }
401
- return { cat: "layout", val: prefix + prop + ":" + val };
467
+ return attachExtra({ cat: "layout", val: prefix + prop + ":" + val });
468
+ }
469
+ const translateMatch = baseClass.match(/^(-?)translate-([xy])-(\d+|px|full|1\/2|1\/3|2\/3|1\/4|2\/4|3\/4|\[.+\])$/);
470
+ if (translateMatch) {
471
+ const isNeg = translateMatch[1] === "-";
472
+ const axis = translateMatch[2];
473
+ let val = translateMatch[3];
474
+ if (val.startsWith("[") && val.endsWith("]")) {
475
+ if (isNeg) {
476
+ const inner = val.slice(1, -1);
477
+ val = `[-${inner}]`;
478
+ }
479
+ } else if (fractionScale[val]) {
480
+ val = fractionScale[val];
481
+ if (isNeg) val = `-${val}`;
482
+ } else if (val === "0") {
483
+ val = "0";
484
+ } else {
485
+ val = getSpacing(val, exact);
486
+ if (isNeg) val = `-${val}`;
487
+ }
488
+ return attachExtra({ cat: "visual", val: prefix + `translate-${axis}:${val}` });
402
489
  }
403
490
  if (baseClass === "outline-none") {
404
- return { cat: "visual", val: prefix + "outline:none" };
491
+ return attachExtra({ cat: "visual", val: prefix + "outline:none" });
405
492
  }
406
493
  const orderMatch = baseClass.match(/^order-(\d+|first|last|none)$/);
407
494
  if (orderMatch) {
408
- return { cat: "layout", val: prefix + "order:" + orderMatch[1] };
495
+ return attachExtra({ cat: "layout", val: prefix + "order:" + orderMatch[1] });
409
496
  }
410
497
  const gridColsMatch = baseClass.match(/^grid-cols-(\d+|none)$/);
411
498
  if (gridColsMatch) {
412
- return { cat: "layout", val: prefix + "grid-cols:" + gridColsMatch[1] };
499
+ return attachExtra({ cat: "layout", val: prefix + "grid-cols:" + gridColsMatch[1] });
413
500
  }
414
501
  const colSpanMatch = baseClass.match(/^col-span-(\d+|full)$/);
415
502
  if (colSpanMatch) {
416
- return { cat: "layout", val: prefix + "col-span:" + colSpanMatch[1] };
503
+ return attachExtra({ cat: "layout", val: prefix + "col-span:" + colSpanMatch[1] });
417
504
  }
418
505
  const gridRowsMatch = baseClass.match(/^grid-rows-(\d+|none)$/);
419
506
  if (gridRowsMatch) {
420
- return { cat: "layout", val: prefix + "grid-rows:" + gridRowsMatch[1] };
507
+ return attachExtra({ cat: "layout", val: prefix + "grid-rows:" + gridRowsMatch[1] });
421
508
  }
422
509
  const rowSpanMatch = baseClass.match(/^row-span-(\d+|full)$/);
423
510
  if (rowSpanMatch) {
424
- return { cat: "layout", val: prefix + "row-span:" + rowSpanMatch[1] };
511
+ return attachExtra({ cat: "layout", val: prefix + "row-span:" + rowSpanMatch[1] });
425
512
  }
426
513
  const opacityMatch = baseClass.match(/^opacity-(\d+)$/);
427
514
  if (opacityMatch) {
428
- return { cat: "visual", val: prefix + "opacity:" + opacityMatch[1] };
515
+ return attachExtra({ cat: "visual", val: prefix + "opacity:" + opacityMatch[1] });
429
516
  }
430
517
  const bgGradientMatch = baseClass.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);
431
518
  if (bgGradientMatch) {
432
- return { cat: "visual", val: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] };
519
+ return attachExtra({ cat: "visual", val: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] });
433
520
  }
434
521
  const fromMatch = baseClass.match(/^from-(.+)$/);
435
522
  if (fromMatch) {
436
- return { cat: "visual", val: prefix + "from:" + fromMatch[1] };
523
+ return attachExtra({ cat: "visual", val: prefix + "from:" + fromMatch[1] });
437
524
  }
438
525
  const viaMatch = baseClass.match(/^via-(.+)$/);
439
526
  if (viaMatch) {
440
- return { cat: "visual", val: prefix + "via:" + viaMatch[1] };
527
+ return attachExtra({ cat: "visual", val: prefix + "via:" + viaMatch[1] });
441
528
  }
442
529
  const toMatch = baseClass.match(/^to-(.+)$/);
443
530
  if (toMatch) {
444
- return { cat: "visual", val: prefix + "to:" + toMatch[1] };
531
+ return attachExtra({ cat: "visual", val: prefix + "to:" + toMatch[1] });
445
532
  }
446
533
  const transitionMatch = baseClass.match(/^transition(?:-(all|colors|opacity|shadow|transform|none))?$/);
447
534
  if (transitionMatch) {
448
535
  const type = transitionMatch[1] || "all";
449
- return { cat: "visual", val: prefix + "transition:" + type };
536
+ return attachExtra({ cat: "visual", val: prefix + "transition:" + type });
450
537
  }
451
538
  const durationMatch = baseClass.match(/^duration-(\d+)$/);
452
539
  if (durationMatch) {
@@ -459,17 +546,17 @@
459
546
  else if (ms <= 300) durationVal = "slow";
460
547
  else if (ms <= 500) durationVal = "slower";
461
548
  else durationVal = "lazy";
462
- return { cat: "visual", val: prefix + "duration:" + durationVal };
549
+ return attachExtra({ cat: "visual", val: prefix + "duration:" + durationVal });
463
550
  }
464
551
  const easeMatch = baseClass.match(/^ease-(linear|in|out|in-out)$/);
465
552
  if (easeMatch) {
466
- return { cat: "visual", val: prefix + "ease:" + easeMatch[1] };
553
+ return attachExtra({ cat: "visual", val: prefix + "ease:" + easeMatch[1] });
467
554
  }
468
555
  const ringMatch = baseClass.match(/^ring(?:-(\d+))?$/);
469
556
  if (ringMatch) {
470
557
  const width = ringMatch[1] || "3";
471
558
  if (width === "0") {
472
- return { cat: "visual", val: prefix + "ring:none" };
559
+ return attachExtra({ cat: "visual", val: prefix + "ring:none" });
473
560
  }
474
561
  const ringScale = {
475
562
  "1": "thin",
@@ -479,100 +566,108 @@
479
566
  "8": "big"
480
567
  };
481
568
  const scale = ringScale[width] || `[${width}px]`;
482
- return { cat: "visual", val: prefix + "ring:" + scale };
569
+ return attachExtra({ cat: "visual", val: prefix + "ring:" + scale });
483
570
  }
484
571
  const ringOffsetMatch = baseClass.match(/^ring-offset-(\d+)$/);
485
572
  if (ringOffsetMatch) {
486
- return { cat: "visual", val: prefix + "ring-offset:" + ringOffsetMatch[1] };
573
+ return attachExtra({ cat: "visual", val: prefix + "ring-offset:" + ringOffsetMatch[1] });
487
574
  }
488
575
  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
576
  if (ringColorMatch) {
490
- return { cat: "visual", val: prefix + "ring-color:" + ringColorMatch[1] };
577
+ return attachExtra({ cat: "visual", val: prefix + "ring-color:" + ringColorMatch[1] });
491
578
  }
492
579
  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
580
  if (divideXMatch) {
494
- return {
581
+ return attachExtra({
495
582
  cat: "visual",
496
583
  val: prefix + "divide-x:" + divideXMatch[1]
497
- };
584
+ });
498
585
  }
499
586
  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
587
  if (divideYMatch) {
501
- return {
588
+ return attachExtra({
502
589
  cat: "visual",
503
590
  val: prefix + "divide-y:" + divideYMatch[1]
504
- };
591
+ });
505
592
  }
506
593
  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
594
  if (divideColorMatch) {
508
- return {
595
+ return attachExtra({
509
596
  cat: "visual",
510
597
  val: prefix + "divide:" + divideColorMatch[1]
511
- };
598
+ });
512
599
  }
513
600
  const divideWidthMatch = baseClass.match(/^divide-(\d+)$/);
514
601
  if (divideWidthMatch) {
515
- return {
602
+ return attachExtra({
516
603
  cat: "visual",
517
604
  val: prefix + "divide-w:" + getBorderWidth(divideWidthMatch[1], exact)
518
- };
605
+ });
519
606
  }
520
607
  if (baseClass === "divide-x-reverse") {
521
- return { cat: "visual", val: prefix + "divide-x:reverse" };
608
+ return attachExtra({ cat: "visual", val: prefix + "divide-x:reverse" });
522
609
  }
523
610
  if (baseClass === "divide-y-reverse") {
524
- return { cat: "visual", val: prefix + "divide-y:reverse" };
611
+ return attachExtra({ cat: "visual", val: prefix + "divide-y:reverse" });
525
612
  }
526
613
  const divideXWidthMatch = baseClass.match(/^divide-x-(\d+)$/);
527
614
  if (divideXWidthMatch) {
528
- return {
615
+ return attachExtra({
529
616
  cat: "visual",
530
617
  val: prefix + "divide-x-w:" + getBorderWidth(divideXWidthMatch[1], exact)
531
- };
618
+ });
532
619
  }
533
620
  if (baseClass === "divide-x") {
534
- return { cat: "visual", val: prefix + "divide-x-w:thin" };
621
+ return attachExtra({ cat: "visual", val: prefix + "divide-x-w:thin" });
535
622
  }
536
623
  if (baseClass === "divide-y") {
537
- return { cat: "visual", val: prefix + "divide-y-w:thin" };
624
+ return attachExtra({ cat: "visual", val: prefix + "divide-y-w:thin" });
538
625
  }
539
626
  const divideYWidthMatch = baseClass.match(/^divide-y-(\d+)$/);
540
627
  if (divideYWidthMatch) {
541
- return {
628
+ return attachExtra({
542
629
  cat: "visual",
543
630
  val: prefix + "divide-y-w:" + getBorderWidth(divideYWidthMatch[1], exact)
544
- };
631
+ });
545
632
  }
546
633
  const divideStyleMatch = baseClass.match(/^divide-(solid|dashed|dotted|double|none)$/);
547
634
  if (divideStyleMatch) {
548
- return {
635
+ return attachExtra({
549
636
  cat: "visual",
550
637
  val: prefix + "divide-style:" + divideStyleMatch[1]
551
638
  // Fixed category from 'color' to 'visual'
552
- };
639
+ });
553
640
  }
554
641
  return null;
555
642
  }
556
643
  function convertClasses(classString, exact) {
557
644
  const classes = classString.trim().split(/\s+/).filter((c) => c);
558
- const layout = [], space = [], visual = [], unknown = [];
645
+ const layout = [], space = [], visual = [], interact = [], listens = [], unknown = [];
646
+ const pushUnique = (arr, val) => {
647
+ if (!arr.includes(val)) arr.push(val);
648
+ };
559
649
  for (const cls of classes) {
560
650
  const result = convertClass(cls, exact);
561
651
  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);
652
+ const results = Array.isArray(result) ? result : [result];
653
+ for (const res of results) {
654
+ if (res.cat === "layout") pushUnique(layout, res.val);
655
+ else if (res.cat === "space") pushUnique(space, res.val);
656
+ else if (res.cat === "visual") pushUnique(visual, res.val);
657
+ else if (res.cat === "interact") pushUnique(interact, res.val);
658
+ else if (res.cat === "listens") pushUnique(listens, res.val);
659
+ }
565
660
  } else {
566
661
  unknown.push(cls);
567
662
  }
568
663
  }
569
- return { layout, space, visual, unknown };
664
+ return { layout, space, visual, interact, listens, unknown };
570
665
  }
571
666
  function convertHTML(html, exact) {
572
667
  return html.replace(
573
668
  /class=(['"])([^"']+)\1/g,
574
669
  (match, quote, classValue) => {
575
- const { layout, space, visual, unknown } = convertClasses(
670
+ const { layout, space, visual, interact, listens, unknown } = convertClasses(
576
671
  classValue,
577
672
  exact
578
673
  );
@@ -580,6 +675,8 @@
580
675
  if (layout.length) attrs.push(`layout="${layout.join(" ")}"`);
581
676
  if (space.length) attrs.push(`space="${space.join(" ")}"`);
582
677
  if (visual.length) attrs.push(`visual="${visual.join(" ")}"`);
678
+ if (interact.length) attrs.push(`interact="${interact.join(" ")}"`);
679
+ if (listens.length) attrs.push(`listens="${listens.join(" ")}"`);
583
680
  if (unknown.length) attrs.push(`class="${unknown.join(" ")}"`);
584
681
  return attrs.join(" ") || 'class=""';
585
682
  }
@@ -1,2 +1,2 @@
1
1
  /* SenangStart TW v0.2.0 | MIT */
2
- (()=>{var $={0:"none",px:"thin",.5:"regular",1:"tiny",1.5:"tiny-2x",2:"small",2.5:"small-2x",3:"small-3x",3.5:"small-4x",4:"medium",5:"medium-2x",6:"medium-3x",7:"medium-4x",8:"large",9:"large-2x",10:"large-3x",11:"large-4x",12:"big",14:"big-2x",16:"big-3x",20:"big-4x",24:"giant",28:"giant-2x",32:"giant-3x",36:"giant-4x",40:"vast",44:"vast-2x",48:"vast-3x",52:"vast-4x",56:"vast-5x",60:"vast-6x",64:"vast-7x",72:"vast-8x",80:"vast-9x",96:"vast-10x",full:"[100%]",screen:"[100vw]",auto:"auto"},Q={none:"none",sm:"small","":"small",md:"small",lg:"medium",xl:"medium","2xl":"big","3xl":"big",full:"round"},Z={none:"none",sm:"small","":"small",md:"medium",lg:"big",xl:"giant","2xl":"giant",inner:"none"},_={xs:"mini",sm:"small",base:"base",lg:"large",xl:"big","2xl":"huge","3xl":"grand","4xl":"giant","5xl":"mount","6xl":"mega","7xl":"giga","8xl":"tera","9xl":"hero"},k={container:"container",flex:"flex","inline-flex":"inline-flex",grid:"grid","inline-grid":"inline-grid",block:"block","inline-block":"inline",hidden:"hidden","flex-row":"row","flex-col":"col","flex-row-reverse":"row-reverse","flex-col-reverse":"col-reverse","flex-wrap":"wrap","flex-nowrap":"nowrap","flex-wrap-reverse":"wrap-reverse","flex-grow":"grow","flex-grow-0":"grow-0",grow:"grow","grow-0":"grow-0","flex-shrink":"shrink","flex-shrink-0":"shrink-0",shrink:"shrink","shrink-0":"shrink-0","flex-1":"flex:1","flex-auto":"flex:auto","flex-initial":"flex:initial","flex-none":"flex:none","justify-start":"justify:start","justify-end":"justify:end","justify-center":"justify:center","justify-between":"justify:between","justify-around":"justify:around","justify-evenly":"justify:evenly","items-start":"items:start","items-end":"items:end","items-center":"items:center","items-baseline":"items:baseline","items-stretch":"items:stretch","self-auto":"self:auto","self-start":"self:start","self-end":"self:end","self-center":"self:center","self-stretch":"self:stretch",relative:"relative",absolute:"absolute",fixed:"fixed",sticky:"sticky",static:"static","overflow-auto":"overflow:auto","overflow-hidden":"overflow:hidden","overflow-visible":"overflow:visible","overflow-scroll":"overflow:scroll","object-contain":"object:contain","object-cover":"object:cover","object-fill":"object:fill","object-none":"object:none","object-scale-down":"object:scale-down"},M={italic:"italic","not-italic":"not-italic",antialiased:"antialiased",uppercase:"uppercase",lowercase:"lowercase",capitalize:"capitalize","normal-case":"normal-case",underline:"underline","line-through":"line-through","no-underline":"no-underline",truncate:"truncate","cursor-pointer":"cursor:pointer","cursor-default":"cursor:default","cursor-not-allowed":"cursor:not-allowed","select-none":"select:none","select-text":"select:text","select-all":"select:all"};function v(l,n){return l.startsWith("[")&&l.endsWith("]")?l:n?["full","screen","auto"].includes(l)?$[l]||`[${l}]`:`tw-${l}`:$[l]||`[${l}]`}var at={0:"none",1:"thin",2:"regular",3:"thick",4:"tiny",8:"small"};function p(l,n){return n?`tw-${l}`:at[l]||`[${l}px]`}function tt(l,n){let f=l.match(/^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|focus-visible:|active:|disabled:|dark:)(.+)$/),e="",t=l;if(f){let a=f[1].slice(0,-1);["sm","md","lg","xl","2xl"].includes(a)?e=`tw-${a}:`:e=f[1],t=f[2]}if(k[t])return{cat:"layout",val:e+k[t]};if(M[t])return{cat:"visual",val:e+M[t]};let d=t.match(/^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+)?)$/);if(d)return{cat:"visual",val:e+"text:"+d[1]};if(["text-left","text-center","text-right","text-justify"].includes(t))return{cat:"visual",val:e+"text:"+t.replace("text-","")};let o=t.match(/^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/);if(o){let a=n?`tw-${o[1]}`:_[o[1]]||o[1];return{cat:"visual",val:e+"text-size:"+a}}let c=t.match(/^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)$/);if(c){let a=c[1];return a==="transparent"?{cat:"visual",val:e+"bg:[transparent]"}:a==="current"?{cat:"visual",val:e+"bg:[currentColor]"}:a==="inherit"?{cat:"visual",val:e+"bg:[inherit]"}:{cat:"visual",val:e+"bg:"+a}}let r=t.match(/^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+)?)$/);if(r)return{cat:"visual",val:e+"border:"+r[1]};let u=t.match(/^p([trblxy])?-(.+)$/);if(u){let a=u[1]?"-"+u[1]:"";return{cat:"space",val:e+"p"+a+":"+v(u[2],n)}}let m=t.match(/^-?m([trblxy])?-(\[.+\]|\d+\.?\d*|px|auto|full|screen)$/);if(m){let a=t.startsWith("-"),i=m[1]?"-"+m[1]:"",s=v(m[2],n);return a&&(s.startsWith("[")&&s.endsWith("]")?s=`[-${s.slice(1,-1)}]`:s=`-${s}`),{cat:"space",val:e+"m"+i+":"+s}}let g=t.match(/^gap-([xy])?-?(.+)$/);if(g){let a=g[1]?"-"+g[1]:"";return{cat:"space",val:e+"g"+a+":"+v(g[2],n)}}let w=t.match(/^(min-w|max-w|w)-(.+)$/);if(w){let a=w[1],i=w[2],x={max:"[max-content]",min:"[min-content]",fit:"[fit-content]",prose:"[65ch]"}[i]||v(i,n);return{cat:"space",val:e+a+":"+x}}let b=t.match(/^(min-h|max-h|h)-(.+)$/);if(b){let a=b[1],i=b[2],x={screen:"[100vh]",svh:"[100svh]",lvh:"[100lvh]",dvh:"[100dvh]",max:"[max-content]",min:"[min-content]",fit:"[fit-content]"}[i]||v(i,n);return{cat:"space",val:e+a+":"+x}}let j=t.match(/^rounded(?:-(.+))?$/);if(j){let a=j[1]||"",i=n?a===""?"tw-DEFAULT":`tw-${a}`:Q[a]||"medium";return{cat:"visual",val:e+"rounded:"+i}}let z=t.match(/^shadow(?:-(.+))?$/);if(z){let a=z[1]||"",i=n?a===""?"tw-DEFAULT":`tw-${a}`:Z[a]||"medium";return{cat:"visual",val:e+"shadow:"+i}}let W=t.match(/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/);if(W)return{cat:"visual",val:e+"font:tw-"+W[1]};let h=t.match(/^border(?:-([trblxy]))?(?:-(\d+))?$/);if(h&&(h[2]||!h[1]&&t==="border")){let a=h[1]?"-"+h[1]+"-w":"-w",i=h[2]||"1";return{cat:"visual",val:e+"border"+a+":"+p(i,n)}}let y=t.match(/^(top|right|bottom|left|inset|inset-x|inset-y)-(\d+|px|auto|full|\[.+\])$/);if(y){let a=y[1],i=y[2];return i==="0"?i="none":i.startsWith("[")&&i.endsWith("]")||(i=v(i,n)),{cat:"layout",val:e+a+":"+i}}if(t==="outline-none")return{cat:"visual",val:e+"outline:none"};let S=t.match(/^order-(\d+|first|last|none)$/);if(S)return{cat:"layout",val:e+"order:"+S[1]};let C=t.match(/^grid-cols-(\d+|none)$/);if(C)return{cat:"layout",val:e+"grid-cols:"+C[1]};let V=t.match(/^col-span-(\d+|full)$/);if(V)return{cat:"layout",val:e+"col-span:"+V[1]};let T=t.match(/^grid-rows-(\d+|none)$/);if(T)return{cat:"layout",val:e+"grid-rows:"+T[1]};let H=t.match(/^row-span-(\d+|full)$/);if(H)return{cat:"layout",val:e+"row-span:"+H[1]};let L=t.match(/^opacity-(\d+)$/);if(L)return{cat:"visual",val:e+"opacity:"+L[1]};let q=t.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);if(q)return{cat:"visual",val:e+"bg-image:gradient-to-"+q[1]};let A=t.match(/^from-(.+)$/);if(A)return{cat:"visual",val:e+"from:"+A[1]};let D=t.match(/^via-(.+)$/);if(D)return{cat:"visual",val:e+"via:"+D[1]};let E=t.match(/^to-(.+)$/);if(E)return{cat:"visual",val:e+"to:"+E[1]};let F=t.match(/^transition(?:-(all|colors|opacity|shadow|transform|none))?$/);if(F){let a=F[1]||"all";return{cat:"visual",val:e+"transition:"+a}}let U=t.match(/^duration-(\d+)$/);if(U){let a=parseInt(U[1]),i;return a<=75?i="instant":a<=100?i="quick":a<=150?i="fast":a<=200?i="normal":a<=300?i="slow":a<=500?i="slower":i="lazy",{cat:"visual",val:e+"duration:"+i}}let X=t.match(/^ease-(linear|in|out|in-out)$/);if(X)return{cat:"visual",val:e+"ease:"+X[1]};let Y=t.match(/^ring(?:-(\d+))?$/);if(Y){let a=Y[1]||"3";if(a==="0")return{cat:"visual",val:e+"ring:none"};let s={1:"thin",2:"regular",3:"small",4:"medium",8:"big"}[a]||`[${a}px]`;return{cat:"visual",val:e+"ring:"+s}}let B=t.match(/^ring-offset-(\d+)$/);if(B)return{cat:"visual",val:e+"ring-offset:"+B[1]};let G=t.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+)?)$/);if(G)return{cat:"visual",val:e+"ring-color:"+G[1]};let I=t.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+)?)$/);if(I)return{cat:"visual",val:e+"divide-x:"+I[1]};let K=t.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+)?)$/);if(K)return{cat:"visual",val:e+"divide-y:"+K[1]};let N=t.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+)?)$/);if(N)return{cat:"visual",val:e+"divide:"+N[1]};let O=t.match(/^divide-(\d+)$/);if(O)return{cat:"visual",val:e+"divide-w:"+p(O[1],n)};if(t==="divide-x-reverse")return{cat:"visual",val:e+"divide-x:reverse"};if(t==="divide-y-reverse")return{cat:"visual",val:e+"divide-y:reverse"};let P=t.match(/^divide-x-(\d+)$/);if(P)return{cat:"visual",val:e+"divide-x-w:"+p(P[1],n)};if(t==="divide-x")return{cat:"visual",val:e+"divide-x-w:thin"};if(t==="divide-y")return{cat:"visual",val:e+"divide-y-w:thin"};let R=t.match(/^divide-y-(\d+)$/);if(R)return{cat:"visual",val:e+"divide-y-w:"+p(R[1],n)};let J=t.match(/^divide-(solid|dashed|dotted|double|none)$/);return J?{cat:"visual",val:e+"divide-style:"+J[1]}:null}function et(l,n){let f=l.trim().split(/\s+/).filter(c=>c),e=[],t=[],d=[],o=[];for(let c of f){let r=tt(c,n);r?r.cat==="layout"?e.push(r.val):r.cat==="space"?t.push(r.val):r.cat==="visual"&&d.push(r.val):o.push(c)}return{layout:e,space:t,visual:d,unknown:o}}function it(l,n){return l.replace(/class=(['"])([^"']+)\1/g,(f,e,t)=>{let{layout:d,space:o,visual:c,unknown:r}=et(t,n),u=[];return d.length&&u.push(`layout="${d.join(" ")}"`),o.length&&u.push(`space="${o.join(" ")}"`),c.length&&u.push(`visual="${c.join(" ")}"`),r.length&&u.push(`class="${r.join(" ")}"`),u.join(" ")||'class=""'})}typeof window<"u"&&(window.SenangStartTW={convertClass:tt,convertClasses:et,convertHTML:it,scales:{spacing:$,radius:Q,shadow:Z,fontSize:_},mappings:{layout:k,visual:M}});})();
2
+ (()=>{var S={0:"none",px:"thin",.5:"regular",1:"tiny",1.5:"tiny-2x",2:"small",2.5:"small-2x",3:"small-3x",3.5:"small-4x",4:"medium",5:"medium-2x",6:"medium-3x",7:"medium-4x",8:"large",9:"large-2x",10:"large-3x",11:"large-4x",12:"big",14:"big-2x",16:"big-3x",20:"big-4x",24:"giant",28:"giant-2x",32:"giant-3x",36:"giant-4x",40:"vast",44:"vast-2x",48:"vast-3x",52:"vast-4x",56:"vast-5x",60:"vast-6x",64:"vast-7x",72:"vast-8x",80:"vast-9x",96:"vast-10x",full:"[100%]",screen:"[100vw]",auto:"auto"},ae={none:"none",sm:"small","":"small",md:"small",lg:"medium",xl:"medium","2xl":"big","3xl":"big",full:"round"},ie={none:"none",sm:"small","":"small",md:"medium",lg:"big",xl:"giant","2xl":"giant",inner:"none"},re={xs:"mini",sm:"small",base:"base",lg:"large",xl:"big","2xl":"huge","3xl":"grand","4xl":"giant","5xl":"mount","6xl":"mega","7xl":"giga","8xl":"tera","9xl":"hero"},$={"1/2":"half","1/3":"third","2/3":"third-2x","1/4":"quarter","2/4":"half","3/4":"quarter-3x",full:"full"},z={container:"container",flex:"flex","inline-flex":"inline-flex",grid:"grid","inline-grid":"inline-grid",block:"block","inline-block":"inline",hidden:"hidden","flex-row":"row","flex-col":"col","flex-row-reverse":"row-reverse","flex-col-reverse":"col-reverse","flex-wrap":"wrap","flex-nowrap":"nowrap","flex-wrap-reverse":"wrap-reverse","flex-grow":"grow","flex-grow-0":"grow-0",grow:"grow","grow-0":"grow-0","flex-shrink":"shrink","flex-shrink-0":"shrink-0",shrink:"shrink","shrink-0":"shrink-0","flex-1":"flex:1","flex-auto":"flex:auto","flex-initial":"flex:initial","flex-none":"flex:none","justify-start":"justify:start","justify-end":"justify:end","justify-center":"justify:center","justify-between":"justify:between","justify-around":"justify:around","justify-evenly":"justify:evenly","items-start":"items:start","items-end":"items:end","items-center":"items:center","items-baseline":"items:baseline","items-stretch":"items:stretch","self-auto":"self:auto","self-start":"self:start","self-end":"self:end","self-center":"self:center","self-stretch":"self:stretch",relative:"relative",absolute:"absolute",fixed:"fixed",sticky:"sticky",static:"static","overflow-auto":"overflow:auto","overflow-hidden":"overflow:hidden","overflow-visible":"overflow:visible","overflow-scroll":"overflow:scroll","object-contain":"object:contain","object-cover":"object:cover","object-fill":"object:fill","object-none":"object:none","object-scale-down":"object:scale-down"},C={italic:"italic","not-italic":"not-italic",antialiased:"antialiased",uppercase:"uppercase",lowercase:"lowercase",capitalize:"capitalize","normal-case":"normal-case",underline:"underline","line-through":"line-through","no-underline":"no-underline",truncate:"truncate","cursor-pointer":"cursor:pointer","cursor-default":"cursor:default","cursor-not-allowed":"cursor:not-allowed","select-none":"select:none","select-text":"select:text","select-all":"select:all"};function g(s,l){return s.startsWith("[")&&s.endsWith("]")?s:l?["full","screen","auto"].includes(s)?S[s]||`[${s}]`:`tw-${s}`:S[s]||`[${s}]`}var se={0:"none",1:"thin",2:"regular",3:"thick",4:"tiny",8:"small"};function k(s,l){return l?`tw-${s}`:se[s]||`[${s}px]`}function ne(s,l){let m=s.match(/^(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:)(.+)$/),a="",t=s,f=null;if(m){let e=m[1].slice(0,-1);if(["sm","md","lg","xl","2xl"].includes(e))a=`tw-${e}:`;else if(e.startsWith("group-")||e.startsWith("peer-")){let r={hover:"hover",focus:"focus",active:"active",open:"expanded",check:"checked"},n=e.split("-")[1];a=`${r[n]||n}:`,e.startsWith("peer-")&&(f={cat:"listens",val:"peer"})}else a=m[1];t=m[2]}if(t==="group")return{cat:"layout",val:"hoverable focusable pressable expandable"};if(t==="peer")return[{cat:"layout",val:"hoverable focusable pressable expandable"},{cat:"interact",val:"peer"}];let i=e=>e?f?Array.isArray(e)?[...e,f]:[e,f]:e:null;if(z[t])return i({cat:"layout",val:a+z[t]});if(C[t])return i({cat:"visual",val:a+C[t]});let v=t.match(/^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+)?)$/);if(v)return i({cat:"visual",val:a+"text:"+v[1]});if(["text-left","text-center","text-right","text-justify"].includes(t))return i({cat:"visual",val:a+"text:"+t.replace("text-","")});let h=t.match(/^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/);if(h){let e=l?`tw-${h[1]}`:re[h[1]]||h[1];return i({cat:"visual",val:a+"text-size:"+e})}let d=t.match(/^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)$/);if(d){let e=d[1];return i(e==="transparent"?{cat:"visual",val:a+"bg:transparent"}:e==="current"?{cat:"visual",val:a+"bg:currentColor"}:e==="inherit"?{cat:"visual",val:a+"bg:inherit"}:{cat:"visual",val:a+"bg:"+e})}let u=t.match(/^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+)?)$/);if(u){let e=u[1];return e==="current"&&(e="currentColor"),i({cat:"visual",val:a+"border:"+e})}let o=t.match(/^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+)?)$/);if(o){let e=o[1],r=o[2];return r==="current"&&(r="currentColor"),i({cat:"visual",val:a+`border-${e}:${r}`})}let p=t.match(/^p([trblxy])?-(.+)$/);if(p){let e=p[1]?"-"+p[1]:"";return i({cat:"space",val:a+"p"+e+":"+g(p[2],l)})}let c=t.match(/^-?m([trblxy])?-(\[.+\]|\d+\.?\d*|px|auto|full|screen)$/);if(c){let e=t.startsWith("-"),r=c[1]?"-"+c[1]:"",n=g(c[2],l);return e&&(n.startsWith("[")&&n.endsWith("]")?n=`[-${n.slice(1,-1)}]`:n=`-${n}`),i({cat:"space",val:a+"m"+r+":"+n})}let w=t.match(/^gap-([xy])?-?(.+)$/);if(w){let e=w[1]?"-"+w[1]:"";return i({cat:"space",val:a+"g"+e+":"+g(w[2],l)})}let M=t.match(/^(min-w|max-w|w)-(.+)$/);if(M){let e=M[1],r=M[2],b={max:"[max-content]",min:"[min-content]",fit:"[fit-content]",prose:"[65ch]"}[r]||g(r,l);return i({cat:"space",val:a+e+":"+b})}let j=t.match(/^(min-h|max-h|h)-(.+)$/);if(j){let e=j[1],r=j[2],b={screen:"[100vh]",svh:"[100svh]",lvh:"[100lvh]",dvh:"[100dvh]",max:"[max-content]",min:"[min-content]",fit:"[fit-content]"}[r]||g(r,l);return i({cat:"space",val:a+e+":"+b})}let V=t.match(/^rounded(?:-(.+))?$/);if(V){let e=V[1]||"",r=l?e===""?"tw-DEFAULT":`tw-${e}`:ae[e]||"medium";return i({cat:"visual",val:a+"rounded:"+r})}let A=t.match(/^shadow(?:-(.+))?$/);if(A){let e=A[1]||"",r=l?e===""?"tw-DEFAULT":`tw-${e}`:ie[e]||"medium";return i({cat:"visual",val:a+"shadow:"+r})}let q=t.match(/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/);if(q)return i({cat:"visual",val:a+"font:tw-"+q[1]});let x=t.match(/^border(?:-([trblxy]))?(?:-(\d+))?$/);if(x&&(x[2]||!x[1]&&t==="border")){let e=x[1]?"-"+x[1]+"-w":"-w",r=x[2]||"1";return i({cat:"visual",val:a+"border"+e+":"+k(r,l)})}let W=t.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|\[.+\])$/);if(W){let e=W[1],r=W[2];return r==="0"?r="none":r.startsWith("[")&&r.endsWith("]")||($[r]?r=$[r]:r=g(r,l)),i({cat:"layout",val:a+e+":"+r})}let y=t.match(/^(-?)translate-([xy])-(\d+|px|full|1\/2|1\/3|2\/3|1\/4|2\/4|3\/4|\[.+\])$/);if(y){let e=y[1]==="-",r=y[2],n=y[3];return n.startsWith("[")&&n.endsWith("]")?e&&(n=`[-${n.slice(1,-1)}]`):$[n]?(n=$[n],e&&(n=`-${n}`)):n==="0"?n="0":(n=g(n,l),e&&(n=`-${n}`)),i({cat:"visual",val:a+`translate-${r}:${n}`})}if(t==="outline-none")return i({cat:"visual",val:a+"outline:none"});let T=t.match(/^order-(\d+|first|last|none)$/);if(T)return i({cat:"layout",val:a+"order:"+T[1]});let E=t.match(/^grid-cols-(\d+|none)$/);if(E)return i({cat:"layout",val:a+"grid-cols:"+E[1]});let H=t.match(/^col-span-(\d+|full)$/);if(H)return i({cat:"layout",val:a+"col-span:"+H[1]});let L=t.match(/^grid-rows-(\d+|none)$/);if(L)return i({cat:"layout",val:a+"grid-rows:"+L[1]});let U=t.match(/^row-span-(\d+|full)$/);if(U)return i({cat:"layout",val:a+"row-span:"+U[1]});let D=t.match(/^opacity-(\d+)$/);if(D)return i({cat:"visual",val:a+"opacity:"+D[1]});let F=t.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);if(F)return i({cat:"visual",val:a+"bg-image:gradient-to-"+F[1]});let N=t.match(/^from-(.+)$/);if(N)return i({cat:"visual",val:a+"from:"+N[1]});let X=t.match(/^via-(.+)$/);if(X)return i({cat:"visual",val:a+"via:"+X[1]});let Y=t.match(/^to-(.+)$/);if(Y)return i({cat:"visual",val:a+"to:"+Y[1]});let B=t.match(/^transition(?:-(all|colors|opacity|shadow|transform|none))?$/);if(B){let e=B[1]||"all";return i({cat:"visual",val:a+"transition:"+e})}let G=t.match(/^duration-(\d+)$/);if(G){let e=parseInt(G[1]),r;return e<=75?r="instant":e<=100?r="quick":e<=150?r="fast":e<=200?r="normal":e<=300?r="slow":e<=500?r="slower":r="lazy",i({cat:"visual",val:a+"duration:"+r})}let I=t.match(/^ease-(linear|in|out|in-out)$/);if(I)return i({cat:"visual",val:a+"ease:"+I[1]});let K=t.match(/^ring(?:-(\d+))?$/);if(K){let e=K[1]||"3";if(e==="0")return i({cat:"visual",val:a+"ring:none"});let n={1:"thin",2:"regular",3:"small",4:"medium",8:"big"}[e]||`[${e}px]`;return i({cat:"visual",val:a+"ring:"+n})}let O=t.match(/^ring-offset-(\d+)$/);if(O)return i({cat:"visual",val:a+"ring-offset:"+O[1]});let P=t.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+)?)$/);if(P)return i({cat:"visual",val:a+"ring-color:"+P[1]});let R=t.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+)?)$/);if(R)return i({cat:"visual",val:a+"divide-x:"+R[1]});let J=t.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+)?)$/);if(J)return i({cat:"visual",val:a+"divide-y:"+J[1]});let Q=t.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+)?)$/);if(Q)return i({cat:"visual",val:a+"divide:"+Q[1]});let Z=t.match(/^divide-(\d+)$/);if(Z)return i({cat:"visual",val:a+"divide-w:"+k(Z[1],l)});if(t==="divide-x-reverse")return i({cat:"visual",val:a+"divide-x:reverse"});if(t==="divide-y-reverse")return i({cat:"visual",val:a+"divide-y:reverse"});let _=t.match(/^divide-x-(\d+)$/);if(_)return i({cat:"visual",val:a+"divide-x-w:"+k(_[1],l)});if(t==="divide-x")return i({cat:"visual",val:a+"divide-x-w:thin"});if(t==="divide-y")return i({cat:"visual",val:a+"divide-y-w:thin"});let ee=t.match(/^divide-y-(\d+)$/);if(ee)return i({cat:"visual",val:a+"divide-y-w:"+k(ee[1],l)});let te=t.match(/^divide-(solid|dashed|dotted|double|none)$/);return te?i({cat:"visual",val:a+"divide-style:"+te[1]}):null}function le(s,l){let m=s.trim().split(/\s+/).filter(u=>u),a=[],t=[],f=[],i=[],v=[],h=[],d=(u,o)=>{u.includes(o)||u.push(o)};for(let u of m){let o=ne(u,l);if(o){let p=Array.isArray(o)?o:[o];for(let c of p)c.cat==="layout"?d(a,c.val):c.cat==="space"?d(t,c.val):c.cat==="visual"?d(f,c.val):c.cat==="interact"?d(i,c.val):c.cat==="listens"&&d(v,c.val)}else h.push(u)}return{layout:a,space:t,visual:f,interact:i,listens:v,unknown:h}}function oe(s,l){return s.replace(/class=(['"])([^"']+)\1/g,(m,a,t)=>{let{layout:f,space:i,visual:v,interact:h,listens:d,unknown:u}=le(t,l),o=[];return f.length&&o.push(`layout="${f.join(" ")}"`),i.length&&o.push(`space="${i.join(" ")}"`),v.length&&o.push(`visual="${v.join(" ")}"`),h.length&&o.push(`interact="${h.join(" ")}"`),d.length&&o.push(`listens="${d.join(" ")}"`),u.length&&o.push(`class="${u.join(" ")}"`),o.join(" ")||'class=""'})}typeof window<"u"&&(window.SenangStartTW={convertClass:ne,convertClasses:le,convertHTML:oe,scales:{spacing:S,radius:ae,shadow:ie,fontSize:re},mappings:{layout:z,visual:C}});})();
@@ -78,7 +78,7 @@ theme: {
78
78
  ```javascript
79
79
  theme: {
80
80
  colors: {
81
- 'brand': '#8B5CF6',
81
+ 'brand': '#38BDF8',
82
82
  'accent': '#EC4899',
83
83
  'custom': '#FF5733'
84
84
  }
@@ -131,7 +131,7 @@ export default {
131
131
  'huge': '256px'
132
132
  },
133
133
  colors: {
134
- 'brand': '#8B5CF6',
134
+ 'brand': '#38BDF8',
135
135
  'accent': '#EC4899'
136
136
  },
137
137
  screens: {
@@ -160,3 +160,63 @@ Always ensure sufficient color contrast for hover and focus states. The `:focus`
160
160
  Accessible focus ring
161
161
  </button>
162
162
  ```
163
+
164
+ ## State Capabilities & Interaction
165
+
166
+ SenangStart allows elements to react to parent ("Group") or sibling ("Peer") states. This works across **all three attributes**.
167
+
168
+ ### Supported Triggers
169
+
170
+ Add these to the `layout` attribute of the parent/trigger element:
171
+
172
+ | Capability | Trigger State |
173
+ |------------|---------------|
174
+ | `layout="hoverable"` | `:hover` |
175
+ | `layout="focusable"` | `:focus-within` |
176
+ | `layout="pressable"` | `:active` |
177
+ | `layout="expandable"` | `[aria-expanded="true"]` |
178
+ | `layout="selectable"` | `[aria-selected="true"]` |
179
+ | `layout="disabled"` | Disables all triggers |
180
+
181
+ ### Cross-Attribute Example
182
+
183
+ State variants like `hover:`, `focus:`, etc., apply to **any attribute** (`layout`, `space`, `visual`) when nested inside a capable group.
184
+
185
+ ```html
186
+ <!-- Parent has "hoverable" capability -->
187
+ <div layout="flex hoverable" space="p:medium" visual="bg:white shadow:medium">
188
+
189
+ <!-- Layout Reaction: Hidden by default, becomes flex on parent hover -->
190
+ <div layout="hidden hover:flex items:center">
191
+ <span>Revealed!</span>
192
+ </div>
193
+
194
+ <!-- Space Reaction: Padding increases on parent hover -->
195
+ <button space="p:small hover:p:medium">
196
+ Expand Me
197
+ </button>
198
+
199
+ <!-- Visual Reaction: Color changes on parent hover -->
200
+ <p visual="text:grey hover:text:primary">
201
+ Highlight Me
202
+ </p>
203
+
204
+ </div>
205
+ ```
206
+
207
+ ### Peer Interactions
208
+
209
+ Use `interact="[id]"` on the trigger and `listens="[id]"` on the receiver.
210
+
211
+ ```html
212
+ <button layout="hoverable" interact="menu-trigger">
213
+ Hover me
214
+ </button>
215
+
216
+ <div
217
+ layout="absolute hidden hover:block"
218
+ listens="menu-trigger"
219
+ >
220
+ Dropdown Menu
221
+ </div>
222
+ ```