@bookklik/senangstart-css 0.2.3 → 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.
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import * as esbuild from 'esbuild';
9
- import { mkdirSync, statSync } from 'fs';
9
+ import { mkdirSync, statSync, copyFileSync } from 'fs';
10
10
  import { dirname, join } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
 
@@ -49,6 +49,48 @@ await esbuild.build({
49
49
  const minSize = statSync(join(distDir, 'senangstart-css.min.js')).size;
50
50
  console.log(`✓ Created senangstart-css.min.js (${(minSize / 1024).toFixed(1)} KB)`);
51
51
 
52
+ // Build Tailwind converter - unminified
53
+ await esbuild.build({
54
+ entryPoints: [join(root, 'src', 'cdn', 'tw-conversion-engine.js')],
55
+ bundle: true,
56
+ format: 'iife',
57
+ outfile: join(distDir, 'senangstart-tw.js'),
58
+ minify: false,
59
+ banner: {
60
+ js: '/* SenangStart CSS - Tailwind Converter v0.2.0 | MIT License */'
61
+ }
62
+ });
63
+
64
+ const twUnminSize = statSync(join(distDir, 'senangstart-tw.js')).size;
65
+ console.log(`✓ Created senangstart-tw.js (${(twUnminSize / 1024).toFixed(1)} KB)`);
66
+
67
+ // Build Tailwind converter - minified
68
+ await esbuild.build({
69
+ entryPoints: [join(root, 'src', 'cdn', 'tw-conversion-engine.js')],
70
+ bundle: true,
71
+ format: 'iife',
72
+ outfile: join(distDir, 'senangstart-tw.min.js'),
73
+ minify: true,
74
+ banner: {
75
+ js: '/* SenangStart TW v0.2.0 | MIT */'
76
+ }
77
+ });
78
+
79
+ const twMinSize = statSync(join(distDir, 'senangstart-tw.min.js')).size;
80
+ console.log(`✓ Created senangstart-tw.min.js (${(twMinSize / 1024).toFixed(1)} KB)`);
81
+
82
+ // Copy to docs/public/assets for VitePress
83
+ const docsAssetsDir = join(root, 'docs', 'public', 'assets');
84
+ mkdirSync(docsAssetsDir, { recursive: true });
85
+ copyFileSync(
86
+ join(distDir, 'senangstart-css.min.js'),
87
+ join(docsAssetsDir, 'senangstart-css.min.js')
88
+ );
89
+ console.log(`✓ Copied to docs/public/assets/senangstart-css.min.js`);
90
+
52
91
  console.log('\n📦 Dist build complete!');
53
92
  console.log(' dist/senangstart-css.js');
54
93
  console.log(' dist/senangstart-css.min.js');
94
+ console.log(' dist/senangstart-tw.js');
95
+ console.log(' dist/senangstart-tw.min.js');
96
+
@@ -583,6 +583,30 @@ function convertClass(twClass, options = {}) {
583
583
  return { category: 'visual', value: prefix + "opacity:" + opacityMatch[1] };
584
584
  }
585
585
 
586
+ // Gradient direction (bg-gradient-to-*)
587
+ const bgGradientMatch = baseClass.match(/^bg-gradient-to-(t|tr|r|br|b|bl|l|tl)$/);
588
+ if (bgGradientMatch) {
589
+ return { category: 'visual', value: prefix + "bg-image:gradient-to-" + bgGradientMatch[1] };
590
+ }
591
+
592
+ // Gradient from-* (starting color)
593
+ const fromMatch = baseClass.match(/^from-(.+)$/);
594
+ if (fromMatch) {
595
+ return { category: 'visual', value: prefix + "from:" + fromMatch[1] };
596
+ }
597
+
598
+ // Gradient via-* (middle color)
599
+ const viaMatch = baseClass.match(/^via-(.+)$/);
600
+ if (viaMatch) {
601
+ return { category: 'visual', value: prefix + "via:" + viaMatch[1] };
602
+ }
603
+
604
+ // Gradient to-* (ending color) - Note: must come after bg-gradient-to-*
605
+ const toMatch = baseClass.match(/^to-(.+)$/);
606
+ if (toMatch) {
607
+ return { category: 'visual', value: prefix + "to:" + toMatch[1] };
608
+ }
609
+
586
610
  return null;
587
611
  }
588
612
 
package/src/cdn/jit.js CHANGED
@@ -25,6 +25,9 @@ import { tokenize, parseToken } from '../core/tokenizer-core.js';
25
25
  theme: {
26
26
  spacing: {
27
27
  'none': '0px',
28
+ 'thin': '1px',
29
+ 'regular': '2px',
30
+ 'thick': '3px',
28
31
  'tiny': '4px',
29
32
  'small': '8px',
30
33
  'medium': '16px',
@@ -1658,6 +1661,22 @@ img, video {
1658
1661
  return `background-image: ${cssValue};`;
1659
1662
  },
1660
1663
 
1664
+ // ============================================
1665
+ // GRADIENT COLOR STOPS
1666
+ // ============================================
1667
+ 'from': () => {
1668
+ const color = isArbitrary ? value : `var(--c-${value})`;
1669
+ return `--tw-gradient-from: ${color}; --tw-gradient-to: transparent; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);`;
1670
+ },
1671
+ 'via': () => {
1672
+ const color = isArbitrary ? value : `var(--c-${value})`;
1673
+ return `--tw-gradient-via: ${color}; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-via), var(--tw-gradient-to);`;
1674
+ },
1675
+ 'to': () => {
1676
+ const color = isArbitrary ? value : `var(--c-${value})`;
1677
+ return `--tw-gradient-to: ${color};`;
1678
+ },
1679
+
1661
1680
  // ============================================
1662
1681
  // BACKDROP FILTER UTILITIES
1663
1682
  // ============================================
@@ -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
+ };
@@ -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
  }