@bookklik/senangstart-css 0.2.4 → 0.2.6

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 (154) hide show
  1. package/.agent/skills/add-utility/scripts/scaffold-utility.js +209 -0
  2. package/.agent/skills/compiler-development/SKILL.md +272 -0
  3. package/.agent/skills/jit-engine/SKILL.md +241 -0
  4. package/.agent/skills/jit-engine/examples/add-visual-utility.js +117 -0
  5. package/.agent/skills/jit-engine/examples/scale-based-utilities.js +130 -0
  6. package/.agent/skills/jit-engine/examples/state-responsive-handling.js +177 -0
  7. package/.agent/skills/senangstart-architecture/SKILL.md +163 -0
  8. package/.agent/skills/tailwind-conversion/SKILL.md +264 -0
  9. package/.agent/workflows/add-utility.md +155 -0
  10. package/.agent/workflows/build.md +97 -0
  11. package/.agent/workflows/dev.md +58 -0
  12. package/.agent/workflows/docs.md +113 -0
  13. package/.agent/workflows/test.md +103 -0
  14. package/dist/senangstart-css.js +165 -20
  15. package/dist/senangstart-css.min.js +39 -26
  16. package/dist/senangstart-tw.js +262 -52
  17. package/dist/senangstart-tw.min.js +1 -1
  18. package/docs/.vitepress/config.js +10 -2
  19. package/docs/guide/cdn.md +1 -1
  20. package/docs/ms/guide/cdn.md +1 -1
  21. package/docs/ms/reference/layout/position.md +4 -4
  22. package/docs/ms/reference/layout/z-index.md +8 -8
  23. package/docs/ms/reference/space/gap.md +1 -1
  24. package/docs/ms/reference/space/height.md +42 -7
  25. package/docs/ms/reference/space/margin.md +1 -1
  26. package/docs/ms/reference/space/padding.md +1 -1
  27. package/docs/ms/reference/space/scale-reference.md +46 -17
  28. package/docs/ms/reference/space/width.md +40 -5
  29. package/docs/ms/reference/space.md +1 -1
  30. package/docs/ms/reference/spacing.md +103 -21
  31. package/docs/ms/reference/visual/animation-fill.md +8 -8
  32. package/docs/ms/reference/visual/backdrop-blur.md +4 -4
  33. package/docs/ms/reference/visual/backdrop-brightness.md +8 -8
  34. package/docs/ms/reference/visual/backdrop-grayscale.md +6 -6
  35. package/docs/ms/reference/visual/backdrop-sepia.md +6 -6
  36. package/docs/ms/reference/visual/background-clip.md +2 -2
  37. package/docs/ms/reference/visual/background-image.md +4 -4
  38. package/docs/ms/reference/visual/divide-reverse.md +66 -0
  39. package/docs/ms/reference/visual/divide-style.md +80 -0
  40. package/docs/ms/reference/visual/divide-width.md +89 -0
  41. package/docs/ms/reference/visual/divide.md +115 -0
  42. package/docs/ms/reference/visual/filter-brightness.md +4 -4
  43. package/docs/ms/reference/visual/filter-contrast.md +4 -4
  44. package/docs/ms/reference/visual/filter-drop-shadow.md +6 -6
  45. package/docs/ms/reference/visual/filter-grayscale.md +4 -4
  46. package/docs/ms/reference/visual/filter-hue-rotate.md +4 -4
  47. package/docs/ms/reference/visual/filter-invert.md +2 -2
  48. package/docs/ms/reference/visual/filter-saturate.md +4 -4
  49. package/docs/ms/reference/visual/filter-sepia.md +4 -4
  50. package/docs/ms/reference/visual/font-family.md +2 -2
  51. package/docs/ms/reference/visual/gradient-from.md +57 -57
  52. package/docs/ms/reference/visual/gradient-to.md +57 -57
  53. package/docs/ms/reference/visual/gradient-via.md +54 -54
  54. package/docs/ms/reference/visual/letter-spacing.md +2 -2
  55. package/docs/ms/reference/visual/line-clamp.md +2 -2
  56. package/docs/ms/reference/visual/line-height.md +2 -2
  57. package/docs/ms/reference/visual/outline.md +2 -2
  58. package/docs/ms/reference/visual/ring-color.md +29 -0
  59. package/docs/ms/reference/visual/ring-offset.md +30 -0
  60. package/docs/ms/reference/visual/ring.md +62 -0
  61. package/docs/ms/reference/visual/stroke-width.md +6 -6
  62. package/docs/ms/reference/visual/stroke.md +4 -4
  63. package/docs/ms/reference/visual/text-indent.md +2 -2
  64. package/docs/ms/reference/visual/text-overflow.md +2 -2
  65. package/docs/ms/reference/visual/text-size.md +2 -2
  66. package/docs/ms/reference/visual/text-wrap.md +2 -2
  67. package/docs/ms/reference/visual/transform-backface.md +4 -4
  68. package/docs/ms/reference/visual/transform-perspective-origin.md +6 -6
  69. package/docs/ms/reference/visual/transform-perspective.md +6 -6
  70. package/docs/ms/reference/visual/transform-rotate-3d.md +6 -6
  71. package/docs/ms/reference/visual/transform-style.md +4 -4
  72. package/docs/ms/reference/visual/transform-translate-z.md +6 -6
  73. package/docs/ms/reference/visual/transform-translate.md +2 -2
  74. package/docs/ms/reference/visual/whitespace.md +2 -2
  75. package/docs/ms/reference/visual/word-break.md +2 -2
  76. package/docs/public/assets/senangstart-css.min.js +39 -26
  77. package/docs/public/llms.txt +1756 -0
  78. package/docs/reference/layout/position.md +4 -4
  79. package/docs/reference/layout/z-index.md +8 -8
  80. package/docs/reference/space/gap.md +1 -1
  81. package/docs/reference/space/height.md +42 -7
  82. package/docs/reference/space/margin.md +1 -1
  83. package/docs/reference/space/padding.md +1 -1
  84. package/docs/reference/space/scale-reference.md +46 -17
  85. package/docs/reference/space/width.md +40 -5
  86. package/docs/reference/space.md +1 -1
  87. package/docs/reference/spacing.md +103 -21
  88. package/docs/reference/visual/animation-fill.md +8 -8
  89. package/docs/reference/visual/backdrop-blur.md +4 -4
  90. package/docs/reference/visual/backdrop-brightness.md +8 -8
  91. package/docs/reference/visual/backdrop-grayscale.md +6 -6
  92. package/docs/reference/visual/backdrop-sepia.md +6 -6
  93. package/docs/reference/visual/background-clip.md +2 -2
  94. package/docs/reference/visual/background-image.md +4 -4
  95. package/docs/reference/visual/divide-reverse.md +66 -0
  96. package/docs/reference/visual/divide-style.md +80 -0
  97. package/docs/reference/visual/divide-width.md +89 -0
  98. package/docs/reference/visual/divide.md +115 -0
  99. package/docs/reference/visual/filter-brightness.md +4 -4
  100. package/docs/reference/visual/filter-contrast.md +4 -4
  101. package/docs/reference/visual/filter-drop-shadow.md +6 -6
  102. package/docs/reference/visual/filter-grayscale.md +4 -4
  103. package/docs/reference/visual/filter-hue-rotate.md +4 -4
  104. package/docs/reference/visual/filter-invert.md +2 -2
  105. package/docs/reference/visual/filter-saturate.md +4 -4
  106. package/docs/reference/visual/filter-sepia.md +4 -4
  107. package/docs/reference/visual/font-family.md +2 -2
  108. package/docs/reference/visual/gradient-from.md +57 -57
  109. package/docs/reference/visual/gradient-to.md +57 -57
  110. package/docs/reference/visual/gradient-via.md +54 -54
  111. package/docs/reference/visual/letter-spacing.md +2 -2
  112. package/docs/reference/visual/line-clamp.md +2 -2
  113. package/docs/reference/visual/line-height.md +2 -2
  114. package/docs/reference/visual/outline.md +2 -2
  115. package/docs/reference/visual/ring-color.md +29 -0
  116. package/docs/reference/visual/ring-offset.md +30 -0
  117. package/docs/reference/visual/ring.md +62 -0
  118. package/docs/reference/visual/stroke-width.md +6 -6
  119. package/docs/reference/visual/stroke.md +4 -4
  120. package/docs/reference/visual/text-indent.md +2 -2
  121. package/docs/reference/visual/text-overflow.md +2 -2
  122. package/docs/reference/visual/text-size.md +2 -2
  123. package/docs/reference/visual/text-wrap.md +2 -2
  124. package/docs/reference/visual/transform-backface.md +4 -4
  125. package/docs/reference/visual/transform-perspective-origin.md +6 -6
  126. package/docs/reference/visual/transform-perspective.md +6 -6
  127. package/docs/reference/visual/transform-rotate-3d.md +6 -6
  128. package/docs/reference/visual/transform-style.md +4 -4
  129. package/docs/reference/visual/transform-translate-z.md +6 -6
  130. package/docs/reference/visual/transform-translate.md +2 -2
  131. package/docs/reference/visual/whitespace.md +2 -2
  132. package/docs/reference/visual/word-break.md +2 -2
  133. package/docs/reference/visual.md +8 -2
  134. package/package.json +4 -2
  135. package/scripts/build-dist.js +2 -2
  136. package/scripts/bundle-jit.js +3 -3
  137. package/scripts/convert-tailwind.js +250 -2
  138. package/scripts/generate-llms-txt.js +264 -0
  139. package/src/cdn/{jit.js → senangstart-engine.js} +184 -38
  140. package/src/cdn/tw-conversion-engine.js +282 -68
  141. package/src/compiler/generators/css.js +115 -2
  142. package/src/config/defaults.js +37 -11
  143. package/src/core/constants.js +37 -8
  144. package/src/definitions/index.js +3 -0
  145. package/src/definitions/space.js +97 -20
  146. package/src/definitions/visual-borders.js +80 -1
  147. package/src/definitions/visual-divide.js +225 -0
  148. package/src/definitions/visual-transform3d.js +16 -16
  149. package/src/definitions/visual-transforms.js +1 -1
  150. package/src/definitions/visual-transitions.js +4 -4
  151. package/src/definitions/visual-typography.js +6 -6
  152. package/src/definitions/visual.js +4 -4
  153. package/tests/unit/compiler/generators/css.test.js +267 -3
  154. package/tests/unit/convert-tailwind.test.js +59 -1
