@bookklik/senangstart-css 0.2.9 → 0.2.12
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.
- package/.agent/skills/add-utility/SKILL.md +65 -0
- package/.agent/workflows/add-utility.md +2 -0
- package/.agent/workflows/build.md +2 -0
- package/.agent/workflows/dev.md +2 -0
- package/AGENTS.md +30 -0
- package/dist/senangstart-css.js +607 -180
- package/dist/senangstart-css.min.js +234 -195
- package/dist/senangstart-tw.js +274 -8
- package/dist/senangstart-tw.min.js +1 -1
- package/docs/SYNTAX-REFERENCE.md +1731 -1590
- package/docs/guide/preflight.md +20 -1
- package/docs/ms/guide/preflight.md +19 -0
- package/docs/ms/reference/breakpoints.md +14 -0
- package/docs/ms/reference/visual/border-radius.md +50 -10
- package/docs/ms/reference/visual/contain.md +57 -0
- package/docs/ms/reference/visual/content-visibility.md +53 -0
- package/docs/ms/reference/visual/placeholder-color.md +92 -0
- package/docs/ms/reference/visual/ring-color.md +2 -2
- package/docs/ms/reference/visual/ring-offset.md +3 -3
- package/docs/ms/reference/visual/ring.md +5 -5
- package/docs/ms/reference/visual/writing-mode.md +53 -0
- package/docs/ms/reference/visual.md +6 -0
- package/docs/public/assets/senangstart-css.min.js +234 -195
- package/docs/public/llms.txt +45 -12
- package/docs/reference/breakpoints.md +14 -0
- package/docs/reference/visual/border-radius.md +50 -10
- package/docs/reference/visual/contain.md +57 -0
- package/docs/reference/visual/content-visibility.md +53 -0
- package/docs/reference/visual/placeholder-color.md +92 -0
- package/docs/reference/visual/ring-color.md +2 -2
- package/docs/reference/visual/ring-offset.md +3 -3
- package/docs/reference/visual/ring.md +5 -5
- package/docs/reference/visual/writing-mode.md +53 -0
- package/docs/reference/visual.md +7 -0
- package/docs/syntax-reference.json +2185 -2009
- package/package.json +1 -1
- package/scripts/convert-tailwind.js +300 -26
- package/scripts/generate-docs.js +403 -403
- package/src/cdn/senangstart-engine.js +5 -5
- package/src/cdn/tw-conversion-engine.js +305 -8
- package/src/cli/commands/build.js +51 -13
- package/src/cli/commands/dev.js +157 -93
- package/src/compiler/generators/css.js +467 -208
- package/src/compiler/generators/preflight.js +26 -13
- package/src/compiler/generators/typescript.js +3 -1
- package/src/compiler/index.js +27 -3
- package/src/compiler/parser.js +13 -6
- package/src/compiler/tokenizer.js +25 -23
- package/src/config/defaults.js +3 -0
- package/src/core/tokenizer-core.js +46 -19
- package/src/definitions/index.js +4 -1
- package/src/definitions/visual-borders.js +10 -10
- package/src/definitions/visual-performance.js +126 -0
- package/src/definitions/visual.js +25 -9
- package/src/utils/common.js +456 -27
- package/src/utils/node-io.js +82 -0
- package/tests/integration/dev-recovery.test.js +231 -0
- package/tests/unit/cli/memory-limits.test.js +169 -0
- package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
- package/tests/unit/compiler/generators/css-errors.test.js +102 -0
- package/tests/unit/compiler/generators/css.test.js +102 -5
- package/tests/unit/convert-tailwind.test.js +518 -431
- package/tests/unit/utils/common.test.js +376 -26
- package/tests/unit/utils/file-timeout.test.js +154 -0
- package/tests/unit/utils/theme-validation.test.js +181 -0
- package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
- package/tests/unit/convert-tailwind.cli.test.js +0 -95
- package/tests/unit/security.test.js +0 -206
- /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
|
@@ -82,6 +82,19 @@ export function generateCSSVariables(config) {
|
|
|
82
82
|
css += ` --c-${key}: ${value};\n`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// Placeholder color variable
|
|
86
|
+
if (theme.placeholder) {
|
|
87
|
+
css += ` --placeholder-color: ${theme.placeholder};\n`;
|
|
88
|
+
} else {
|
|
89
|
+
css += ' --placeholder-color: #9ca3af;\n';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Gradient direction variables for better gradient support
|
|
93
|
+
css += ' --gradient-from: transparent;\n';
|
|
94
|
+
css += ' --gradient-via: transparent;\n';
|
|
95
|
+
css += ' --gradient-to: transparent;\n';
|
|
96
|
+
css += ' --gradient-stops: var(--gradient-from), var(--gradient-via), var(--gradient-to);\n';
|
|
97
|
+
|
|
85
98
|
// Z-index variables
|
|
86
99
|
for (const [key, value] of Object.entries(theme.zIndex)) {
|
|
87
100
|
css += ` --z-${key}: ${value};\n`;
|
|
@@ -166,6 +179,14 @@ export function generateCSSVariables(config) {
|
|
|
166
179
|
css += ` --tw-font-${key}: ${value};\n`;
|
|
167
180
|
}
|
|
168
181
|
|
|
182
|
+
// Divide reverse variables (used by divide-x:reverse and divide-y:reverse)
|
|
183
|
+
css += ' --ss-divide-x-reverse: 0;\n';
|
|
184
|
+
css += ' --ss-divide-y-reverse: 0;\n';
|
|
185
|
+
|
|
186
|
+
// Ring utility variables
|
|
187
|
+
css += ' --ring-inset: ;\n';
|
|
188
|
+
css += ' --ss-ring-color: var(--c-primary);\n';
|
|
189
|
+
|
|
169
190
|
css += '}\n\n';
|
|
170
191
|
return css;
|
|
171
192
|
}
|
|
@@ -309,6 +330,37 @@ function generateLayoutRule(token, config) {
|
|
|
309
330
|
return `object-position: ${cssValue};`;
|
|
310
331
|
}
|
|
311
332
|
|
|
333
|
+
// Content Visibility
|
|
334
|
+
if (property === 'content-visibility') {
|
|
335
|
+
return `content-visibility: ${value};`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Contain
|
|
339
|
+
if (property === 'contain') {
|
|
340
|
+
const containMap = {
|
|
341
|
+
'none': 'none',
|
|
342
|
+
'strict': 'strict',
|
|
343
|
+
'content': 'content',
|
|
344
|
+
'size': 'size',
|
|
345
|
+
'layout': 'layout',
|
|
346
|
+
'style': 'style',
|
|
347
|
+
'paint': 'paint'
|
|
348
|
+
};
|
|
349
|
+
const cssValue = isArbitrary ? value : (containMap[value] || value);
|
|
350
|
+
return `contain: ${cssValue};`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Writing Mode (for RTL support)
|
|
354
|
+
if (property === 'writing') {
|
|
355
|
+
const writingMap = {
|
|
356
|
+
'horizontal-tb': 'horizontal-tb',
|
|
357
|
+
'vertical-rl': 'vertical-rl',
|
|
358
|
+
'vertical-lr': 'vertical-lr'
|
|
359
|
+
};
|
|
360
|
+
const cssValue = isArbitrary ? value.replace(/_/g, ' ') : (writingMap[value] || value);
|
|
361
|
+
return `writing-mode: ${cssValue};`;
|
|
362
|
+
}
|
|
363
|
+
|
|
312
364
|
// Percentage adjectives for positioning utilities
|
|
313
365
|
const positioningPercentages = {
|
|
314
366
|
'full': '100%',
|
|
@@ -331,7 +383,7 @@ function generateLayoutRule(token, config) {
|
|
|
331
383
|
// Helper function to resolve positioning value
|
|
332
384
|
const resolvePositioningValue = (val, arb) => {
|
|
333
385
|
if (arb) return val;
|
|
334
|
-
if (val === '0') return '0';
|
|
386
|
+
if (!val || val === '0') return '0';
|
|
335
387
|
// Check for negative percentage adjective
|
|
336
388
|
if (val.startsWith('-')) {
|
|
337
389
|
const positiveVal = val.substring(1);
|
|
@@ -590,8 +642,8 @@ function generateSpaceRule(token, config) {
|
|
|
590
642
|
cssValue = value;
|
|
591
643
|
} else {
|
|
592
644
|
// Check for negative value
|
|
593
|
-
const isNegative = value.startsWith('-');
|
|
594
|
-
const cleanValue = isNegative ? value.substring(1) : value;
|
|
645
|
+
const isNegative = value && value.startsWith('-');
|
|
646
|
+
const cleanValue = isNegative ? value.substring(1) : (value || '');
|
|
595
647
|
|
|
596
648
|
let baseValue;
|
|
597
649
|
if (cleanValue.startsWith('tw-')) {
|
|
@@ -679,7 +731,7 @@ function generateVisualRule(token, config) {
|
|
|
679
731
|
|
|
680
732
|
// Background Image
|
|
681
733
|
'bg-image': () => {
|
|
682
|
-
if (value === 'none') return 'background-image: none;';
|
|
734
|
+
if (!value || value === 'none') return 'background-image: none;';
|
|
683
735
|
|
|
684
736
|
// Handle gradient definitions
|
|
685
737
|
if (value.startsWith('gradient-to-')) {
|
|
@@ -951,33 +1003,33 @@ function generateVisualRule(token, config) {
|
|
|
951
1003
|
// Border width
|
|
952
1004
|
'border-w': () => {
|
|
953
1005
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
954
|
-
return `border-width: ${cssValue}
|
|
1006
|
+
return `border-width: ${cssValue};`;
|
|
955
1007
|
},
|
|
956
1008
|
|
|
957
1009
|
// Border width - directional
|
|
958
1010
|
'border-t-w': () => {
|
|
959
1011
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
960
|
-
return `border-top-width: ${cssValue}
|
|
1012
|
+
return `border-top-width: ${cssValue};`;
|
|
961
1013
|
},
|
|
962
1014
|
'border-b-w': () => {
|
|
963
1015
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
964
|
-
return `border-bottom-width: ${cssValue}
|
|
1016
|
+
return `border-bottom-width: ${cssValue};`;
|
|
965
1017
|
},
|
|
966
1018
|
'border-l-w': () => {
|
|
967
1019
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
968
|
-
return `border-left-width: ${cssValue}
|
|
1020
|
+
return `border-left-width: ${cssValue};`;
|
|
969
1021
|
},
|
|
970
1022
|
'border-r-w': () => {
|
|
971
1023
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
972
|
-
return `border-right-width: ${cssValue}
|
|
1024
|
+
return `border-right-width: ${cssValue};`;
|
|
973
1025
|
},
|
|
974
1026
|
'border-x-w': () => {
|
|
975
1027
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
976
|
-
return `border-left-width: ${cssValue}; border-right-width: ${cssValue}
|
|
1028
|
+
return `border-left-width: ${cssValue}; border-right-width: ${cssValue};`;
|
|
977
1029
|
},
|
|
978
1030
|
'border-y-w': () => {
|
|
979
1031
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
980
|
-
return `border-top-width: ${cssValue}; border-bottom-width: ${cssValue}
|
|
1032
|
+
return `border-top-width: ${cssValue}; border-bottom-width: ${cssValue};`;
|
|
981
1033
|
},
|
|
982
1034
|
|
|
983
1035
|
// Border style
|
|
@@ -990,6 +1042,40 @@ function generateVisualRule(token, config) {
|
|
|
990
1042
|
return `border-radius: var(--r-${value});`;
|
|
991
1043
|
},
|
|
992
1044
|
|
|
1045
|
+
// Directional border radius
|
|
1046
|
+
'rounded-t': () => {
|
|
1047
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1048
|
+
return `border-top-left-radius: ${cssValue}; border-top-right-radius: ${cssValue};`;
|
|
1049
|
+
},
|
|
1050
|
+
'rounded-b': () => {
|
|
1051
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1052
|
+
return `border-bottom-left-radius: ${cssValue}; border-bottom-right-radius: ${cssValue};`;
|
|
1053
|
+
},
|
|
1054
|
+
'rounded-l': () => {
|
|
1055
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1056
|
+
return `border-top-left-radius: ${cssValue}; border-bottom-left-radius: ${cssValue};`;
|
|
1057
|
+
},
|
|
1058
|
+
'rounded-r': () => {
|
|
1059
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1060
|
+
return `border-top-right-radius: ${cssValue}; border-bottom-right-radius: ${cssValue};`;
|
|
1061
|
+
},
|
|
1062
|
+
'rounded-tl': () => {
|
|
1063
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1064
|
+
return `border-top-left-radius: ${cssValue};`;
|
|
1065
|
+
},
|
|
1066
|
+
'rounded-tr': () => {
|
|
1067
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1068
|
+
return `border-top-right-radius: ${cssValue};`;
|
|
1069
|
+
},
|
|
1070
|
+
'rounded-bl': () => {
|
|
1071
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1072
|
+
return `border-bottom-left-radius: ${cssValue};`;
|
|
1073
|
+
},
|
|
1074
|
+
'rounded-br': () => {
|
|
1075
|
+
const cssValue = isArbitrary ? value : `var(--r-${value})`;
|
|
1076
|
+
return `border-bottom-right-radius: ${cssValue};`;
|
|
1077
|
+
},
|
|
1078
|
+
|
|
993
1079
|
// =====================
|
|
994
1080
|
// DIVIDE UTILITIES
|
|
995
1081
|
// =====================
|
|
@@ -1007,6 +1093,7 @@ function generateVisualRule(token, config) {
|
|
|
1007
1093
|
return '--ss-divide-x-reverse: 1;';
|
|
1008
1094
|
}
|
|
1009
1095
|
const cssValue = resolveColorValue(value, isArbitrary);
|
|
1096
|
+
// Apply to both to ensure color is inherited, width will control visibility
|
|
1010
1097
|
return `border-left-color: ${cssValue}; border-right-color: ${cssValue}; border-left-style: solid; border-right-style: solid;`;
|
|
1011
1098
|
},
|
|
1012
1099
|
'divide-y': () => {
|
|
@@ -1015,23 +1102,24 @@ function generateVisualRule(token, config) {
|
|
|
1015
1102
|
return '--ss-divide-y-reverse: 1;';
|
|
1016
1103
|
}
|
|
1017
1104
|
const cssValue = resolveColorValue(value, isArbitrary);
|
|
1105
|
+
// Apply to both to ensure color is inherited, width will control visibility
|
|
1018
1106
|
return `border-top-color: ${cssValue}; border-bottom-color: ${cssValue}; border-top-style: solid; border-bottom-style: solid;`;
|
|
1019
1107
|
},
|
|
1020
1108
|
|
|
1021
1109
|
// Divide width - all sides
|
|
1022
1110
|
'divide-w': () => {
|
|
1023
1111
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
1024
|
-
return `border-width: ${cssValue}; border-
|
|
1112
|
+
return `border-top-width: calc(${cssValue} * (1 - var(--ss-divide-y-reverse))); border-bottom-width: calc(${cssValue} * var(--ss-divide-y-reverse)); border-left-width: calc(${cssValue} * (1 - var(--ss-divide-x-reverse))); border-right-width: calc(${cssValue} * var(--ss-divide-x-reverse));`;
|
|
1025
1113
|
},
|
|
1026
1114
|
|
|
1027
1115
|
// Divide width - directional
|
|
1028
1116
|
'divide-x-w': () => {
|
|
1029
1117
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
1030
|
-
return `border-
|
|
1118
|
+
return `border-right-width: calc(${cssValue} * var(--ss-divide-x-reverse)); border-left-width: calc(${cssValue} * (1 - var(--ss-divide-x-reverse)));`;
|
|
1031
1119
|
},
|
|
1032
1120
|
'divide-y-w': () => {
|
|
1033
1121
|
const cssValue = isArbitrary ? value : `var(--s-${value})`;
|
|
1034
|
-
return `border-
|
|
1122
|
+
return `border-bottom-width: calc(${cssValue} * var(--ss-divide-y-reverse)); border-top-width: calc(${cssValue} * (1 - var(--ss-divide-y-reverse)));`;
|
|
1035
1123
|
},
|
|
1036
1124
|
|
|
1037
1125
|
// Divide style
|
|
@@ -1047,6 +1135,9 @@ function generateVisualRule(token, config) {
|
|
|
1047
1135
|
|
|
1048
1136
|
// Outline Color
|
|
1049
1137
|
'outline': () => {
|
|
1138
|
+
if (value === 'none') {
|
|
1139
|
+
return 'outline: none;';
|
|
1140
|
+
}
|
|
1050
1141
|
const cssValue = resolveColorValue(value, isArbitrary);
|
|
1051
1142
|
return `outline-color: ${cssValue};`;
|
|
1052
1143
|
},
|
|
@@ -1102,9 +1193,14 @@ function generateVisualRule(token, config) {
|
|
|
1102
1193
|
|
|
1103
1194
|
const width = isArbitrary ? value : (ringPresets[value] || (parseInt(value) ? `${value}px` : `var(--s-${value})`));
|
|
1104
1195
|
|
|
1105
|
-
//
|
|
1196
|
+
// Set both the variable and the box-shadow that uses it
|
|
1106
1197
|
// This allows ring:[size] to work on its own or with ring-color:[color]
|
|
1107
|
-
return `--ss-ring-width: ${width}; box-shadow: var(--
|
|
1198
|
+
return `--ss-ring-width: ${width}; box-shadow: var(--ring-inset) 0 0 0 calc(var(--ss-ring-width) + var(--ss-ring-offset-width, 0px)) var(--ss-ring-color);`;
|
|
1199
|
+
},
|
|
1200
|
+
|
|
1201
|
+
// Ring Inset
|
|
1202
|
+
'ring-inset': () => {
|
|
1203
|
+
return '--ring-inset: inset;';
|
|
1108
1204
|
},
|
|
1109
1205
|
|
|
1110
1206
|
// Box shadow
|
|
@@ -1980,6 +2076,32 @@ function generateVisualRule(token, config) {
|
|
|
1980
2076
|
return generator ? generator() : '';
|
|
1981
2077
|
}
|
|
1982
2078
|
|
|
2079
|
+
/**
|
|
2080
|
+
* Validate a CSS rule declaration
|
|
2081
|
+
* @param {string} declaration - CSS declaration (e.g., "property: value;")
|
|
2082
|
+
* @returns {boolean} - True if valid
|
|
2083
|
+
*/
|
|
2084
|
+
function isValidCSSRule(declaration) {
|
|
2085
|
+
if (!declaration || typeof declaration !== 'string') {
|
|
2086
|
+
return false;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
declaration = declaration.trim();
|
|
2090
|
+
if (!declaration) return false;
|
|
2091
|
+
|
|
2092
|
+
if (!declaration.endsWith(';')) return false;
|
|
2093
|
+
|
|
2094
|
+
const parts = declaration.substring(0, declaration.length - 1).split(':');
|
|
2095
|
+
if (parts.length < 2) return false;
|
|
2096
|
+
|
|
2097
|
+
const property = parts[0].trim();
|
|
2098
|
+
const value = parts.slice(1).join(':').trim();
|
|
2099
|
+
|
|
2100
|
+
if (!property || !value) return false;
|
|
2101
|
+
|
|
2102
|
+
return true;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
1983
2105
|
/**
|
|
1984
2106
|
* Generate a single CSS rule from a token
|
|
1985
2107
|
* @param {Object} token - Token object
|
|
@@ -1987,98 +2109,141 @@ function generateVisualRule(token, config) {
|
|
|
1987
2109
|
* @param {boolean} skipDarkWrapper - If true, don't add dark mode wrapper (used when generating inside dark block)
|
|
1988
2110
|
*/
|
|
1989
2111
|
export function generateRule(token, config, skipDarkWrapper = false, interactIds = new Set()) {
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2112
|
+
try {
|
|
2113
|
+
if (!token || typeof token !== 'object') {
|
|
2114
|
+
console.warn('[SenangStart] Invalid token object:', token);
|
|
2115
|
+
return '';
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
const { raw, attrType, breakpoint, state } = token;
|
|
2119
|
+
|
|
2120
|
+
if (!attrType || typeof attrType !== 'string') {
|
|
2121
|
+
console.warn('[SenangStart] Invalid token attrType:', attrType);
|
|
2122
|
+
return '';
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
if (!raw || typeof raw !== 'string') {
|
|
2126
|
+
console.warn('[SenangStart] Invalid token raw:', raw);
|
|
2127
|
+
return '';
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
let cssDeclaration = '';
|
|
2131
|
+
|
|
2132
|
+
switch (attrType) {
|
|
2133
|
+
case 'layout':
|
|
2134
|
+
try {
|
|
2135
|
+
cssDeclaration = generateLayoutRule(token, config);
|
|
2136
|
+
} catch (e) {
|
|
2137
|
+
console.warn(`[SenangStart] Error generating layout rule for "${raw}": ${e.message}`);
|
|
2138
|
+
return '';
|
|
2139
|
+
}
|
|
2140
|
+
break;
|
|
2141
|
+
case 'space':
|
|
2142
|
+
try {
|
|
2143
|
+
cssDeclaration = generateSpaceRule(token, config);
|
|
2144
|
+
} catch (e) {
|
|
2145
|
+
console.warn(`[SenangStart] Error generating space rule for "${raw}": ${e.message}`);
|
|
2146
|
+
return '';
|
|
2147
|
+
}
|
|
2148
|
+
break;
|
|
2149
|
+
case 'visual':
|
|
2150
|
+
try {
|
|
2151
|
+
cssDeclaration = generateVisualRule(token, config);
|
|
2152
|
+
} catch (e) {
|
|
2153
|
+
console.warn(`[SenangStart] Error generating visual rule for "${raw}": ${e.message}`);
|
|
2154
|
+
return '';
|
|
2155
|
+
}
|
|
2156
|
+
break;
|
|
2157
|
+
default:
|
|
2158
|
+
console.warn(`[SenangStart] Unknown attrType: ${attrType}`);
|
|
2159
|
+
return '';
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
if (!cssDeclaration) return '';
|
|
2163
|
+
|
|
2164
|
+
if (!isValidCSSRule(cssDeclaration)) {
|
|
2165
|
+
console.warn(`[SenangStart] Invalid CSS rule generated for "${raw}": ${cssDeclaration}`);
|
|
2166
|
+
return '';
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Check if this is a divide utility (needs special selector)
|
|
2170
|
+
const isDivide = raw && raw.startsWith('divide');
|
|
2171
|
+
|
|
2172
|
+
// Build selector
|
|
2173
|
+
let selector = '';
|
|
2174
|
+
|
|
2023
2175
|
if (isDivide) {
|
|
2024
|
-
//
|
|
2025
|
-
|
|
2026
|
-
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden]):${state}`;
|
|
2176
|
+
// Divide utilities use special child selector pattern
|
|
2177
|
+
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])`;
|
|
2027
2178
|
} else {
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2179
|
+
selector = `[${attrType}~="${raw}"]`;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// Add state pseudo-class (but not for 'dark' - it's handled separately)
|
|
2183
|
+
if (state && state !== 'dark') {
|
|
2184
|
+
if (isDivide) {
|
|
2185
|
+
// For divide utilities, add state to the element after tilde
|
|
2186
|
+
// Divide utilities don't support group/peer states yet to avoid complexity
|
|
2187
|
+
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden]):${state}`;
|
|
2188
|
+
} else {
|
|
2189
|
+
// Helper to map state to CSS selector
|
|
2190
|
+
const getStateSelector = (s) => {
|
|
2191
|
+
const map = {
|
|
2192
|
+
'expanded': '[aria-expanded="true"]',
|
|
2193
|
+
'selected': '[aria-selected="true"]',
|
|
2194
|
+
'disabled': ':disabled'
|
|
2195
|
+
};
|
|
2196
|
+
return map[s] || `:${s}`;
|
|
2034
2197
|
};
|
|
2035
|
-
return map[s] || `:${s}`;
|
|
2036
|
-
};
|
|
2037
2198
|
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2199
|
+
const selectors = [];
|
|
2200
|
+
|
|
2201
|
+
// 1. Standard State Selector
|
|
2202
|
+
selectors.push(`${selector}${getStateSelector(state)}`);
|
|
2203
|
+
|
|
2204
|
+
// 2. Group & Peer State Selectors
|
|
2205
|
+
// Only for supported triggers
|
|
2206
|
+
const groupTriggers = {
|
|
2207
|
+
'hover': 'hoverable',
|
|
2208
|
+
'focus': 'focusable',
|
|
2209
|
+
'focus-visible': 'focusable',
|
|
2210
|
+
'active': 'pressable',
|
|
2211
|
+
'expanded': 'expandable',
|
|
2212
|
+
'selected': 'selectable'
|
|
2213
|
+
};
|
|
2214
|
+
|
|
2215
|
+
if (groupTriggers[state]) {
|
|
2216
|
+
const parentAttr = groupTriggers[state];
|
|
2217
|
+
// For focus, we trigger on focus-within of the container
|
|
2218
|
+
let triggerState = state;
|
|
2219
|
+
if (state === 'focus' || state === 'focus-visible') triggerState = 'focus-within';
|
|
2220
|
+
|
|
2221
|
+
const triggerSelector = getStateSelector(triggerState);
|
|
2222
|
+
|
|
2223
|
+
// Group Selector
|
|
2224
|
+
// [layout~="hoverable"]:not([layout~="disabled"]):hover [visual~="..."]
|
|
2225
|
+
const groupSelector = `[layout~="${parentAttr}"]:not([layout~="disabled"])${triggerSelector} ${selector}`;
|
|
2226
|
+
selectors.push(groupSelector);
|
|
2227
|
+
|
|
2228
|
+
// Peer Selectors
|
|
2229
|
+
// [interact~="id"]:not([layout~="disabled"]):hover ~ [listens~="id"][visual~="..."]
|
|
2230
|
+
if (interactIds && interactIds.size > 0) {
|
|
2231
|
+
for (const id of interactIds) {
|
|
2232
|
+
const peerSelector = `[interact~="${id}"]:not([layout~="disabled"])${triggerSelector} ~ [listens~="${id}"]${selector}`;
|
|
2233
|
+
selectors.push(peerSelector);
|
|
2234
|
+
}
|
|
2073
2235
|
}
|
|
2074
2236
|
}
|
|
2237
|
+
|
|
2238
|
+
selector = selectors.join(',\n');
|
|
2075
2239
|
}
|
|
2076
|
-
|
|
2077
|
-
selector = selectors.join(',\n');
|
|
2078
2240
|
}
|
|
2241
|
+
|
|
2242
|
+
return `${selector} { ${cssDeclaration} }\n`;
|
|
2243
|
+
} catch (e) {
|
|
2244
|
+
console.warn(`[SenangStart] Error in generateRule: ${e.message}`);
|
|
2245
|
+
return '';
|
|
2079
2246
|
}
|
|
2080
|
-
|
|
2081
|
-
return `${selector} { ${cssDeclaration} }\n`;
|
|
2082
2247
|
}
|
|
2083
2248
|
|
|
2084
2249
|
/**
|
|
@@ -2103,24 +2268,48 @@ function getDarkModeSelector(config) {
|
|
|
2103
2268
|
}
|
|
2104
2269
|
|
|
2105
2270
|
/**
|
|
2106
|
-
* Generate
|
|
2271
|
+
* Generate CSS from tokens with detailed error reporting
|
|
2272
|
+
* Each token is processed in isolation - one failure doesn't crash the build
|
|
2107
2273
|
* @param {Array} tokens - Array of token objects
|
|
2108
2274
|
* @param {Object} config - Configuration object
|
|
2109
|
-
* @returns {
|
|
2275
|
+
* @returns {Object} - { css: string, errors: Array<{type, token, message}> }
|
|
2110
2276
|
*/
|
|
2111
|
-
export function
|
|
2112
|
-
|
|
2277
|
+
export function generateCSSWithErrors(tokens, config) {
|
|
2278
|
+
const errors = [];
|
|
2279
|
+
try {
|
|
2280
|
+
let css = '';
|
|
2113
2281
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2282
|
+
// Validate inputs
|
|
2283
|
+
if (!config || typeof config !== 'object') {
|
|
2284
|
+
errors.push({ type: 'config', message: 'Invalid config provided' });
|
|
2285
|
+
return { css: '', errors };
|
|
2286
|
+
}
|
|
2116
2287
|
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2288
|
+
if (!Array.isArray(tokens)) {
|
|
2289
|
+
errors.push({ type: 'tokens', message: 'Invalid tokens provided' });
|
|
2290
|
+
return { css: '', errors };
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
// Add CSS variables
|
|
2294
|
+
try {
|
|
2295
|
+
css += generateCSSVariables(config);
|
|
2296
|
+
} catch (e) {
|
|
2297
|
+
errors.push({ type: 'variables', message: e.message });
|
|
2298
|
+
console.warn(`[SenangStart] Error generating CSS variables: ${e.message}`);
|
|
2299
|
+
}
|
|
2121
2300
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2301
|
+
// Add Preflight base styles if enabled (default: true)
|
|
2302
|
+
if (config.preflight !== false) {
|
|
2303
|
+
try {
|
|
2304
|
+
css += generatePreflight(config);
|
|
2305
|
+
} catch (e) {
|
|
2306
|
+
errors.push({ type: 'preflight', message: e.message });
|
|
2307
|
+
console.warn(`[SenangStart] Error generating preflight: ${e.message}`);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
// Add animation keyframes
|
|
2312
|
+
css += `/* SenangStart CSS - Animation Keyframes */
|
|
2124
2313
|
@keyframes spin {
|
|
2125
2314
|
to { transform: rotate(360deg); }
|
|
2126
2315
|
}
|
|
@@ -2138,128 +2327,198 @@ export function generateCSS(tokens, config) {
|
|
|
2138
2327
|
/* SenangStart CSS - Utility Classes */
|
|
2139
2328
|
`;
|
|
2140
2329
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
// Initialize breakpoint collections from config
|
|
2147
|
-
const { screens } = config.theme;
|
|
2148
|
-
for (const bp of Object.keys(screens)) {
|
|
2149
|
-
breakpointTokens[bp] = [];
|
|
2150
|
-
}
|
|
2330
|
+
// Group tokens by breakpoint and dark mode
|
|
2331
|
+
const baseTokens = [];
|
|
2332
|
+
const darkTokens = [];
|
|
2333
|
+
const breakpointTokens = {};
|
|
2151
2334
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
breakpointTokens[token.breakpoint] = [];
|
|
2335
|
+
// Initialize breakpoint collections from config
|
|
2336
|
+
const { screens } = config.theme || {};
|
|
2337
|
+
if (screens && typeof screens === 'object') {
|
|
2338
|
+
for (const bp of Object.keys(screens)) {
|
|
2339
|
+
breakpointTokens[bp] = [];
|
|
2158
2340
|
}
|
|
2159
|
-
breakpointTokens[token.breakpoint].push(token);
|
|
2160
|
-
} else {
|
|
2161
|
-
baseTokens.push(token);
|
|
2162
2341
|
}
|
|
2163
|
-
}
|
|
2164
2342
|
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2343
|
+
for (const token of tokens) {
|
|
2344
|
+
try {
|
|
2345
|
+
if (token && typeof token === 'object') {
|
|
2346
|
+
if (token.state === 'dark') {
|
|
2347
|
+
darkTokens.push(token);
|
|
2348
|
+
} else if (token.breakpoint) {
|
|
2349
|
+
if (!breakpointTokens[token.breakpoint]) {
|
|
2350
|
+
breakpointTokens[token.breakpoint] = [];
|
|
2351
|
+
}
|
|
2352
|
+
breakpointTokens[token.breakpoint].push(token);
|
|
2353
|
+
} else {
|
|
2354
|
+
baseTokens.push(token);
|
|
2355
|
+
}
|
|
2356
|
+
} else {
|
|
2357
|
+
errors.push({ type: 'token_format', token: token, message: 'Token is not an object' });
|
|
2358
|
+
}
|
|
2359
|
+
} catch (e) {
|
|
2360
|
+
errors.push({ type: 'token_processing', token: token?.raw, message: e.message });
|
|
2361
|
+
console.warn(`[SenangStart] Error processing token: ${e.message}`);
|
|
2362
|
+
}
|
|
2170
2363
|
}
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
// Track display properties to handle conflicts like Tailwind
|
|
2174
|
-
// When responsive display property conflicts with base display property on the same element,
|
|
2175
|
-
// we need to add reset rules in the responsive media query
|
|
2176
|
-
const displayProps = ['flex', 'grid', 'inline-flex', 'inline-grid', 'block', 'inline', 'hidden', 'contents'];
|
|
2177
|
-
|
|
2178
|
-
// Map: attrType -> Set of raw values that have display properties in base
|
|
2179
|
-
// e.g., { 'layout' => new Set(['hidden', 'block']) }
|
|
2180
|
-
const baseDisplayTokens = new Map();
|
|
2181
2364
|
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2365
|
+
// Collect interact IDs for Peer selector generation
|
|
2366
|
+
const interactIds = new Set();
|
|
2367
|
+
for (const token of tokens) {
|
|
2368
|
+
try {
|
|
2369
|
+
if (token && token.attrType === 'interact' && token.raw) {
|
|
2370
|
+
interactIds.add(token.raw);
|
|
2371
|
+
}
|
|
2372
|
+
} catch (e) {
|
|
2373
|
+
errors.push({ type: 'interact_collection', token: token?.raw, message: e.message });
|
|
2374
|
+
console.warn(`[SenangStart] Error collecting interact IDs: ${e.message}`);
|
|
2187
2375
|
}
|
|
2188
|
-
baseDisplayTokens.get(token.attrType).add(token.raw);
|
|
2189
2376
|
}
|
|
2190
|
-
}
|
|
2191
2377
|
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
}
|
|
2378
|
+
// Track display properties to handle conflicts like Tailwind
|
|
2379
|
+
const displayProps = ['flex', 'grid', 'inline-flex', 'inline-grid', 'block', 'inline', 'hidden', 'contents'];
|
|
2380
|
+
const baseDisplayTokens = new Map();
|
|
2196
2381
|
|
|
2197
|
-
|
|
2382
|
+
// Find display properties in base tokens
|
|
2383
|
+
for (const token of baseTokens) {
|
|
2384
|
+
try {
|
|
2385
|
+
if (token.attrType && displayProps.includes(token.property)) {
|
|
2386
|
+
if (!baseDisplayTokens.has(token.attrType)) {
|
|
2387
|
+
baseDisplayTokens.set(token.attrType, new Set());
|
|
2388
|
+
}
|
|
2389
|
+
baseDisplayTokens.get(token.attrType).add(token.raw);
|
|
2390
|
+
}
|
|
2391
|
+
} catch (e) {
|
|
2392
|
+
errors.push({ type: 'display_track', token: token?.raw, message: e.message });
|
|
2393
|
+
console.warn(`[SenangStart] Error tracking display properties: ${e.message}`);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2198
2396
|
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2397
|
+
// Generate base rules
|
|
2398
|
+
for (const token of baseTokens) {
|
|
2399
|
+
try {
|
|
2400
|
+
const rule = generateRule(token, config, false, interactIds);
|
|
2401
|
+
if (rule) {
|
|
2402
|
+
css += rule;
|
|
2403
|
+
} else {
|
|
2404
|
+
errors.push({ type: 'rule_generation', token: token.raw, message: 'No rule generated' });
|
|
2405
|
+
}
|
|
2406
|
+
} catch (e) {
|
|
2407
|
+
errors.push({ type: 'rule_generation', token: token.raw, message: e.message });
|
|
2408
|
+
console.warn(`[SenangStart] Error generating base rule: ${e.message}`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2202
2411
|
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2412
|
+
// Generate responsive rules
|
|
2413
|
+
for (const [bp, bpTokens] of Object.entries(breakpointTokens)) {
|
|
2414
|
+
try {
|
|
2415
|
+
if (bpTokens.length > 0) {
|
|
2416
|
+
// Use screen value if defined, otherwise use breakpoint name itself
|
|
2417
|
+
const screenWidth = screens && screens[bp] ? screens[bp] : bp;
|
|
2418
|
+
css += `\n@media (min-width: ${screenWidth}) {\n`;
|
|
2208
2419
|
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2420
|
+
const processedResetSelectors = new Set();
|
|
2421
|
+
for (const bpToken of bpTokens) {
|
|
2422
|
+
try {
|
|
2423
|
+
if (bpToken.attrType && displayProps.includes(bpToken.property)) {
|
|
2424
|
+
if (baseDisplayTokens.has(bpToken.attrType)) {
|
|
2425
|
+
const baseDisplays = baseDisplayTokens.get(bpToken.attrType);
|
|
2426
|
+
if (baseDisplays.size > 0 && !baseDisplays.has(bpToken.raw) && !processedResetSelectors.has(bpToken.raw)) {
|
|
2427
|
+
const selector = `[${bpToken.attrType}~="${bpToken.raw}"]`;
|
|
2428
|
+
css += ` ${selector} { display: revert-layer; }\n`;
|
|
2429
|
+
processedResetSelectors.add(bpToken.raw);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
} catch (e) {
|
|
2434
|
+
errors.push({ type: 'display_reset', token: bpToken.raw, message: e.message });
|
|
2435
|
+
console.warn(`[SenangStart] Error generating display reset: ${e.message}`);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2215
2438
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2439
|
+
for (const token of bpTokens) {
|
|
2440
|
+
try {
|
|
2441
|
+
const rule = generateRule(token, config, false, interactIds);
|
|
2442
|
+
if (rule) {
|
|
2443
|
+
css += ' ' + rule;
|
|
2444
|
+
} else {
|
|
2445
|
+
errors.push({ type: 'responsive_rule', token: token.raw, message: 'No rule generated' });
|
|
2446
|
+
}
|
|
2447
|
+
} catch (e) {
|
|
2448
|
+
errors.push({ type: 'responsive_rule', token: token.raw, message: e.message });
|
|
2449
|
+
console.warn(`[SenangStart] Error generating responsive rule: ${e.message}`);
|
|
2225
2450
|
}
|
|
2226
2451
|
}
|
|
2452
|
+
css += '}\n';
|
|
2227
2453
|
}
|
|
2454
|
+
} catch (e) {
|
|
2455
|
+
errors.push({ type: 'breakpoint_generation', message: `Error generating breakpoint ${bp}: ${e.message}` });
|
|
2456
|
+
console.warn(`[SenangStart] Error generating breakpoint ${bp}: ${e.message}`);
|
|
2228
2457
|
}
|
|
2229
|
-
|
|
2230
|
-
// Generate responsive token rules
|
|
2231
|
-
for (const token of bpTokens) {
|
|
2232
|
-
css += ' ' + generateRule(token, config, false, interactIds);
|
|
2233
|
-
}
|
|
2234
|
-
css += '}\n';
|
|
2235
2458
|
}
|
|
2236
|
-
}
|
|
2237
2459
|
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2460
|
+
// Generate dark mode rules
|
|
2461
|
+
if (darkTokens.length > 0) {
|
|
2462
|
+
try {
|
|
2463
|
+
const darkMode = config.darkMode || 'media';
|
|
2464
|
+
const darkSelector = getDarkModeSelector(config);
|
|
2242
2465
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2466
|
+
if (darkMode === 'media') {
|
|
2467
|
+
css += `\n/* Dark Mode (prefers-color-scheme) */\n`;
|
|
2468
|
+
css += `@media (prefers-color-scheme: dark) {\n`;
|
|
2469
|
+
for (const token of darkTokens) {
|
|
2470
|
+
try {
|
|
2471
|
+
const rule = generateRule(token, config, true, interactIds);
|
|
2472
|
+
if (rule) {
|
|
2473
|
+
css += ' ' + rule;
|
|
2474
|
+
} else {
|
|
2475
|
+
errors.push({ type: 'dark_rule', token: token.raw, message: 'No rule generated' });
|
|
2476
|
+
}
|
|
2477
|
+
} catch (e) {
|
|
2478
|
+
errors.push({ type: 'dark_rule', token: token.raw, message: e.message });
|
|
2479
|
+
console.warn(`[SenangStart] Error generating dark rule (media): ${e.message}`);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
css += '}\n';
|
|
2483
|
+
} else {
|
|
2484
|
+
css += `\n/* Dark Mode (${darkSelector}) */\n`;
|
|
2485
|
+
for (const token of darkTokens) {
|
|
2486
|
+
try {
|
|
2487
|
+
const baseRule = generateRule(token, config, true, interactIds);
|
|
2488
|
+
if (baseRule) {
|
|
2489
|
+
const wrappedRule = baseRule.replace(/^(\[[^\]]+\])/, `${darkSelector} $1`);
|
|
2490
|
+
css += wrappedRule;
|
|
2491
|
+
} else {
|
|
2492
|
+
errors.push({ type: 'dark_rule', token: token.raw, message: 'No rule generated' });
|
|
2493
|
+
}
|
|
2494
|
+
} catch (e) {
|
|
2495
|
+
errors.push({ type: 'dark_rule', token: token.raw, message: e.message });
|
|
2496
|
+
console.warn(`[SenangStart] Error generating dark rule (selector): ${e.message}`);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
} catch (e) {
|
|
2501
|
+
errors.push({ type: 'dark_mode_generation', message: e.message });
|
|
2502
|
+
console.warn(`[SenangStart] Error generating dark mode rules: ${e.message}`);
|
|
2259
2503
|
}
|
|
2260
2504
|
}
|
|
2505
|
+
|
|
2506
|
+
return { css, errors };
|
|
2507
|
+
} catch (e) {
|
|
2508
|
+
errors.push({ type: 'fatal', message: e.message });
|
|
2509
|
+
console.error(`[SenangStart] Fatal error in generateCSSWithErrors: ${e.message}`);
|
|
2510
|
+
return { css: '', errors };
|
|
2261
2511
|
}
|
|
2512
|
+
}
|
|
2262
2513
|
|
|
2514
|
+
/**
|
|
2515
|
+
* Generate CSS from tokens (Backward compatible wrapper)
|
|
2516
|
+
* @param {Array} tokens - Array of token objects
|
|
2517
|
+
* @param {Object} config - Configuration object
|
|
2518
|
+
* @returns {string} - Generated CSS
|
|
2519
|
+
*/
|
|
2520
|
+
export function generateCSS(tokens, config) {
|
|
2521
|
+
const { css } = generateCSSWithErrors(tokens, config);
|
|
2263
2522
|
return css;
|
|
2264
2523
|
}
|
|
2265
2524
|
|