@bookklik/senangstart-css 0.2.0 → 0.2.4

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 (149) hide show
  1. package/dist/senangstart-css.js +102 -7
  2. package/dist/senangstart-css.min.js +26 -24
  3. package/dist/senangstart-tw.js +389 -0
  4. package/dist/senangstart-tw.min.js +2 -0
  5. package/docs/.vitepress/config.js +13 -4
  6. package/docs/SYNTAX-REFERENCE.md +1590 -1555
  7. package/docs/index.md +6 -0
  8. package/docs/ms/index.md +6 -0
  9. package/docs/ms/reference/visual/accent-color.md +6 -6
  10. package/docs/ms/reference/visual/animation-builtin.md +6 -6
  11. package/docs/ms/reference/visual/animation-delay.md +4 -2
  12. package/docs/ms/reference/visual/animation-direction.md +6 -6
  13. package/docs/ms/reference/visual/animation-duration.md +4 -4
  14. package/docs/ms/reference/visual/animation-fill.md +33 -9
  15. package/docs/ms/reference/visual/animation-iteration.md +5 -5
  16. package/docs/ms/reference/visual/animation-play.md +4 -4
  17. package/docs/ms/reference/visual/appearance.md +4 -4
  18. package/docs/ms/reference/visual/blend-modes.md +6 -6
  19. package/docs/ms/reference/visual/border-radius.md +8 -8
  20. package/docs/ms/reference/visual/box-shadow.md +6 -6
  21. package/docs/ms/reference/visual/caret-color.md +2 -2
  22. package/docs/ms/reference/visual/cursor.md +6 -6
  23. package/docs/ms/reference/visual/filter-blur.md +7 -7
  24. package/docs/ms/reference/visual/font-family.md +8 -8
  25. package/docs/ms/reference/visual/font-smoothing.md +4 -4
  26. package/docs/ms/reference/visual/font-style.md +4 -4
  27. package/docs/ms/reference/visual/font-variant-numeric.md +6 -4
  28. package/docs/ms/reference/visual/font-weight.md +6 -6
  29. package/docs/ms/reference/visual/gradient-from.md +57 -0
  30. package/docs/ms/reference/visual/gradient-to.md +57 -0
  31. package/docs/ms/reference/visual/gradient-via.md +54 -0
  32. package/docs/ms/reference/visual/hyphens.md +6 -6
  33. package/docs/ms/reference/visual/letter-spacing.md +8 -8
  34. package/docs/ms/reference/visual/line-clamp.md +9 -9
  35. package/docs/ms/reference/visual/line-height.md +8 -8
  36. package/docs/ms/reference/visual/list-style.md +7 -7
  37. package/docs/ms/reference/visual/mask.md +6 -2
  38. package/docs/ms/reference/visual/opacity.md +8 -8
  39. package/docs/ms/reference/visual/pointer-events.md +4 -4
  40. package/docs/ms/reference/visual/state-prefixes.md +5 -3
  41. package/docs/ms/reference/visual/text-decoration.md +6 -4
  42. package/docs/ms/reference/visual/text-indent.md +8 -4
  43. package/docs/ms/reference/visual/text-overflow.md +6 -4
  44. package/docs/ms/reference/visual/text-shadow.md +8 -2
  45. package/docs/ms/reference/visual/text-size.md +31 -13
  46. package/docs/ms/reference/visual/text-transform.md +6 -6
  47. package/docs/ms/reference/visual/text-wrap.md +8 -8
  48. package/docs/ms/reference/visual/transform-backface.md +29 -9
  49. package/docs/ms/reference/visual/transform-origin.md +4 -4
  50. package/docs/ms/reference/visual/transform-perspective-origin.md +41 -7
  51. package/docs/ms/reference/visual/transform-perspective.md +41 -7
  52. package/docs/ms/reference/visual/transform-rotate-3d.md +93 -0
  53. package/docs/ms/reference/visual/transform-rotate.md +6 -6
  54. package/docs/ms/reference/visual/transform-scale.md +7 -7
  55. package/docs/ms/reference/visual/transform-skew.md +6 -6
  56. package/docs/ms/reference/visual/transform-style.md +31 -11
  57. package/docs/ms/reference/visual/transform-translate-z.md +90 -0
  58. package/docs/ms/reference/visual/transform-translate.md +40 -14
  59. package/docs/ms/reference/visual/transition-delay.md +4 -2
  60. package/docs/ms/reference/visual/transition-duration.md +4 -4
  61. package/docs/ms/reference/visual/transition-timing.md +6 -6
  62. package/docs/ms/reference/visual/typography-keywords.md +8 -8
  63. package/docs/ms/reference/visual/user-select.md +4 -4
  64. package/docs/ms/reference/visual/vertical-align.md +10 -8
  65. package/docs/ms/reference/visual/whitespace.md +8 -8
  66. package/docs/ms/reference/visual/word-break.md +8 -8
  67. package/docs/public/assets/senangstart-css.min.js +209 -1545
  68. package/docs/public/assets/ss-logo.svg +83 -0
  69. package/docs/reference/visual/accent-color.md +6 -6
  70. package/docs/reference/visual/animation-builtin.md +6 -6
  71. package/docs/reference/visual/animation-delay.md +4 -2
  72. package/docs/reference/visual/animation-direction.md +6 -6
  73. package/docs/reference/visual/animation-duration.md +4 -4
  74. package/docs/reference/visual/animation-fill.md +33 -9
  75. package/docs/reference/visual/animation-iteration.md +5 -5
  76. package/docs/reference/visual/animation-play.md +4 -4
  77. package/docs/reference/visual/appearance.md +4 -4
  78. package/docs/reference/visual/blend-modes.md +6 -6
  79. package/docs/reference/visual/border-radius.md +8 -8
  80. package/docs/reference/visual/box-shadow.md +6 -6
  81. package/docs/reference/visual/caret-color.md +2 -2
  82. package/docs/reference/visual/cursor.md +6 -6
  83. package/docs/reference/visual/filter-blur.md +7 -7
  84. package/docs/reference/visual/font-family.md +8 -8
  85. package/docs/reference/visual/font-smoothing.md +4 -4
  86. package/docs/reference/visual/font-style.md +4 -4
  87. package/docs/reference/visual/font-variant-numeric.md +6 -4
  88. package/docs/reference/visual/font-weight.md +6 -6
  89. package/docs/reference/visual/gradient-from.md +57 -0
  90. package/docs/reference/visual/gradient-to.md +57 -0
  91. package/docs/reference/visual/gradient-via.md +54 -0
  92. package/docs/reference/visual/hyphens.md +6 -6
  93. package/docs/reference/visual/letter-spacing.md +8 -8
  94. package/docs/reference/visual/line-clamp.md +9 -9
  95. package/docs/reference/visual/line-height.md +8 -8
  96. package/docs/reference/visual/list-style.md +7 -7
  97. package/docs/reference/visual/mask.md +6 -2
  98. package/docs/reference/visual/opacity.md +8 -8
  99. package/docs/reference/visual/pointer-events.md +4 -4
  100. package/docs/reference/visual/state-prefixes.md +5 -3
  101. package/docs/reference/visual/text-decoration.md +6 -4
  102. package/docs/reference/visual/text-indent.md +8 -4
  103. package/docs/reference/visual/text-overflow.md +6 -4
  104. package/docs/reference/visual/text-shadow.md +8 -2
  105. package/docs/reference/visual/text-size.md +31 -13
  106. package/docs/reference/visual/text-transform.md +6 -6
  107. package/docs/reference/visual/text-wrap.md +8 -8
  108. package/docs/reference/visual/transform-backface.md +29 -9
  109. package/docs/reference/visual/transform-origin.md +4 -4
  110. package/docs/reference/visual/transform-perspective-origin.md +41 -7
  111. package/docs/reference/visual/transform-perspective.md +41 -7
  112. package/docs/reference/visual/transform-rotate-3d.md +93 -0
  113. package/docs/reference/visual/transform-rotate.md +6 -6
  114. package/docs/reference/visual/transform-scale.md +7 -7
  115. package/docs/reference/visual/transform-skew.md +6 -6
  116. package/docs/reference/visual/transform-style.md +31 -11
  117. package/docs/reference/visual/transform-translate-z.md +90 -0
  118. package/docs/reference/visual/transform-translate.md +40 -14
  119. package/docs/reference/visual/transition-delay.md +4 -2
  120. package/docs/reference/visual/transition-duration.md +4 -4
  121. package/docs/reference/visual/transition-timing.md +6 -6
  122. package/docs/reference/visual/typography-keywords.md +8 -8
  123. package/docs/reference/visual/user-select.md +4 -4
  124. package/docs/reference/visual/vertical-align.md +10 -8
  125. package/docs/reference/visual/whitespace.md +8 -8
  126. package/docs/reference/visual/word-break.md +8 -8
  127. package/docs/syntax-reference.json +2009 -1972
  128. package/package.json +2 -2
  129. package/playground/index.html +37 -38
  130. package/playground/sample_code.txt +23 -0
  131. package/playground/tw-convertor.html +103 -589
  132. package/scripts/build-dist.js +43 -1
  133. package/scripts/convert-tailwind.js +24 -0
  134. package/scripts/generate-docs.js +9 -7
  135. package/src/cdn/jit.js +70 -7
  136. package/src/cdn/tw-conversion-engine.js +497 -0
  137. package/src/compiler/generators/css.js +24 -2
  138. package/src/config/defaults.js +31 -7
  139. package/src/definitions/layout-positioning.js +6 -6
  140. package/src/definitions/visual-backgrounds.js +113 -15
  141. package/src/definitions/visual-borders.js +1 -1
  142. package/src/definitions/visual-filters.js +16 -16
  143. package/src/definitions/visual-svg.js +5 -5
  144. package/src/definitions/visual-transform3d.js +202 -36
  145. package/src/definitions/visual-transforms.js +42 -25
  146. package/src/definitions/visual-transitions.js +40 -26
  147. package/src/definitions/visual-typography.js +53 -44
  148. package/src/definitions/visual.js +73 -58
  149. package/tests/unit/convert-tailwind.test.js +8 -0