@@ -7,83 +7,105 @@
7
7
  // MAPPING SCALES
8
8
  // ============================
9
9
 
10
+ // Spacing scale mapping Tailwind values to SenangStart semantic values
11
+ // Engine native values: none(0px), thin(1px), regular(2px), thick(3px), tiny(4px), tiny-2x(6px),
12
+ // small(8px), small-2x(10px), small-3x(12px), small-4x(14px),
13
+ // medium(16px), medium-2x(20px), medium-3x(24px), medium-4x(28px),
14
+ // large(32px), large-2x(36px), large-3x(40px), large-4x(44px),
15
+ // big(48px), big-2x(56px), big-3x(64px), big-4x(80px),
16
+ // giant(96px), giant-2x(112px), giant-3x(128px), giant-4x(144px),
17
+ // vast(160px), vast-2x(176px), vast-3x(192px), vast-4x(208px),
18
+ // vast-5x(224px), vast-6x(240px), vast-7x(256px), vast-8x(288px),
19
+ // vast-9x(320px), vast-10x(384px)
20
+ // Tailwind base: 4px per unit (1 = 0.25rem = 4px)
10
21
  const spacingScale = {
11
- 0: "none",
12
- px: "[1px]",
13
- 0.5: "tiny",
14
- 1: "tiny",
15
- 1.5: "tiny",
16
- 2: "tiny",
17
- 2.5: "small",
18
- 3: "small",
19
- 3.5: "small",
20
- 4: "small",
21
- 5: "medium",
22
- 6: "medium",
23
- 7: "medium",
24
- 8: "big",
25
- 9: "big",
26
- 10: "big",
27
- 11: "big",
28
- 12: "giant",
29
- 14: "giant",
30
- 16: "giant",
31
- 20: "vast",
32
- 24: "vast",
33
- 28: "vast",
34
- 32: "vast",
35
- 36: "vast",
36
- 40: "vast",
37
- 44: "vast",
38
- 48: "vast",
39
- 52: "vast",
40
- 56: "vast",
41
- 60: "vast",
42
- 64: "vast",
43
- 72: "vast",
44
- 80: "vast",
45
- 96: "vast",
22
+ 0: "none", // 0px → none
23
+ px: "thin", // 1px → thin
24
+ 0.5: "regular", // 2px → regular
25
+ 1: "tiny", // 4px → tiny
26
+ 1.5: "tiny-2x", // 6px → tiny-2x
27
+ 2: "small", // 8px → small
28
+ 2.5: "small-2x", // 10px → small-2x
29
+ 3: "small-3x", // 12px → small-3x
30
+ 3.5: "small-4x", // 14px → small-4x
31
+ 4: "medium", // 16px → medium
32
+ 5: "medium-2x", // 20px → medium-2x
33
+ 6: "medium-3x", // 24px → medium-3x
34
+ 7: "medium-4x", // 28px → medium-4x
35
+ 8: "large", // 32px → large
36
+ 9: "large-2x", // 36px → large-2x
37
+ 10: "large-3x", // 40px → large-3x
38
+ 11: "large-4x", // 44px → large-4x
39
+ 12: "big", // 48px → big
40
+ 14: "big-2x", // 56px → big-2x
41
+ 16: "big-3x", // 64px → big-3x
42
+ 20: "big-4x", // 80px → big-4x
43
+ 24: "giant", // 96px → giant
44
+ 28: "giant-2x", // 112px → giant-2x
45
+ 32: "giant-3x", // 128px → giant-3x
46
+ 36: "giant-4x", // 144px → giant-4x
47
+ 40: "vast", // 160px → vast
48
+ 44: "vast-2x", // 176px → vast-2x
49
+ 48: "vast-3x", // 192px → vast-3x
50
+ 52: "vast-4x", // 208px → vast-4x
51
+ 56: "vast-5x", // 224px → vast-5x
52
+ 60: "vast-6x", // 240px → vast-6x
53
+ 64: "vast-7x", // 256px → vast-7x
54
+ 72: "vast-8x", // 288px → vast-8x
55
+ 80: "vast-9x", // 320px → vast-9x
56
+ 96: "vast-10x", // 384px → vast-10x
46
57
  full: "[100%]",
47
58
  screen: "[100vw]",
48
59
  auto: "auto",
49
60
  };
