@rakeyshgidwani/roger-ui-bank-theme-harvey 0.2.51 → 0.3.0
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/CHANGELOG.md +1 -1
- package/dist/components/ui/button.d.ts +3 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.esm.js +3 -2
- package/dist/components/ui/button.js +3 -2
- package/dist/components/ui/layout/container.d.ts +57 -0
- package/dist/components/ui/layout/container.d.ts.map +1 -0
- package/dist/components/ui/layout/container.esm.js +173 -0
- package/dist/components/ui/layout/container.js +173 -0
- package/dist/components/ui/layout/index.d.ts +9 -0
- package/dist/components/ui/layout/index.d.ts.map +1 -0
- package/dist/components/ui/layout/index.esm.js +6 -0
- package/dist/components/ui/layout/index.js +6 -0
- package/dist/components/ui/layout/responsive-grid.d.ts +93 -0
- package/dist/components/ui/layout/responsive-grid.d.ts.map +1 -0
- package/dist/components/ui/layout/responsive-grid.esm.js +124 -0
- package/dist/components/ui/layout/responsive-grid.js +124 -0
- package/dist/components/ui/navigation/index.d.ts +2 -1
- package/dist/components/ui/navigation/index.d.ts.map +1 -1
- package/dist/components/ui/navigation/index.esm.js +1 -0
- package/dist/components/ui/navigation/index.js +1 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts +37 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts.map +1 -0
- package/dist/components/ui/navigation/progressive-navigation.esm.js +145 -0
- package/dist/components/ui/navigation/progressive-navigation.js +145 -0
- package/dist/components/ui/navigation/types.d.ts +21 -0
- package/dist/components/ui/navigation/types.d.ts.map +1 -1
- package/dist/components/ui/theme-toggle.esm.js +1 -1
- package/dist/components/ui/theme-toggle.js +1 -1
- package/dist/hooks/use-adaptive-layout.d.ts +2 -1
- package/dist/hooks/use-adaptive-layout.d.ts.map +1 -1
- package/dist/hooks/use-adaptive-layout.esm.js +13 -8
- package/dist/hooks/use-adaptive-layout.js +13 -8
- package/dist/hooks/use-device.d.ts +3 -1
- package/dist/hooks/use-device.d.ts.map +1 -1
- package/dist/hooks/use-device.esm.js +14 -7
- package/dist/hooks/use-device.js +14 -7
- package/dist/index.d.ts +19 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +9 -4
- package/dist/index.js +9 -4
- package/dist/plugins/css-purge-optimizer.d.ts +25 -0
- package/dist/plugins/css-purge-optimizer.d.ts.map +1 -0
- package/dist/plugins/css-purge-optimizer.esm.js +414 -0
- package/dist/plugins/css-purge-optimizer.js +414 -0
- package/dist/plugins/performance-monitor.d.ts +29 -0
- package/dist/plugins/performance-monitor.d.ts.map +1 -0
- package/dist/plugins/performance-monitor.esm.js +221 -0
- package/dist/plugins/performance-monitor.js +221 -0
- package/dist/plugins/progressive-css-loader.d.ts +21 -0
- package/dist/plugins/progressive-css-loader.d.ts.map +1 -0
- package/dist/plugins/progressive-css-loader.esm.js +227 -0
- package/dist/plugins/progressive-css-loader.js +227 -0
- package/dist/plugins/theme-css-generator.d.ts.map +1 -1
- package/dist/plugins/theme-css-generator.esm.js +19 -6
- package/dist/plugins/theme-css-generator.js +19 -6
- package/dist/styles.css +1025 -110
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.esm.js +4 -1
- package/dist/theme.js +4 -1
- package/dist/themes/phase1-constants.d.ts +23 -0
- package/dist/themes/phase1-constants.d.ts.map +1 -0
- package/dist/themes/phase1-constants.esm.js +180 -0
- package/dist/themes/phase1-constants.js +180 -0
- package/dist/themes/themes/default.d.ts.map +1 -1
- package/dist/themes/themes/default.esm.js +4 -1
- package/dist/themes/themes/default.js +4 -1
- package/dist/themes/themes/harvey.d.ts.map +1 -1
- package/dist/themes/themes/harvey.esm.js +4 -1
- package/dist/themes/themes/harvey.js +4 -1
- package/dist/themes/types.d.ts +62 -0
- package/dist/themes/types.d.ts.map +1 -1
- package/dist/themes/validation.d.ts +17 -0
- package/dist/themes/validation.d.ts.map +1 -1
- package/dist/themes/validation.esm.js +218 -0
- package/dist/themes/validation.js +218 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/progressive-css-injector.d.ts +80 -0
- package/dist/utils/progressive-css-injector.d.ts.map +1 -0
- package/dist/utils/progressive-css-injector.esm.js +217 -0
- package/dist/utils/progressive-css-injector.js +217 -0
- package/package.json +1 -1
- package/src/components/ui/button.tsx +9 -6
- package/src/components/ui/layout/container.tsx +312 -0
- package/src/components/ui/layout/index.ts +10 -0
- package/src/components/ui/layout/responsive-grid.tsx +286 -0
- package/src/components/ui/navigation/index.ts +2 -0
- package/src/components/ui/navigation/progressive-navigation.tsx +453 -0
- package/src/components/ui/navigation/types.ts +41 -0
- package/src/components/ui/theme-toggle.tsx +4 -4
- package/src/hooks/use-adaptive-layout.ts +13 -9
- package/src/hooks/use-device.tsx +17 -10
- package/src/index.ts +19 -4
- package/src/plugins/css-purge-optimizer.ts +491 -0
- package/src/plugins/performance-monitor.ts +292 -0
- package/src/plugins/progressive-css-loader.ts +269 -0
- package/src/plugins/theme-css-generator.ts +22 -6
- package/src/styles/components/base/badge.css +2 -2
- package/src/styles/components/base/button.css +238 -35
- package/src/styles/components/base/card.css +2 -2
- package/src/styles/components/base/checkbox.css +3 -3
- package/src/styles/components/base/label.css +3 -3
- package/src/styles/components/feedback/skeleton.css +1 -1
- package/src/styles/components/feedback/toast.css +1 -1
- package/src/styles/components/index.css +3 -0
- package/src/styles/components/layout/container.css +466 -0
- package/src/styles/components/layout/index.css +5 -0
- package/src/styles/components/layout/responsive-grid.css +422 -0
- package/src/styles/components/navigation/breadcrumb.css +1 -1
- package/src/styles/components/navigation/index.css +1 -0
- package/src/styles/components/navigation/menu.css +2 -2
- package/src/styles/components/navigation/pagination.css +4 -4
- package/src/styles/components/navigation/progressive-navigation.css +633 -0
- package/src/styles/components/navigation/sidebar.css +4 -4
- package/src/styles/components/navigation/stepper.css +2 -2
- package/src/styles/components/navigation/tabs.css +1 -1
- package/src/styles/progressive.css +17 -0
- package/src/styles/themes/harvey.css +103 -19
- package/src/styles/utilities/semantic-input-system.css +7 -13
- package/src/theme.ts +5 -1
- package/src/themes/phase1-constants.ts +189 -0
- package/src/themes/themes/default.ts +5 -1
- package/src/themes/themes/harvey.ts +5 -1
- package/src/themes/types.ts +77 -1
- package/src/themes/validation.ts +249 -0
- package/src/types.ts +77 -1
- package/src/utils/progressive-css-injector.ts +254 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive CSS Loader Plugin
|
|
3
|
+
* Implements Phase 3 Performance Optimization: Progressive Enhancement Loading
|
|
4
|
+
*
|
|
5
|
+
* Separates CSS into:
|
|
6
|
+
* - Critical: xs-md breakpoints (mobile-tablet) - loaded immediately
|
|
7
|
+
* - Progressive: lg-3xl breakpoints (desktop+) - loaded on demand
|
|
8
|
+
*/
|
|
9
|
+
import postcss from 'postcss';
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
critical: ['xs', 'sm', 'md'],
|
|
12
|
+
progressive: ['lg', 'xl', '2xl', '3xl'],
|
|
13
|
+
progressivePath: 'progressive.css'
|
|
14
|
+
};
|
|
15
|
+
export default function progressiveCSSLoader(config = {}) {
|
|
16
|
+
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
17
|
+
return {
|
|
18
|
+
name: 'progressive-css-loader',
|
|
19
|
+
apply: 'build',
|
|
20
|
+
generateBundle(_options, bundle) {
|
|
21
|
+
// Find CSS assets in bundle
|
|
22
|
+
const cssAssets = Object.keys(bundle).filter(key => key.endsWith('.css'));
|
|
23
|
+
cssAssets.forEach((assetKey, index) => {
|
|
24
|
+
const asset = bundle[assetKey];
|
|
25
|
+
if (asset.type === 'asset' && typeof asset.source === 'string') {
|
|
26
|
+
const originalSource = asset.source;
|
|
27
|
+
// Safely separate CSS into critical and progressive parts
|
|
28
|
+
const { critical, progressive } = separateCSS(originalSource, finalConfig);
|
|
29
|
+
const originalSize = Math.round(originalSource.length / 1024);
|
|
30
|
+
const criticalSize = Math.round(critical.length / 1024);
|
|
31
|
+
const progressiveSize = Math.round(progressive.length / 1024);
|
|
32
|
+
console.log(` Split ${assetKey}: ${originalSize}KB -> ${criticalSize}KB critical + ${progressiveSize}KB progressive`);
|
|
33
|
+
// Update the main CSS asset to contain only critical CSS
|
|
34
|
+
asset.source = critical;
|
|
35
|
+
// Create separate progressive CSS file with unique naming for desktop enhancement
|
|
36
|
+
if (progressive.trim()) {
|
|
37
|
+
const progressiveFileName = index === 0
|
|
38
|
+
? finalConfig.progressivePath
|
|
39
|
+
: finalConfig.progressivePath.replace('.css', `-${index}.css`);
|
|
40
|
+
this.emitFile({
|
|
41
|
+
type: 'asset',
|
|
42
|
+
fileName: progressiveFileName,
|
|
43
|
+
source: progressive
|
|
44
|
+
});
|
|
45
|
+
console.log(` Created ${progressiveFileName} for desktop enhancement (${progressiveSize}KB)`);
|
|
46
|
+
}
|
|
47
|
+
// Skip creating explicit critical CSS file since main bundle already contains critical styles
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* CSS Variable to pixel value mapping based on design system
|
|
55
|
+
* This provides the authoritative mapping for breakpoint classification
|
|
56
|
+
*/
|
|
57
|
+
const BREAKPOINT_VALUES = {
|
|
58
|
+
'xs': 475,
|
|
59
|
+
'sm': 640,
|
|
60
|
+
'mobile': 640,
|
|
61
|
+
'md': 768,
|
|
62
|
+
'tablet': 768,
|
|
63
|
+
'lg': 1024,
|
|
64
|
+
'desktop': 1024,
|
|
65
|
+
'xl': 1280,
|
|
66
|
+
'wide': 1280,
|
|
67
|
+
'2xl': 1536,
|
|
68
|
+
'ultra': 1536,
|
|
69
|
+
'3xl': 1920
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Desktop breakpoint threshold - anything >= 1024px is considered desktop
|
|
73
|
+
*/
|
|
74
|
+
const DESKTOP_THRESHOLD = 1024;
|
|
75
|
+
/**
|
|
76
|
+
* Determines if a media query represents a desktop breakpoint
|
|
77
|
+
* Uses CSS variable resolution to properly classify breakpoints
|
|
78
|
+
*/
|
|
79
|
+
function isDesktopMediaQuery(mediaQuery) {
|
|
80
|
+
// Skip max-width queries - they should always stay in critical CSS
|
|
81
|
+
if (mediaQuery.includes('max-width')) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// Check for CSS variable-based breakpoints
|
|
85
|
+
const variableMatch = mediaQuery.match(/var\(--cs-breakpoints-([^)]+)\)/);
|
|
86
|
+
if (variableMatch) {
|
|
87
|
+
const breakpointName = variableMatch[1];
|
|
88
|
+
const pixelValue = BREAKPOINT_VALUES[breakpointName];
|
|
89
|
+
if (pixelValue !== undefined) {
|
|
90
|
+
return pixelValue >= DESKTOP_THRESHOLD;
|
|
91
|
+
}
|
|
92
|
+
// If we don't recognize the variable, log it and assume it's critical for safety
|
|
93
|
+
console.warn(`Unknown breakpoint variable: --cs-breakpoints-${breakpointName}`);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
// Check for direct pixel values
|
|
97
|
+
const pixelMatch = mediaQuery.match(/min-width:\s*(\d+)px/);
|
|
98
|
+
if (pixelMatch) {
|
|
99
|
+
const pixelValue = parseInt(pixelMatch[1], 10);
|
|
100
|
+
return pixelValue >= DESKTOP_THRESHOLD;
|
|
101
|
+
}
|
|
102
|
+
// If we can't classify it, keep it in critical CSS for safety
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Safely separates CSS content into critical and progressive sections
|
|
107
|
+
* Critical: < 1024px breakpoints (mobile/tablet) + base styles
|
|
108
|
+
* Progressive: >= 1024px breakpoints (desktop+) only
|
|
109
|
+
*/
|
|
110
|
+
function separateCSS(cssContent, _config) {
|
|
111
|
+
try {
|
|
112
|
+
const desktopRules = [];
|
|
113
|
+
// Parse CSS using PostCSS
|
|
114
|
+
const root = postcss.parse(cssContent);
|
|
115
|
+
// Walk through all rules and find desktop media queries
|
|
116
|
+
root.walkAtRules('media', (rule) => {
|
|
117
|
+
const mediaQuery = rule.params;
|
|
118
|
+
const shouldExtract = isDesktopMediaQuery(mediaQuery);
|
|
119
|
+
if (shouldExtract) {
|
|
120
|
+
// Convert the rule back to CSS string and collect it
|
|
121
|
+
desktopRules.push(rule.toString());
|
|
122
|
+
// Remove the rule from the original AST
|
|
123
|
+
rule.remove();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// Generate the critical CSS (without desktop rules)
|
|
127
|
+
const criticalCSS = root.toString();
|
|
128
|
+
const progressiveCSS = desktopRules.join('\n');
|
|
129
|
+
// Validate CSS syntax by checking for balanced braces
|
|
130
|
+
const criticalOpenBraces = (criticalCSS.match(/\{/g) || []).length;
|
|
131
|
+
const criticalCloseBraces = (criticalCSS.match(/\}/g) || []).length;
|
|
132
|
+
const progressiveOpenBraces = (progressiveCSS.match(/\{/g) || []).length;
|
|
133
|
+
const progressiveCloseBraces = (progressiveCSS.match(/\}/g) || []).length;
|
|
134
|
+
if (criticalOpenBraces !== criticalCloseBraces) {
|
|
135
|
+
console.error(`PostCSS extraction created malformed critical CSS: ${criticalOpenBraces} open braces vs ${criticalCloseBraces} close braces`);
|
|
136
|
+
console.log('Falling back to original CSS to avoid syntax errors');
|
|
137
|
+
return {
|
|
138
|
+
critical: cssContent,
|
|
139
|
+
progressive: ''
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (progressiveOpenBraces !== progressiveCloseBraces) {
|
|
143
|
+
console.error(`PostCSS extraction created malformed progressive CSS: ${progressiveOpenBraces} open braces vs ${progressiveCloseBraces} close braces`);
|
|
144
|
+
console.log('Falling back to original CSS to avoid syntax errors');
|
|
145
|
+
return {
|
|
146
|
+
critical: cssContent,
|
|
147
|
+
progressive: ''
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
console.log(`CSS separation: Extracted ${desktopRules.length} desktop-only media queries using PostCSS`);
|
|
151
|
+
// Note: Any remaining CSS syntax warnings are from pre-existing CSS generation issues,
|
|
152
|
+
// not from the progressive CSS extraction process
|
|
153
|
+
return {
|
|
154
|
+
critical: criticalCSS,
|
|
155
|
+
progressive: progressiveCSS
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error('PostCSS separation failed, falling back to original CSS:', error);
|
|
160
|
+
return {
|
|
161
|
+
critical: cssContent,
|
|
162
|
+
progressive: ''
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Creates progressive CSS loading script for runtime
|
|
168
|
+
*/
|
|
169
|
+
export function createProgressiveLoader() {
|
|
170
|
+
return `
|
|
171
|
+
// Progressive CSS Loader - Phase 3 Performance Optimization
|
|
172
|
+
(function() {
|
|
173
|
+
'use strict';
|
|
174
|
+
|
|
175
|
+
const DESKTOP_BREAKPOINT = 1024; // lg breakpoint
|
|
176
|
+
const PROGRESSIVE_CSS_PATH = '/assets/progressive.css';
|
|
177
|
+
|
|
178
|
+
let progressiveCSSLoaded = false;
|
|
179
|
+
|
|
180
|
+
function loadProgressiveCSS() {
|
|
181
|
+
if (progressiveCSSLoaded) return;
|
|
182
|
+
|
|
183
|
+
const link = document.createElement('link');
|
|
184
|
+
link.rel = 'stylesheet';
|
|
185
|
+
link.href = PROGRESSIVE_CSS_PATH;
|
|
186
|
+
link.media = 'screen and (min-width: ' + DESKTOP_BREAKPOINT + 'px)';
|
|
187
|
+
|
|
188
|
+
// Load asynchronously for non-blocking
|
|
189
|
+
link.onload = function() {
|
|
190
|
+
console.log('Progressive CSS loaded for desktop');
|
|
191
|
+
progressiveCSSLoaded = true;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
document.head.appendChild(link);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function checkViewport() {
|
|
198
|
+
if (window.innerWidth >= DESKTOP_BREAKPOINT) {
|
|
199
|
+
loadProgressiveCSS();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Load on resize to desktop
|
|
204
|
+
let resizeTimeout;
|
|
205
|
+
window.addEventListener('resize', function() {
|
|
206
|
+
clearTimeout(resizeTimeout);
|
|
207
|
+
resizeTimeout = setTimeout(checkViewport, 150);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Check initial viewport
|
|
211
|
+
if (document.readyState === 'loading') {
|
|
212
|
+
document.addEventListener('DOMContentLoaded', checkViewport);
|
|
213
|
+
} else {
|
|
214
|
+
checkViewport();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Preload for fast desktop switches
|
|
218
|
+
if (window.innerWidth >= 768) { // tablet+
|
|
219
|
+
const preload = document.createElement('link');
|
|
220
|
+
preload.rel = 'preload';
|
|
221
|
+
preload.href = PROGRESSIVE_CSS_PATH;
|
|
222
|
+
preload.as = 'style';
|
|
223
|
+
document.head.appendChild(preload);
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
`;
|
|
227
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-css-generator.d.ts","sourceRoot":"","sources":["../../src/plugins/theme-css-generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,MAAM,CAAC,OAAO,UAAU,iBAAiB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"theme-css-generator.d.ts","sourceRoot":"","sources":["../../src/plugins/theme-css-generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,MAAM,CAAC,OAAO,UAAU,iBAAiB,IAAI,MAAM,CAuUlD"}
|
|
@@ -89,7 +89,7 @@ export default function themeCSSGenerator() {
|
|
|
89
89
|
return fonts?.[fontKey]?.source?.type === 'google';
|
|
90
90
|
};
|
|
91
91
|
// Recursively generate CSS variables from theme object
|
|
92
|
-
const generateCSSVariables = (obj, path = [], rootTheme) => {
|
|
92
|
+
const generateCSSVariables = (obj, path = [], rootTheme, localVars = new Set()) => {
|
|
93
93
|
let css = '';
|
|
94
94
|
if (typeof obj !== 'object' || obj === null) {
|
|
95
95
|
return css;
|
|
@@ -104,19 +104,31 @@ export default function themeCSSGenerator() {
|
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
// Skip breakpoints as they're handled by generateBreakpointVariables
|
|
108
|
+
if (key === 'breakpoints') {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
107
111
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
108
112
|
// Generate CSS variable for primitive values
|
|
109
113
|
const cssVarName = createCSSVarName(currentPath);
|
|
110
|
-
|
|
114
|
+
// Only prevent duplicates within the same traversal (same CSS rule block)
|
|
115
|
+
if (!localVars.has(cssVarName)) {
|
|
116
|
+
css += ` ${cssVarName}: ${value};\n`;
|
|
117
|
+
localVars.add(cssVarName);
|
|
118
|
+
}
|
|
111
119
|
}
|
|
112
120
|
else if (Array.isArray(value)) {
|
|
113
121
|
// Handle arrays (like font weights, tags)
|
|
114
122
|
const cssVarName = createCSSVarName(currentPath);
|
|
115
|
-
|
|
123
|
+
// Only prevent duplicates within the same traversal (same CSS rule block)
|
|
124
|
+
if (!localVars.has(cssVarName)) {
|
|
125
|
+
css += ` ${cssVarName}: ${valueToString(value)};\n`;
|
|
126
|
+
localVars.add(cssVarName);
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
else if (typeof value === 'object' && value !== null) {
|
|
118
130
|
// Recursively process nested objects
|
|
119
|
-
css += generateCSSVariables(value, currentPath, rootTheme);
|
|
131
|
+
css += generateCSSVariables(value, currentPath, rootTheme, localVars);
|
|
120
132
|
}
|
|
121
133
|
});
|
|
122
134
|
return css;
|
|
@@ -138,8 +150,9 @@ export default function themeCSSGenerator() {
|
|
|
138
150
|
css += ` /* Light Mode Variables */\n`;
|
|
139
151
|
// NEW: Generate breakpoint variables first
|
|
140
152
|
css += generateBreakpointVariables(breakpoints);
|
|
141
|
-
// Generate all other CSS variables
|
|
142
|
-
|
|
153
|
+
// Generate all other CSS variables (each CSS block has its own scope)
|
|
154
|
+
const localVars = new Set();
|
|
155
|
+
css += generateCSSVariables(themeObj, [], themeObj, localVars);
|
|
143
156
|
css += '}\n\n';
|
|
144
157
|
// Generate dark mode variables
|
|
145
158
|
css += `.dark {\n`;
|
|
@@ -89,7 +89,7 @@ export default function themeCSSGenerator() {
|
|
|
89
89
|
return fonts?.[fontKey]?.source?.type === 'google';
|
|
90
90
|
};
|
|
91
91
|
// Recursively generate CSS variables from theme object
|
|
92
|
-
const generateCSSVariables = (obj, path = [], rootTheme) => {
|
|
92
|
+
const generateCSSVariables = (obj, path = [], rootTheme, localVars = new Set()) => {
|
|
93
93
|
let css = '';
|
|
94
94
|
if (typeof obj !== 'object' || obj === null) {
|
|
95
95
|
return css;
|
|
@@ -104,19 +104,31 @@ export default function themeCSSGenerator() {
|
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
// Skip breakpoints as they're handled by generateBreakpointVariables
|
|
108
|
+
if (key === 'breakpoints') {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
107
111
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
108
112
|
// Generate CSS variable for primitive values
|
|
109
113
|
const cssVarName = createCSSVarName(currentPath);
|
|
110
|
-
|
|
114
|
+
// Only prevent duplicates within the same traversal (same CSS rule block)
|
|
115
|
+
if (!localVars.has(cssVarName)) {
|
|
116
|
+
css += ` ${cssVarName}: ${value};\n`;
|
|
117
|
+
localVars.add(cssVarName);
|
|
118
|
+
}
|
|
111
119
|
}
|
|
112
120
|
else if (Array.isArray(value)) {
|
|
113
121
|
// Handle arrays (like font weights, tags)
|
|
114
122
|
const cssVarName = createCSSVarName(currentPath);
|
|
115
|
-
|
|
123
|
+
// Only prevent duplicates within the same traversal (same CSS rule block)
|
|
124
|
+
if (!localVars.has(cssVarName)) {
|
|
125
|
+
css += ` ${cssVarName}: ${valueToString(value)};\n`;
|
|
126
|
+
localVars.add(cssVarName);
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
else if (typeof value === 'object' && value !== null) {
|
|
118
130
|
// Recursively process nested objects
|
|
119
|
-
css += generateCSSVariables(value, currentPath, rootTheme);
|
|
131
|
+
css += generateCSSVariables(value, currentPath, rootTheme, localVars);
|
|
120
132
|
}
|
|
121
133
|
});
|
|
122
134
|
return css;
|
|
@@ -138,8 +150,9 @@ export default function themeCSSGenerator() {
|
|
|
138
150
|
css += ` /* Light Mode Variables */\n`;
|
|
139
151
|
// NEW: Generate breakpoint variables first
|
|
140
152
|
css += generateBreakpointVariables(breakpoints);
|
|
141
|
-
// Generate all other CSS variables
|
|
142
|
-
|
|
153
|
+
// Generate all other CSS variables (each CSS block has its own scope)
|
|
154
|
+
const localVars = new Set();
|
|
155
|
+
css += generateCSSVariables(themeObj, [], themeObj, localVars);
|
|
143
156
|
css += '}\n\n';
|
|
144
157
|
// Generate dark mode variables
|
|
145
158
|
css += `.dark {\n`;
|