@@ -0,0 +1,497 @@
1
+ /**
2
+ * SenangStart CSS - Tailwind to SenangStart Conversion Engine
3
+ * Converts Tailwind CSS class syntax to SenangStart CSS attribute syntax
4
+ */
5
+
6
+ // ============================
7
+ // MAPPING SCALES
8
+ // ============================
9
+
10
+ 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",
46
+ full: "[100%]",
47
+ screen: "[100vw]",
48
+ auto: "auto",
49
+ };
50
+
51
+ 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",
61
+ };
62
+
63
+ const shadowScale = {
64
+ sm: "small",
65
+ "": "small",
66
+ md: "medium",
67
+ lg: "big",
68
+ xl: "giant",
69
+ "2xl": "giant",
70
+ none: "none",
71
+ };
72
+
73
+ 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
87
+ };
88
+
89
+ const layoutMappings = {
90
+ container: "container",
91
+ flex: "flex",
92
+ "inline-flex": "inline-flex",
93
+ grid: "grid",
94
+ "inline-grid": "inline-grid",
95
+ block: "block",
96
+ "inline-block": "inline",
97
+ hidden: "hidden",
98
+ "flex-row": "row",
99
+ "flex-col": "col",
100
+ "flex-row-reverse": "row-reverse",
101
+ "flex-col-reverse": "col-reverse",
102
+ "flex-wrap": "wrap",
103
+ "flex-nowrap": "nowrap",
104
+ "flex-wrap-reverse": "wrap-reverse",
105
+ "flex-grow": "grow",
106
+ "flex-grow-0": "grow-0",
107
+ grow: "grow",
108
+ "grow-0": "grow-0",
109
+ "flex-shrink": "shrink",
110
+ "flex-shrink-0": "shrink-0",
111
+ shrink: "shrink",
112
+ "shrink-0": "shrink-0",
113
+ "flex-1": "flex:1",
114
+ "flex-auto": "flex:auto",
115
+ "flex-initial": "flex:initial",
116
+ "flex-none": "flex:none",
117
+ "justify-start": "justify:start",
118
+ "justify-end": "justify:end",
119
+ "justify-center": "justify:center",
120
+ "justify-between": "justify:between",
121
+ "justify-around": "justify:around",
122
+ "justify-evenly": "justify:evenly",
123
+ "items-start": "items:start",
124
+ "items-end": "items:end",
125
+ "items-center": "items:center",
126
+ "items-baseline": "items:baseline",
127
+ "items-stretch": "items:stretch",
128
+ "self-auto": "self:auto",
129
+ "self-start": "self:start",
130
+ "self-end": "self:end",
131
+ "self-center": "self:center",
132
+ "self-stretch": "self:stretch",
133
+ relative: "relative",
134
+ absolute: "absolute",
135
+ fixed: "fixed",
136
+ sticky: "sticky",
137
+ static: "static",
138
+ "overflow-auto": "overflow:auto",
139
+ "overflow-hidden": "overflow:hidden",
140
+ "overflow-visible": "overflow:visible",
141
+ "overflow-scroll": "overflow:scroll",
142
+ "object-contain": "object:contain",
143
+ "object-cover": "object:cover",
144
+ "object-fill": "object:fill",
145
+ "object-none": "object:none",
146
+ "object-scale-down": "object:scale-down",
147
+ };
148
+
149
+ const visualKeywords = {
150
+ italic: "italic",
151
+ "not-italic": "not-italic",
152
+ antialiased: "antialiased",
153
+ uppercase: "uppercase",
154
+ lowercase: "lowercase",
155
+ capitalize: "capitalize",
156
+ "normal-case": "normal-case",
157
+ underline: "underline",
158
+ "line-through": "line-through",
159
+ "no-underline": "no-underline",
160
+ truncate: "truncate",
161
+ "cursor-pointer": "cursor:pointer",
162
+ "cursor-default": "cursor:default",
163
+ "cursor-not-allowed": "cursor:not-allowed",
164
+ "select-none": "select:none",
165
+ "select-text": "select:text",
166
+ "select-all": "select:all",
167
+ };
168
+
169
+ // ============================
170
+ // HELPER FUNCTIONS
171
+ // ============================
172
+
173
+ function getSpacing(value, exact) {
174
+ if (exact) {
175
+ if (["full", "screen", "auto"].includes(value))
176
+ return spacingScale[value] || `[${value}]`;
177
+ return `tw-${value}`;
178
+ }
179
+ return spacingScale[value] || `[${value}]`;
180
+ }
181
+
182
+ // ============================
183
+ // CONVERSION FUNCTIONS
184
+ // ============================
185
+
186
+ function convertClass(twClass, exact) {
187
+ // Handle prefixes (hover:, sm:, md:, etc.)
188
+ const prefixMatch = twClass.match(
189
+ /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|active:|disabled:|dark:)(.+)$/
190
+ );
191
+ let prefix = "",
192
+ baseClass = twClass;
193
+ if (prefixMatch) {
194
+ const rawPrefix = prefixMatch[1].slice(0, -1); // remove colon
195
+ if (['sm', 'md', 'lg', 'xl', '2xl'].includes(rawPrefix)) {
196
+ prefix = `tw-${rawPrefix}:`;
197
+ } else {
198
+ prefix = prefixMatch[1];
199
+ }
200
+ baseClass = prefixMatch[2];
201
+ }
202
+
203
+ // Layout mappings
204
+ if (layoutMappings[baseClass])
205
+ return { cat: "layout", val: prefix + layoutMappings[baseClass] };
206
+
207
+ // Visual keywords
208
+ if (visualKeywords[baseClass])
209
+ return { cat: "visual", val: prefix + visualKeywords[baseClass] };
210
+
211
+ // Text color
212
+ const textColorMatch = baseClass.match(
213
+ /^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+)?)$/
214
+ );
215
+ if (textColorMatch)
216
+ return { cat: "visual", val: prefix + "text:" + textColorMatch[1] };
217
+
218
+ // Text alignment
219
+ if (
220
+ ["text-left", "text-center", "text-right", "text-justify"].includes(
221
+ baseClass
222
+ )
223
+ )
224
+ return {
225
+ cat: "visual",
226
+ val: prefix + "text:" + baseClass.replace("text-", ""),
227
+ };
228
+
229
+ // Text size
230
+ const textSizeMatch = baseClass.match(
231
+ /^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/
232
+ );
233
+ if (textSizeMatch) {
234
+ const size = exact
235
+ ? `tw-${textSizeMatch[1]}`
236
+ : fontSizeScale[textSizeMatch[1]] || textSizeMatch[1];
237
+ return { cat: "visual", val: prefix + "text-size:" + size };
238
+ }
239
+
240
+ // Background color
241
+ 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+)?)$/
243
+ );
244
+ if (bgMatch) return { cat: "visual", val: prefix + "bg:" + bgMatch[1] };
245
+
246
+ // Border color
247
+ const borderColorMatch = baseClass.match(
248
+ /^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+)?)$/
249
+ );
250
+ if (borderColorMatch)
251
+ return {
252
+ cat: "visual",
253
+ val: prefix + "border:" + borderColorMatch[1],
254
+ };
255
+
256
+ // Padding
257
+ const paddingMatch = baseClass.match(/^p([trblxy])?-(.+)$/);
258
+ if (paddingMatch) {
259
+ const side = paddingMatch[1] ? "-" + paddingMatch[1] : "";
260
+ return {
261
+ cat: "space",
262
+ val: prefix + "p" + side + ":" + getSpacing(paddingMatch[2], exact),
263
+ };
264
+ }
265
+
266
+ // Margin
267
+ const marginMatch = baseClass.match(
268
+ /^-?m([trblxy])?-(\d+\.?\d*|px|auto|full|screen)$/
269
+ );
270
+ if (marginMatch) {
271
+ const isNeg = baseClass.startsWith("-");
272
+ const side = marginMatch[1] ? "-" + marginMatch[1] : "";
273
+ let val = getSpacing(marginMatch[2], exact);
274
+ if (isNeg) val = "[-" + val + "]";
275
+ return { cat: "space", val: prefix + "m" + side + ":" + val };
276
+ }
277
+
278
+ // Gap
279
+ const gapMatch = baseClass.match(/^gap-([xy])?-?(.+)$/);
280
+ if (gapMatch) {
281
+ const axis = gapMatch[1] ? "-" + gapMatch[1] : "";
282
+ return {
283
+ cat: "space",
284
+ val: prefix + "g" + axis + ":" + getSpacing(gapMatch[2], exact),
285
+ };
286
+ }
287
+
288
+ // Width/Height with special values
289
+ const widthMatch = baseClass.match(/^(min-w|max-w|w)-(.+)$/);
290
+ if (widthMatch) {
291
+ const prop = widthMatch[1];
292
+ const rawVal = widthMatch[2];
293
+ // Special width values
294
+ const specialWidthVals = { 'max': '[max-content]', 'min': '[min-content]', 'fit': '[fit-content]', 'prose': '[65ch]' };
295
+ const val = specialWidthVals[rawVal] || getSpacing(rawVal, exact);
296
+ return { cat: "space", val: prefix + prop + ":" + val };
297
+ }
298
+ const heightMatch = baseClass.match(/^(min-h|max-h|h)-(.+)$/);
299
+ if (heightMatch) {
300
+ const prop = heightMatch[1];
301
+ const rawVal = heightMatch[2];
302
+ const specialHeightVals = { 'screen': '[100vh]', 'svh': '[100svh]', 'lvh': '[100lvh]', 'dvh': '[100dvh]', 'max': '[max-content]', 'min': '[min-content]', 'fit': '[fit-content]' };
303
+ const val = specialHeightVals[rawVal] || getSpacing(rawVal, exact);
304
+ return { cat: "space", val: prefix + prop + ":" + val };
305
+ }
306
+
307
+ // Border radius
308
+ const roundedMatch = baseClass.match(/^rounded(?:-(.+))?$/);
309
+ if (roundedMatch) {
310
+ const size = roundedMatch[1] || "";
311
+ const scale = exact
312
+ ? size === ""
313
+ ? "tw-DEFAULT"
314
+ : `tw-${size}`
315
+ : radiusScale[size] || "medium";
316
+ return { cat: "visual", val: prefix + "rounded:" + scale };
317
+ }
318
+
319
+ // Shadow
320
+ const shadowMatch = baseClass.match(/^shadow(?:-(.+))?$/);
321
+ if (shadowMatch) {
322
+ const size = shadowMatch[1] || "";
323
+ const scale = exact
324
+ ? size === ""
325
+ ? "tw-DEFAULT"
326
+ : `tw-${size}`
327
+ : shadowScale[size] || "medium";
328
+ return { cat: "visual", val: prefix + "shadow:" + scale };
329
+ }
330
+
331
+ // Font weight
332
+ const fontWeightMatch = baseClass.match(
333
+ /^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/
334
+ );
335
+ if (fontWeightMatch)
336
+ return { cat: "visual", val: prefix + "font:tw-" + fontWeightMatch[1] };
337
+
338
+ // Border width
339
+ const borderWidthMatch = baseClass.match(
340
+ /^border(?:-([trblxy]))?(?:-(\d+))?$/
341
+ );
342
+ if (
343
+ borderWidthMatch &&
344
+ (borderWidthMatch[2] ||
345
+ (!borderWidthMatch[1] && baseClass === "border"))
346
+ ) {
347
+ const side = borderWidthMatch[1]
348
+ ? "-" + borderWidthMatch[1] + "-w"
349
+ : "-w";
350
+ const width = borderWidthMatch[2] || "1";
351
+ return {
352
+ cat: "visual",
353
+ val: prefix + "border" + side + ":[" + width + "px]",
354
+ };
355
+ }
356
+
357
+ // Order
358
+ const orderMatch = baseClass.match(/^order-(\d+|first|last|none)$/);
359
+ if (orderMatch) {
360
+ return { cat: "layout", val: prefix + "order:" + orderMatch[1] };
361
+ }
362
+
363
+ // Grid columns
364
+ const gridColsMatch = baseClass.match(/^grid-cols-(\d+|none)$/);
365
+ if (gridColsMatch) {
366
+ return { cat: "layout", val: prefix + "grid-cols:" + gridColsMatch[1] };
367
+ }
368
+
369
+ // Column span
370
+ const colSpanMatch = baseClass.match(/^col-span-(\d+|full)$/);
371
+ if (colSpanMatch) {
372
+ return { cat: "layout", val: prefix + "col-span:" + colSpanMatch[1] };
373
+ }
374
+
375
+ // Grid rows
376
+ const gridRowsMatch = baseClass.match(/^grid-rows-(\d+|none)$/);
377
+ if (gridRowsMatch) {
378
+ return { cat: "layout", val: prefix + "grid-rows:" + gridRowsMatch[1] };
379
+ }
380
+
381
+ // Row span
382
+ const rowSpanMatch = baseClass.match(/^row-span-(\d+|full)$/);
383
+ if (rowSpanMatch) {
384
+ return { cat: "layout", val: prefix + "row-span:" + rowSpanMatch[1] };
385
+ }
386
+
387
+ // Opacity
388
+ const opacityMatch = baseClass.match(/^opacity-(\d+)$/);
389
+ if (opacityMatch) {
390
+ return { cat: "visual", val: prefix + "opacity:" + opacityMatch[1] };
391
+ }
392
+
393
+ // Gradient direction (bg-gradient-to-*)
394
+ const bgGradientMatch = baseClass.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);
395
+ if (bgGradientMatch) {
396
+ return { cat: "visual", val: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] };
397
+ }
398
+
399
+ // Gradient from-* (starting color)
400
+ const fromMatch = baseClass.match(/^from-(.+)$/);
401
+ if (fromMatch) {
402
+ return { cat: "visual", val: prefix + "from:" + fromMatch[1] };
403
+ }
404
+
405
+ // Gradient via-* (middle color)
406
+ const viaMatch = baseClass.match(/^via-(.+)$/);
407
+ if (viaMatch) {
408
+ return { cat: "visual", val: prefix + "via:" + viaMatch[1] };
409
+ }
410
+
411
+ // Gradient to-* (ending color) - Note: must come after bg-gradient-to-*
412
+ const toMatch = baseClass.match(/^to-(.+)$/);
413
+ if (toMatch) {
414
+ return { cat: "visual", val: prefix + "to:" + toMatch[1] };
415
+ }
416
+
417
+ return null;
418
+ }
419
+
420
+ function convertClasses(classString, exact) {
421
+ const classes = classString
422
+ .trim()
423
+ .split(/\s+/)
424
+ .filter((c) => c);
425
+ const layout = [],
426
+ space = [],
427
+ visual = [],
428
+ unknown = [];
429
+
430
+ for (const cls of classes) {
431
+ const result = convertClass(cls, exact);
432
+ if (result) {
433
+ if (result.cat === "layout") layout.push(result.val);
434
+ else if (result.cat === "space") space.push(result.val);
435
+ else if (result.cat === "visual") visual.push(result.val);
436
+ } else {
437
+ unknown.push(cls);
438
+ }
439
+ }
440
+
441
+ return { layout, space, visual, unknown };
442
+ }
443
+
444
+ function convertHTML(html, exact) {
445
+ return html.replace(
446
+ /class=(['"])([^"']+)\1/g,
447
+ (match, quote, classValue) => {
448
+ const { layout, space, visual, unknown } = convertClasses(
449
+ classValue,
450
+ exact
451
+ );
452
+ const attrs = [];
453
+ if (layout.length) attrs.push(`layout="${layout.join(" ")}"`);
454
+ if (space.length) attrs.push(`space="${space.join(" ")}"`);
455
+ if (visual.length) attrs.push(`visual="${visual.join(" ")}"`);
456
+ if (unknown.length) attrs.push(`class="${unknown.join(" ")}"`);
457
+ return attrs.join(" ") || 'class=""';
458
+ }
459
+ );
460
+ }
461
+
462
+ // ============================
463
+ // EXPORTS
464
+ // ============================
465
+
466
+ // Export for browser (IIFE global)
467
+ if (typeof window !== 'undefined') {
468
+ window.SenangStartTW = {
469
+ convertClass,
470
+ convertClasses,
471
+ convertHTML,
472
+ // Expose scales for customization
473
+ scales: {
474
+ spacing: spacingScale,
475
+ radius: radiusScale,
476
+ shadow: shadowScale,
477
+ fontSize: fontSizeScale,
478
+ },
479
+ mappings: {
480
+ layout: layoutMappings,
481
+ visual: visualKeywords,
482
+ },
483
+ };
484
+ }
485
+
486
+ // Export for ES modules
487
+ export {
488
+ convertClass,
489
+ convertClasses,
490
+ convertHTML,
491
+ spacingScale,
492
+ radiusScale,
493
+ shadowScale,
494
+ fontSizeScale,
495
+ layoutMappings,
496
+ visualKeywords,
497
+ };
@@ -51,6 +51,13 @@ export function generateCSSVariables(config) {
51
51
  css += ` --font-${key}: ${value};\n`;
52
52
  }