50
61
 
62
+ // Radius scale mapping Tailwind values to SenangStart semantic values
63
+ // Engine native values: none(0px), small(4px), medium(8px), big(16px), round(9999px)
64
+ // Tailwind: none(0), sm(0.125rem=2px), DEFAULT(0.25rem=4px), md(0.375rem=6px),
65
+ // lg(0.5rem=8px), xl(0.75rem=12px), 2xl(1rem=16px), 3xl(1.5rem=24px), full(9999px)
51
66
  const radiusScale = {
52
- none: "none",
53
- sm: "small",
54
- "": "small",
55
- md: "medium",
56
- lg: "medium",
57
- xl: "big",
58
- "2xl": "big",
59
- "3xl": "big",
60
- full: "round",
67
+ none: "none", // 0px → none
68
+ sm: "small", // 2px → small (closest to 4px)
69
+ "": "small", // 4px → small (Tailwind DEFAULT)
70
+ md: "small", // 6px → small (closest to 4px)
71
+ lg: "medium", // 8px → medium
72
+ xl: "medium", // 12px → medium (closest to 8px)
73
+ "2xl": "big", // 16px → big
74
+ "3xl": "big", // 24px → big (closest to 16px)
75
+ full: "round", // 9999px → round
61
76
  };
62
77
 
78
+ // Shadow scale mapping Tailwind values to SenangStart semantic values
79
+ // Engine native values: none, small, medium, big, giant
63
80
  const shadowScale = {
64
- sm: "small",
65
- "": "small",
66
- md: "medium",
67
- lg: "big",
68
- xl: "giant",
69
- "2xl": "giant",
70
- none: "none",
81
+ none: "none", // none → none
82
+ sm: "small", // small shadow → small
83
+ "": "small", // DEFAULT shadow → small
84
+ md: "medium", // medium shadow → medium
85
+ lg: "big", // large shadow → big
86
+ xl: "giant", // xl shadow → giant
87
+ "2xl": "giant", // 2xl shadow → giant
88
+ inner: "none", // inner shadow not directly supported
71
89
  };
72
90
 
91
+ // Font size scale mapping Tailwind values to SenangStart semantic values
92
+ // Engine native values: mini(0.75rem), small(0.875rem), base(1rem), large(1.125rem),
93
+ // big(1.25rem), huge(1.5rem), grand(1.875rem), giant(2.25rem),
94
+ // mount(3rem), mega(3.75rem), giga(4.5rem), tera(6rem), hero(8rem)
73
95
  const fontSizeScale = {
74
- xs: "mini", // 0.75rem
75
- sm: "small", // 0.875rem
76
- base: "base", // 1rem
77
- lg: "large", // 1.125rem
78
- xl: "big", // 1.25rem
79
- "2xl": "huge", // 1.5rem
80
- "3xl": "grand", // 1.875rem
81
- "4xl": "giant", // 2.25rem
82
- "5xl": "mount", // 3rem
83
- "6xl": "mega", // 3.75rem
84
- "7xl": "giga", // 4.5rem
85
- "8xl": "tera", // 6rem
86
- "9xl": "hero", // 8rem
96
+ xs: "mini", // 0.75rem → mini
97
+ sm: "small", // 0.875rem → small
98
+ base: "base", // 1rem → base
99
+ lg: "large", // 1.125rem → large
100
+ xl: "big", // 1.25rem → big
101
+ "2xl": "huge", // 1.5rem → huge
102
+ "3xl": "grand", // 1.875rem → grand
103
+ "4xl": "giant", // 2.25rem → giant
104
+ "5xl": "mount", // 3rem → mount
105
+ "6xl": "mega", // 3.75rem → mega
106
+ "7xl": "giga", // 4.5rem → giga
107
+ "8xl": "tera", // 6rem → tera
108
+ "9xl": "hero", // 8rem → hero
87
109
  };
88
110
 