53
53
 
54
+ // Font size line-height variables (paired with font sizes)
55
+ if (theme.fontSizeLineHeight) {
56
+ for (const [key, value] of Object.entries(theme.fontSizeLineHeight)) {
57
+ css += ` --font-lh-${key}: ${value};\n`;
58
+ }
59
+ }
60
+
54
61
  // Font weight variables
55
62
  for (const [key, value] of Object.entries(theme.fontWeight)) {
56
63
  css += ` --fw-${key}: ${value};\n`;
@@ -118,6 +125,16 @@ export function generateCSSVariables(config) {
118
125
  css += ` --tw-text-${key}: ${value};\n`;
119
126
  }
120
127
 
128
+ // Tailwind Line Height Scale (paired with font sizes)
129
+ const twLeading = {
130
+ 'xs': '1rem', 'sm': '1.25rem', 'base': '1.5rem', 'lg': '1.75rem', 'xl': '1.75rem',
131
+ '2xl': '2rem', '3xl': '2.25rem', '4xl': '2.5rem', '5xl': '1',
132
+ '6xl': '1', '7xl': '1', '8xl': '1', '9xl': '1'
133
+ };
134
+ for (const [key, value] of Object.entries(twLeading)) {
135
+ css += ` --tw-leading-${key}: ${value};\n`;
136
+ }
137
+
121
138
  // Tailwind Font Weight Scale
122
139
  const twFontWeight = {
123
140
  'thin': '100',
@@ -817,18 +834,23 @@ function generateVisualRule(token, config) {
817
834
  return `text-shadow: ${cssValue};`;
818
835
  },
819
836
 
820
- // Font size
837
+ // Font size (with paired line-height)
821
838
  'text-size': () => {
822
839
  let cssValue;
840
+ let lineHeightValue;
823
841
  if (isArbitrary) {
824
842
  cssValue = value;
843
+ // No line-height for arbitrary values
844
+ return `font-size: ${cssValue};`;
825
845
  } else if (value.startsWith('tw-')) {
826
846
  const twValue = value.slice(3);
827
847
  cssValue = `var(--tw-text-${twValue})`;
848
+ lineHeightValue = `var(--tw-leading-${twValue})`;
828
849
  } else {
829
850
  cssValue = `var(--font-${value})`;
851
+ lineHeightValue = `var(--font-lh-${value})`;
830
852
  }
831
- return `font-size: ${cssValue};`;
853
+ return `font-size: ${cssValue}; line-height: ${lineHeightValue};`;
832
854
  },
833
855
 
834
856
  // Font weight / family
@@ -65,14 +65,38 @@ export const defaultConfig = {
65
65
  'giant': '0 25px 50px rgba(0,0,0,0.25)'
66
66
  },
67
67
 
68
- // 4. FONT SIZES: Reading Scale
68
+ // 4. FONT SIZES: Reading Scale (with paired line-heights)
69
69
  fontSize: {
70
- 'tiny': '12px',
71
- 'small': '14px',
72
- 'medium': '16px',
73
- 'big': '20px',
74
- 'giant': '32px',
75
- 'vast': '48px'
70
+ 'mini': '0.75rem', // 12px
71
+ 'small': '0.875rem', // 14px
72
+ 'base': '1rem', // 16px
73
+ 'large': '1.125rem', // 18px
74
+ 'big': '1.25rem', // 20px (xl)
75
+ 'huge': '1.5rem', // 24px (2xl)
76
+ 'grand': '1.875rem', // 30px (3xl)
77
+ 'giant': '2.25rem', // 36px (4xl)
78
+ 'mount': '3rem', // 48px (5xl)
79
+ 'mega': '3.75rem', // 60px (6xl)
80
+ 'giga': '4.5rem', // 72px (7xl)
81
+ 'tera': '6rem', // 96px (8xl)
82
+ 'hero': '8rem' // 128px
83
+ },
84
+
85
+ // 4b. FONT SIZE LINE-HEIGHTS: Paired with font sizes
86
+ fontSizeLineHeight: {
87
+ 'mini': '1rem', // 16px
88
+ 'small': '1.25rem', // 20px
89
+ 'base': '1.5rem', // 24px
90
+ 'large': '1.75rem', // 28px
91
+ 'big': '1.75rem', // 28px
92
+ 'huge': '2rem', // 32px
93
+ 'grand': '2.25rem', // 36px
94
+ 'giant': '2.5rem', // 40px
95
+ 'mount': '1', // 48px (unitless 1)
96
+ 'mega': '1', // 60px (unitless 1)
97
+ 'giga': '1', // 72px (unitless 1)
98
+ 'tera': '1', // 96px (unitless 1)
99
+ 'hero': '1' // 128px (unitless 1)
76
100
  },
77
101
 
78
102
  // 5. FONT WEIGHTS
@@ -33,7 +33,7 @@ export const position = {
33
33
  descriptionMs: 'Elemen diletakkan relatif kepada aliran normal',
34
34
  html: `<div layout="relative" space="p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
35
35
  <span space="p:small" visual="bg:primary text:white rounded:small">Relative Container</span>
36
- <span layout="absolute" style="top: 0; right: 0;" space="p:tiny" visual="bg:danger text:white rounded:small">Abs</span>
36
+ <span layout="absolute top:0 right:0" space="p:tiny" visual="bg:danger text:white rounded:small">Abs</span>
37
37
  </div>`,
38
38
  highlightValue: 'relative'
39
39
  },
@@ -43,7 +43,7 @@ export const position = {
43
43
  description: 'Element sticks when scrolling past it',
44
44
  descriptionMs: 'Elemen melekat apabila skrol melepasi',
45
45
  html: `<div space="p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
46
- <span layout="sticky" style="top: 0;" space="p:small" visual="bg:primary text:white rounded:small">Sticky Header</span>
46
+ <span layout="sticky top:0" space="p:small" visual="bg:primary text:white rounded:small">Sticky Header</span>
47
47
  </div>`,
48
48
  highlightValue: 'sticky'
49
49
  }
@@ -133,10 +133,10 @@ export const zIndex = {
133
133
  description: 'Control stacking order of positioned elements',
134
134
  descriptionMs: 'Kawal susunan tindanan elemen yang diletakkan',
135
135
  html: `<div layout="relative" space="p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium" style="height: 80px;">
136
- <span layout="absolute z:base" style="left: 0; top: 10px;" space="p:small" visual="bg:neutral-400 text:white rounded:small">z:base</span>
137
- <span layout="absolute z:low" style="left: 30px; top: 20px;" space="p:small" visual="bg:neutral-500 text:white rounded:small">z:low</span>
138
- <span layout="absolute z:mid" style="left: 60px; top: 30px;" space="p:small" visual="bg:neutral-600 text:white rounded:small">z:mid</span>
139
- <span layout="absolute z:high" style="left: 90px; top: 40px;" space="p:small" visual="bg:primary text:white rounded:small">z:high</span>
136
+ <span layout="absolute z:base left:0 top:10px" space="p:small" visual="bg:neutral-400 text:white rounded:small">z:base</span>
137
+ <span layout="absolute z:low left:30px top:20px" space="p:small" visual="bg:neutral-500 text:white rounded:small">z:low</span>
138
+ <span layout="absolute z:mid left:60px top:30px" space="p:small" visual="bg:neutral-600 text:white rounded:small">z:mid</span>
139
+ <span layout="absolute z:high left:90px top:40px" space="p:small" visual="bg:primary text:white rounded:small">z:high</span>
140
140
  </div>`,
141
141
  highlightValue: 'z:high'
142
142
  }