89
111
  const layoutMappings = {
@@ -171,6 +193,10 @@ const visualKeywords = {
171
193
  // ============================
172
194
 
173
195
  function getSpacing(value, exact) {
196
+ // Check if it's already an arbitrary value with brackets
197
+ if (value.startsWith('[') && value.endsWith(']')) {
198
+ return value; // Return as-is, don't double-wrap
199
+ }
174
200
  if (exact) {
175
201
  if (["full", "screen", "auto"].includes(value))
176
202
  return spacingScale[value] || `[${value}]`;
@@ -179,6 +205,24 @@ function getSpacing(value, exact) {
179
205
  return spacingScale[value] || `[${value}]`;
180
206
  }
181
207
 
208
+ // Border width scale mapping Tailwind values to SenangStart semantic values
209
+ // Engine native values: none(0), thin(1px), regular(2px), thick(3px)
210
+ const borderWidthScale = {
211
+ 0: "none",
212
+ 1: "thin", // 1px → thin (was [1px])
213
+ 2: "regular", // 2px → regular
214
+ 3: "thick", // 3px → thick
215
+ 4: "tiny", // 4px → tiny
216
+ 8: "small", // 8px → small
217
+ };
218
+
219
+ function getBorderWidth(value, exact) {
220
+ if (exact) {
221
+ return `tw-${value}`;
222
+ }
223
+ return borderWidthScale[value] || `[${value}px]`;
224
+ }
225
+
182
226
  // ============================
183
227
  // CONVERSION FUNCTIONS
184
228
  // ============================
@@ -186,7 +230,7 @@ function getSpacing(value, exact) {
186
230
  function convertClass(twClass, exact) {
187
231
  // Handle prefixes (hover:, sm:, md:, etc.)
188
232
  const prefixMatch = twClass.match(
189
- /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|active:|disabled:|dark:)(.+)$/
233
+ /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|focus-visible:|active:|disabled:|dark:)(.+)$/
190
234
  );
191
235
  let prefix = "",
192
236
  baseClass = twClass;
@@ -239,9 +283,22 @@ function convertClass(twClass, exact) {
239
283
 
240
284
  // Background color
241
285
  const bgMatch = baseClass.match(
242
- /^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+)?)$/
286
+ /^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)$/
243
287
  );
244
- if (bgMatch) return { cat: "visual", val: prefix + "bg:" + bgMatch[1] };
288
+ if (bgMatch) {
289
+ const colorVal = bgMatch[1];
290
+ // Handle special values
291
+ if (colorVal === 'transparent') {
292
+ return { cat: "visual", val: prefix + "bg:[transparent]" };
293
+ }
294
+ if (colorVal === 'current') {
295
+ return { cat: "visual", val: prefix + "bg:[currentColor]" };
296
+ }
297
+ if (colorVal === 'inherit') {
298
+ return { cat: "visual", val: prefix + "bg:[inherit]" };
299
+ }
300
+ return { cat: "visual", val: prefix + "bg:" + colorVal };
301
+ }
245
302
 
246
303
  // Border color
247
304
  const borderColorMatch = baseClass.match(
@@ -350,10 +407,31 @@ function convertClass(twClass, exact) {
350
407
  const width = borderWidthMatch[2] || "1";
351
408
  return {
352
409
  cat: "visual",
353
- val: prefix + "border" + side + ":[" + width + "px]",
410
+ val: prefix + "border" + side + ":" + getBorderWidth(width, exact),
354
411
  };
355
412
  }
356
413
 
414
+ // Positional properties (top-0, right-0, bottom-0, left-0, inset-0, etc.)
415
+ const positionMatch = baseClass.match(/^(top|right|bottom|left|inset|inset-x|inset-y)-(\d+|px|auto|full|\[.+\])$/);
416
+ if (positionMatch) {
417
+ const prop = positionMatch[1];
418
+ let val = positionMatch[2];
419
+ // Handle 0 specially
420
+ if (val === '0') {
421
+ val = 'none';
422
+ } else if (val.startsWith('[') && val.endsWith(']')) {
423
+ // Keep arbitrary values as-is
424
+ } else {
425
+ val = getSpacing(val, exact);
426
+ }
427
+ return { cat: "layout", val: prefix + prop + ":" + val };
428
+ }
429
+
430
+ // Outline none
431
+ if (baseClass === 'outline-none') {
432
+ return { cat: "visual", val: prefix + "outline:none" };
433
+ }
434
+
357
435
  // Order
358
436
  const orderMatch = baseClass.match(/^order-(\d+|first|last|none)$/);
359
437
  if (orderMatch) {
@@ -414,6 +492,142 @@ function convertClass(twClass, exact) {
414
492
  return { cat: "visual", val: prefix + "to:" + toMatch[1] };
415
493
  }
416
494
 
495
+ // Transition utilities
496
+ const transitionMatch = baseClass.match(/^transition(?:-(all|colors|opacity|shadow|transform|none))?$/);
497
+ if (transitionMatch) {
498
+ const type = transitionMatch[1] || 'all';
499
+ return { cat: "visual", val: prefix + "transition:" + type };
500
+ }
501
+
502
+ // Duration utilities
503
+ const durationMatch = baseClass.match(/^duration-(\d+)$/);
504
+ if (durationMatch) {
505
+ // Map Tailwind duration (ms) to SenangStart semantic values
506
+ const ms = parseInt(durationMatch[1]);
507
+ let durationVal;
508
+ if (ms <= 75) durationVal = 'instant';
509
+ else if (ms <= 100) durationVal = 'quick';
510
+ else if (ms <= 150) durationVal = 'fast';
511
+ else if (ms <= 200) durationVal = 'normal';
512
+ else if (ms <= 300) durationVal = 'slow';
513
+ else if (ms <= 500) durationVal = 'slower';
514
+ else durationVal = 'lazy';
515
+ return { cat: "visual", val: prefix + "duration:" + durationVal };
516
+ }
517
+
518
+ // Ease utilities
519
+ const easeMatch = baseClass.match(/^ease-(linear|in|out|in-out)$/);
520
+ if (easeMatch) {
521
+ return { cat: "visual", val: prefix + "ease:" + easeMatch[1] };
522
+ }
523
+
524
+ // Ring utilities - Convert to native ring utilities
525
+ // Tailwind ring-4 generates: box-shadow: 0 0 0 4px var(--tw-ring-color)
526
+ const ringMatch = baseClass.match(/^ring(?:-(\d+))?$/);
527
+ if (ringMatch) {
528
+ const width = ringMatch[1] || '3';
529
+ if (width === '0') {
530
+ return { cat: "visual", val: prefix + "ring:none" };
531
+ }
532
+ // Map Tailwind ring widths to SenangStart semantic values
533
+ const ringScale = {
534
+ '1': 'thin', '2': 'regular', '3': 'small', '4': 'medium', '8': 'big'
535
+ };
536
+ const scale = ringScale[width] || `[${width}px]`;
537
+ return { cat: "visual", val: prefix + "ring:" + scale };
538
+ }
539
+
540
+ // Ring offset - converts to native ring-offset utility
541
+ const ringOffsetMatch = baseClass.match(/^ring-offset-(\d+)$/);
542
+ if (ringOffsetMatch) {
543
+ return { cat: "visual", val: prefix + "ring-offset:" + ringOffsetMatch[1] };
544
+ }
545
+
546
+ // Ring color - converts to native ring-color utility
547
+ 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+)?)$/);
548
+ if (ringColorMatch) {
549
+ return { cat: "visual", val: prefix + "ring-color:" + ringColorMatch[1] };
550
+ }
551
+
552
+ // Divide color - directional (check divide-x and divide-y BEFORE generic divide)
553
+ 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+)?)$/);
554
+ if (divideXMatch) {
555
+ return {
556
+ cat: "visual",
557
+ val: prefix + "divide-x:" + divideXMatch[1],
558
+ };
559
+ }
560
+
561
+ 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+)?)$/);
562
+ if (divideYMatch) {
563
+ return {
564
+ cat: "visual",
565
+ val: prefix + "divide-y:" + divideYMatch[1],
566
+ };
567
+ }
568
+
569
+ // Divide color - all directions (check divide-x and divide-y AFTER generic divide)
570
+ 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+)?)$/);
571
+ if (divideColorMatch) {
572
+ return {
573
+ cat: "visual",
574
+ val: prefix + "divide:" + divideColorMatch[1],
575
+ };
576
+ }
577
+
578
+ // Divide width - all directions
579
+ const divideWidthMatch = baseClass.match(/^divide-(\d+)$/);
580
+ if (divideWidthMatch) {
581
+ return {
582
+ cat: "visual",
583
+ val: prefix + "divide-w:" + getBorderWidth(divideWidthMatch[1], exact),
584
+ };
585
+ }
586
+
587
+ // Divide reverse (check these FIRST as they are specific)
588
+ if (baseClass === 'divide-x-reverse') {
589
+ return { cat: "visual", val: prefix + "divide-x:reverse" };
590
+ }
591
+ if (baseClass === 'divide-y-reverse') {
592
+ return { cat: "visual", val: prefix + "divide-y:reverse" };
593
+ }
594
+
595
+ // Divide width - directional
596
+ const divideXWidthMatch = baseClass.match(/^divide-x-(\d+)$/);
597
+ if (divideXWidthMatch) {
598
+ return {
599
+ cat: "visual",
600
+ val: prefix + "divide-x-w:" + getBorderWidth(divideXWidthMatch[1], exact),
601
+ };
602
+ }
603
+
604
+ // Divide width (implicit x/y from Tailwind divide-x/y without number is usually 1px)
605
+ // Tailwind: divide-x = border-right-width: 1px (or left if reverse).
606
+ // SenangStart: divide-x-w:thin
607
+ if (baseClass === 'divide-x') {
608
+ return { cat: "visual", val: prefix + "divide-x-w:thin" };
609
+ }
610
+ if (baseClass === 'divide-y') {
611
+ return { cat: "visual", val: prefix + "divide-y-w:thin" };
612
+ }
613
+
614
+ const divideYWidthMatch = baseClass.match(/^divide-y-(\d+)$/);
615
+ if (divideYWidthMatch) {
616
+ return {
617
+ cat: "visual",
618
+ val: prefix + "divide-y-w:" + getBorderWidth(divideYWidthMatch[1], exact),
619
+ };
620
+ }
621
+
622
+ // Divide style
623
+ const divideStyleMatch = baseClass.match(/^divide-(solid|dashed|dotted|double|none)$/);
624
+ if (divideStyleMatch) {
625
+ return {
626
+ cat: "visual",
627
+ val: prefix + "divide-style:" + divideStyleMatch[1], // Fixed category from 'color' to 'visual'
628
+ };
629
+ }
630
+
417
631
  return null;
418
632
  }
419
633
 
@@ -564,6 +564,28 @@ function generateLayoutRule(token, config) {
564
564
  function generateSpaceRule(token, config) {
565
565
  const { property, value, isArbitrary } = token;
566
566
 
567
+ // Handle special sizing values for width/height utilities
568
+ const sizingSpecialValues = {
569
+ 'min': 'min-content',
570
+ 'max': 'max-content',
571
+ 'fit': 'fit-content'
572
+ };
573
+
574
+ // Check if this is a sizing utility with a special value
575
+ const sizingProps = ['w', 'h', 'min-w', 'max-w', 'min-h', 'max-h'];
576
+ if (sizingProps.includes(property) && sizingSpecialValues[value]) {
577
+ const cssVal = sizingSpecialValues[value];
578
+ const propMap = {
579
+ 'w': `width: ${cssVal};`,
580
+ 'h': `height: ${cssVal};`,
581
+ 'min-w': `min-width: ${cssVal};`,
582
+ 'max-w': `max-width: ${cssVal};`,
583
+ 'min-h': `min-height: ${cssVal};`,
584
+ 'max-h': `max-height: ${cssVal};`
585
+ };
586
+ return propMap[property] || '';
587
+ }
588
+
567
589
  // Determine the CSS value
568
590
  const cssValue = isArbitrary ? value : `var(--s-${value})`;
569
591
 
@@ -1007,6 +1029,55 @@ function generateVisualRule(token, config) {
1007
1029
  return `border-radius: var(--r-${value});`;
1008
1030
  },
1009
1031
 
1032
+ // =====================
1033
+ // DIVIDE UTILITIES
1034
+ // =====================
1035
+
1036
+ // Divide color - all sides
1037
+ 'divide': () => {
1038
+ const cssValue = isArbitrary ? value : `var(--c-${value})`;
1039
+ return `border-color: ${cssValue}; border-style: solid;`;
1040
+ },
1041
+
1042
+ // Divide color - directional
1043
+ 'divide-x': () => {
1044
+ // Handle divide-x:reverse specially
1045
+ if (value === 'reverse') {
1046
+ return '--ss-divide-x-reverse: 1;';
1047
+ }
1048
+ const cssValue = isArbitrary ? value : `var(--c-${value})`;
1049
+ return `border-left-color: ${cssValue}; border-right-color: ${cssValue}; border-left-style: solid; border-right-style: solid;`;
1050
+ },
1051
+ 'divide-y': () => {
1052
+ // Handle divide-y:reverse specially
1053
+ if (value === 'reverse') {
1054
+ return '--ss-divide-y-reverse: 1;';
1055
+ }
1056
+ const cssValue = isArbitrary ? value : `var(--c-${value})`;
1057
+ return `border-top-color: ${cssValue}; border-bottom-color: ${cssValue}; border-top-style: solid; border-bottom-style: solid;`;
1058
+ },
1059
+
1060
+ // Divide width - all sides
1061
+ 'divide-w': () => {
1062
+ const cssValue = isArbitrary ? value : `var(--s-${value})`;
1063
+ return `border-width: ${cssValue}; border-style: solid;`;
1064
+ },
1065
+
1066
+ // Divide width - directional
1067
+ 'divide-x-w': () => {
1068
+ const cssValue = isArbitrary ? value : `var(--s-${value})`;
1069
+ return `border-left-width: ${cssValue}; border-right-width: ${cssValue}; border-left-style: solid; border-right-style: solid;`;
1070
+ },
1071
+ 'divide-y-w': () => {
1072
+ const cssValue = isArbitrary ? value : `var(--s-${value})`;
1073
+ return `border-top-width: ${cssValue}; border-bottom-width: ${cssValue}; border-top-style: solid; border-bottom-style: solid;`;
1074
+ },
1075
+
1076
+ // Divide style
1077
+ 'divide-style': () => {
1078
+ return `border-style: ${value};`;
1079
+ },
1080
+
1010
1081
  // Outline Width
1011
1082
  'outline-w': () => {
1012
1083
  const cssValue = isArbitrary ? value : `var(--s-${value})`;
@@ -1577,6 +1648,33 @@ function generateVisualRule(token, config) {
1577
1648
  return `transform: skewY(${cssValue});`;
1578
1649
  },
1579
1650
 
1651
+ // 3D Rotation (Rotate X/Y/Z)
1652
+ 'rotate-x': () => {
1653
+ const cssValue = isArbitrary ? value : `${value}deg`;
1654
+ return `transform: rotateX(${cssValue});`;
1655
+ },
1656
+
1657
+ 'rotate-y': () => {
1658
+ const cssValue = isArbitrary ? value : `${value}deg`;
1659
+ return `transform: rotateY(${cssValue});`;
1660
+ },
1661
+
1662
+ 'rotate-z': () => {
1663
+ const cssValue = isArbitrary ? value : `${value}deg`;
1664
+ return `transform: rotateZ(${cssValue});`;
1665
+ },
1666
+
1667
+ // 3D Translation (Translate Z)
1668
+ 'translate-z': () => {
1669
+ const translatePresets = {
1670
+ 'near': '50px',
1671
+ 'far': '-50px',
1672
+ '0': '0'
1673
+ };
1674
+ const cssValue = isArbitrary ? value : (translatePresets[value] || `var(--s-${value})`);
1675
+ return `transform: translateZ(${cssValue});`;
1676
+ },
1677
+
1580
1678
  // Transform Origin
1581
1679
  'origin': () => {
1582
1680
  const originMap = {
@@ -1887,12 +1985,27 @@ export function generateRule(token, config, skipDarkWrapper = false) {
1887
1985
 
1888
1986
  if (!cssDeclaration) return '';
1889
1987
 
1988
+ // Check if this is a divide utility (needs special selector)
1989
+ const isDivide = raw.startsWith('divide');
1990
+
1890
1991
  // Build selector
1891
- let selector = `[${attrType}~="${raw}"]`;
1992
+ let selector = '';
1993
+
1994
+ if (isDivide) {
1995
+ // Divide utilities use special child selector pattern
1996
+ selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])`;
1997
+ } else {
1998
+ selector = `[${attrType}~="${raw}"]`;
1999
+ }
1892
2000
 
1893
2001
  // Add state pseudo-class (but not for 'dark' - it's handled separately)
1894
2002
  if (state && state !== 'dark') {
1895
- selector += `:${state}`;
2003
+ if (isDivide) {
2004
+ // For divide utilities, add state to the element after tilde
2005
+ selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden]):${state}`;
2006
+ } else {
2007
+ selector += `:${state}`;
2008
+ }
1896
2009
  }
1897
2010
 
1898
2011
  return `${selector} { ${cssDeclaration} }\n`;
@@ -32,19 +32,45 @@ export const defaultConfig = {
32
32
  preflight: true,
33
33
 
34
34
  theme: {
35
- // 1. SPACING: The "Natural Object" Scale
35
+ // 1. SPACING: The "Natural Object" Scale with multiplier variants
36
36
  // Logic: How big is the object/gap physically?
37
37
  spacing: {
38
- 'none': '0px', // No space
39
- 'thin': '1px', // Hairline (for borders)
40
- 'regular': '2px', // Standard border
41
- 'thick': '3px', // Bold border
42
- 'tiny': '4px', // Pebble (Small offsets)
43
- 'small': '8px', // Matchbox (Grouping inside components)
44
- 'medium': '16px', // Smartphone (Standard default)
45
- 'big': '32px', // Laptop (Separation between groups)
46
- 'giant': '64px', // Door (Layout sections)
47
- 'vast': '128px' // House (Hero sections)
38
+ 'none': '0px', // No space
39
+ 'thin': '1px', // Hairline (for borders)
40
+ 'regular': '2px', // Standard border
41
+ 'thick': '3px', // Bold border
42
+ 'tiny': '4px', // Small offsets
43
+ 'tiny-2x': '6px', // Tiny multiplied
44
+ 'small': '8px', // Grouping inside components
45
+ 'small-2x': '10px', //
46
+ 'small-3x': '12px', //
47
+ 'small-4x': '14px', //
48
+ 'medium': '16px', // Standard default
49
+ 'medium-2x': '20px', //
50
+ 'medium-3x': '24px', //
51
+ 'medium-4x': '28px', //
52
+ 'large': '32px', // Separation between groups
53
+ 'large-2x': '36px', //
54
+ 'large-3x': '40px', //
55
+ 'large-4x': '44px', //
56
+ 'big': '48px', // Layout sections
57
+ 'big-2x': '56px', //
58
+ 'big-3x': '64px', //
59
+ 'big-4x': '80px', //
60
+ 'giant': '96px', // Hero sections
61
+ 'giant-2x': '112px', //
62
+ 'giant-3x': '128px', //
63
+ 'giant-4x': '144px', //
64
+ 'vast': '160px', // Page-level spacing
65
+ 'vast-2x': '176px', //
66
+ 'vast-3x': '192px', //
67
+ 'vast-4x': '208px', //
68
+ 'vast-5x': '224px', //
69
+ 'vast-6x': '240px', //
70
+ 'vast-7x': '256px', //
71
+ 'vast-8x': '288px', //
72
+ 'vast-9x': '320px', //
73
+ 'vast-10x': '384px', //
48
74
  },
49
75
 
50
76
  // 2. RADIUS: Tactile Feel