@rakeyshgidwani/roger-ui-bank-theme-harvey 0.2.52 → 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.
Files changed (125) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/components/ui/button.d.ts +3 -1
  3. package/dist/components/ui/button.d.ts.map +1 -1
  4. package/dist/components/ui/button.esm.js +3 -2
  5. package/dist/components/ui/button.js +3 -2
  6. package/dist/components/ui/layout/container.d.ts +57 -0
  7. package/dist/components/ui/layout/container.d.ts.map +1 -0
  8. package/dist/components/ui/layout/container.esm.js +173 -0
  9. package/dist/components/ui/layout/container.js +173 -0
  10. package/dist/components/ui/layout/index.d.ts +9 -0
  11. package/dist/components/ui/layout/index.d.ts.map +1 -0
  12. package/dist/components/ui/layout/index.esm.js +6 -0
  13. package/dist/components/ui/layout/index.js +6 -0
  14. package/dist/components/ui/layout/responsive-grid.d.ts +93 -0
  15. package/dist/components/ui/layout/responsive-grid.d.ts.map +1 -0
  16. package/dist/components/ui/layout/responsive-grid.esm.js +124 -0
  17. package/dist/components/ui/layout/responsive-grid.js +124 -0
  18. package/dist/components/ui/navigation/index.d.ts +2 -1
  19. package/dist/components/ui/navigation/index.d.ts.map +1 -1
  20. package/dist/components/ui/navigation/index.esm.js +1 -0
  21. package/dist/components/ui/navigation/index.js +1 -0
  22. package/dist/components/ui/navigation/progressive-navigation.d.ts +37 -0
  23. package/dist/components/ui/navigation/progressive-navigation.d.ts.map +1 -0
  24. package/dist/components/ui/navigation/progressive-navigation.esm.js +145 -0
  25. package/dist/components/ui/navigation/progressive-navigation.js +145 -0
  26. package/dist/components/ui/navigation/types.d.ts +21 -0
  27. package/dist/components/ui/navigation/types.d.ts.map +1 -1
  28. package/dist/hooks/use-adaptive-layout.d.ts +2 -1
  29. package/dist/hooks/use-adaptive-layout.d.ts.map +1 -1
  30. package/dist/hooks/use-adaptive-layout.esm.js +13 -8
  31. package/dist/hooks/use-adaptive-layout.js +13 -8
  32. package/dist/hooks/use-device.d.ts +3 -1
  33. package/dist/hooks/use-device.d.ts.map +1 -1
  34. package/dist/hooks/use-device.esm.js +14 -7
  35. package/dist/hooks/use-device.js +14 -7
  36. package/dist/index.d.ts +19 -4
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.esm.js +9 -4
  39. package/dist/index.js +9 -4
  40. package/dist/plugins/css-purge-optimizer.d.ts +25 -0
  41. package/dist/plugins/css-purge-optimizer.d.ts.map +1 -0
  42. package/dist/plugins/css-purge-optimizer.esm.js +414 -0
  43. package/dist/plugins/css-purge-optimizer.js +414 -0
  44. package/dist/plugins/performance-monitor.d.ts +29 -0
  45. package/dist/plugins/performance-monitor.d.ts.map +1 -0
  46. package/dist/plugins/performance-monitor.esm.js +221 -0
  47. package/dist/plugins/performance-monitor.js +221 -0
  48. package/dist/plugins/progressive-css-loader.d.ts +21 -0
  49. package/dist/plugins/progressive-css-loader.d.ts.map +1 -0
  50. package/dist/plugins/progressive-css-loader.esm.js +227 -0
  51. package/dist/plugins/progressive-css-loader.js +227 -0
  52. package/dist/plugins/theme-css-generator.d.ts.map +1 -1
  53. package/dist/plugins/theme-css-generator.esm.js +19 -6
  54. package/dist/plugins/theme-css-generator.js +19 -6
  55. package/dist/styles.css +1025 -110
  56. package/dist/theme.d.ts.map +1 -1
  57. package/dist/theme.esm.js +4 -1
  58. package/dist/theme.js +4 -1
  59. package/dist/themes/phase1-constants.d.ts +23 -0
  60. package/dist/themes/phase1-constants.d.ts.map +1 -0
  61. package/dist/themes/phase1-constants.esm.js +180 -0
  62. package/dist/themes/phase1-constants.js +180 -0
  63. package/dist/themes/themes/default.d.ts.map +1 -1
  64. package/dist/themes/themes/default.esm.js +4 -1
  65. package/dist/themes/themes/default.js +4 -1
  66. package/dist/themes/themes/harvey.d.ts.map +1 -1
  67. package/dist/themes/themes/harvey.esm.js +4 -1
  68. package/dist/themes/themes/harvey.js +4 -1
  69. package/dist/themes/types.d.ts +62 -0
  70. package/dist/themes/types.d.ts.map +1 -1
  71. package/dist/themes/validation.d.ts +17 -0
  72. package/dist/themes/validation.d.ts.map +1 -1
  73. package/dist/themes/validation.esm.js +218 -0
  74. package/dist/themes/validation.js +218 -0
  75. package/dist/types.d.ts +62 -0
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/utils/progressive-css-injector.d.ts +80 -0
  78. package/dist/utils/progressive-css-injector.d.ts.map +1 -0
  79. package/dist/utils/progressive-css-injector.esm.js +217 -0
  80. package/dist/utils/progressive-css-injector.js +217 -0
  81. package/package.json +1 -1
  82. package/src/components/ui/button.tsx +9 -6
  83. package/src/components/ui/layout/container.tsx +312 -0
  84. package/src/components/ui/layout/index.ts +10 -0
  85. package/src/components/ui/layout/responsive-grid.tsx +286 -0
  86. package/src/components/ui/navigation/index.ts +2 -0
  87. package/src/components/ui/navigation/progressive-navigation.tsx +453 -0
  88. package/src/components/ui/navigation/types.ts +41 -0
  89. package/src/hooks/use-adaptive-layout.ts +13 -9
  90. package/src/hooks/use-device.tsx +17 -10
  91. package/src/index.ts +19 -4
  92. package/src/plugins/css-purge-optimizer.ts +491 -0
  93. package/src/plugins/performance-monitor.ts +292 -0
  94. package/src/plugins/progressive-css-loader.ts +269 -0
  95. package/src/plugins/theme-css-generator.ts +22 -6
  96. package/src/styles/components/base/badge.css +2 -2
  97. package/src/styles/components/base/button.css +238 -35
  98. package/src/styles/components/base/card.css +2 -2
  99. package/src/styles/components/base/checkbox.css +3 -3
  100. package/src/styles/components/base/label.css +3 -3
  101. package/src/styles/components/feedback/skeleton.css +1 -1
  102. package/src/styles/components/feedback/toast.css +1 -1
  103. package/src/styles/components/index.css +3 -0
  104. package/src/styles/components/layout/container.css +466 -0
  105. package/src/styles/components/layout/index.css +5 -0
  106. package/src/styles/components/layout/responsive-grid.css +422 -0
  107. package/src/styles/components/navigation/breadcrumb.css +1 -1
  108. package/src/styles/components/navigation/index.css +1 -0
  109. package/src/styles/components/navigation/menu.css +2 -2
  110. package/src/styles/components/navigation/pagination.css +4 -4
  111. package/src/styles/components/navigation/progressive-navigation.css +633 -0
  112. package/src/styles/components/navigation/sidebar.css +4 -4
  113. package/src/styles/components/navigation/stepper.css +2 -2
  114. package/src/styles/components/navigation/tabs.css +1 -1
  115. package/src/styles/progressive.css +17 -0
  116. package/src/styles/themes/harvey.css +103 -19
  117. package/src/styles/utilities/semantic-input-system.css +7 -13
  118. package/src/theme.ts +5 -1
  119. package/src/themes/phase1-constants.ts +189 -0
  120. package/src/themes/themes/default.ts +5 -1
  121. package/src/themes/themes/harvey.ts +5 -1
  122. package/src/themes/types.ts +77 -1
  123. package/src/themes/validation.ts +249 -0
  124. package/src/types.ts +77 -1
  125. package/src/utils/progressive-css-injector.ts +254 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Performance Monitor Plugin
3
+ * Implements Phase 3 Performance Optimization: Bundle Size Monitoring
4
+ *
5
+ * Tracks:
6
+ * - CSS bundle sizes (critical vs progressive)
7
+ * - JavaScript bundle sizes
8
+ * - Performance metrics and alerts
9
+ * - Loading performance analysis
10
+ */
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ const DEFAULT_CONFIG = {
14
+ thresholds: {
15
+ criticalCSS: 50, // KB - Mobile-critical styles should be minimal
16
+ progressiveCSS: 150, // KB - Desktop enhancement styles
17
+ totalCSS: 200, // KB - Total CSS budget
18
+ javascript: 500 // KB - JS bundle threshold
19
+ },
20
+ reportPath: 'performance-report.json',
21
+ enableAlerts: true
22
+ };
23
+ export default function performanceMonitor(config = {}) {
24
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
25
+ const startTime = Date.now();
26
+ return {
27
+ name: 'performance-monitor',
28
+ apply: 'build',
29
+ generateBundle(_options, bundle) {
30
+ const analysis = analyzeBundle(bundle, startTime, finalConfig);
31
+ // Write performance report
32
+ const reportPath = path.resolve(finalConfig.reportPath);
33
+ fs.writeFileSync(reportPath, JSON.stringify(analysis, null, 2));
34
+ // Console output
35
+ logPerformanceReport(analysis, finalConfig);
36
+ // Alerts
37
+ if (finalConfig.enableAlerts && analysis.alerts.length > 0) {
38
+ console.warn('\n⚠️ Performance Alerts:');
39
+ analysis.alerts.forEach(alert => console.warn(` ${alert}`));
40
+ }
41
+ }
42
+ };
43
+ }
44
+ /**
45
+ * Analyzes bundle contents and generates performance report
46
+ */
47
+ function analyzeBundle(bundle, startTime, config) {
48
+ const analysis = {
49
+ timestamp: new Date().toISOString(),
50
+ files: {},
51
+ totals: {
52
+ criticalCSS: 0,
53
+ progressiveCSS: 0,
54
+ totalCSS: 0,
55
+ javascript: 0,
56
+ assets: 0,
57
+ total: 0
58
+ },
59
+ performance: {
60
+ bundleTime: Date.now() - startTime,
61
+ compressionRatio: 0
62
+ },
63
+ alerts: []
64
+ };
65
+ // Analyze each file in the bundle
66
+ Object.entries(bundle).forEach(([fileName, asset]) => {
67
+ if (asset.type === 'asset' || asset.type === 'chunk') {
68
+ const size = getAssetSize(asset);
69
+ const type = getAssetType(fileName);
70
+ const category = getAssetCategory(fileName);
71
+ analysis.files[fileName] = {
72
+ size,
73
+ type,
74
+ category
75
+ };
76
+ // Update totals
77
+ if (type === 'css') {
78
+ if (category === 'critical') {
79
+ analysis.totals.criticalCSS += size;
80
+ }
81
+ else if (category === 'progressive') {
82
+ analysis.totals.progressiveCSS += size;
83
+ }
84
+ analysis.totals.totalCSS += size;
85
+ }
86
+ else if (type === 'js') {
87
+ analysis.totals.javascript += size;
88
+ }
89
+ else {
90
+ analysis.totals.assets += size;
91
+ }
92
+ analysis.totals.total += size;
93
+ }
94
+ });
95
+ // Calculate content-aware estimated gzip compression ratio
96
+ const calculateEstimatedGzipRatio = () => {
97
+ // Realistic gzip compression ratios by content type (compressed size / original size)
98
+ const cssGzipRatio = 0.15; // CSS compresses to ~15% of original size
99
+ const jsGzipRatio = 0.25; // JavaScript compresses to ~25% of original size
100
+ const cssSize = analysis.totals.totalCSS;
101
+ const jsSize = analysis.totals.javascript;
102
+ // Only calculate gzip for CSS and JS (assets like fonts/images typically aren't gzipped by CDNs)
103
+ const compressibleSize = cssSize + jsSize;
104
+ if (compressibleSize === 0)
105
+ return 0;
106
+ // Calculate estimated gzipped size for compressible content only
107
+ const estimatedGzipSize = (cssSize * cssGzipRatio) +
108
+ (jsSize * jsGzipRatio);
109
+ // Return ratio of estimated gzipped size to total compressible size
110
+ return Math.round((estimatedGzipSize / compressibleSize) * 100) / 100;
111
+ };
112
+ analysis.performance.compressionRatio = calculateEstimatedGzipRatio();
113
+ // Generate alerts
114
+ analysis.alerts = generateAlerts(analysis.totals, config.thresholds);
115
+ return analysis;
116
+ }
117
+ /**
118
+ * Determines asset size in KB
119
+ */
120
+ function getAssetSize(asset) {
121
+ const source = asset.source || asset.code || '';
122
+ const bytes = typeof source === 'string' ? Buffer.byteLength(source, 'utf8') : source.length;
123
+ return Math.round(bytes / 1024 * 100) / 100; // KB with 2 decimal precision
124
+ }
125
+ /**
126
+ * Determines asset type from filename
127
+ */
128
+ function getAssetType(fileName) {
129
+ if (fileName.endsWith('.css'))
130
+ return 'css';
131
+ if (fileName.endsWith('.js') || fileName.endsWith('.mjs'))
132
+ return 'js';
133
+ return 'asset';
134
+ }
135
+ /**
136
+ * Categorizes assets for performance analysis
137
+ */
138
+ function getAssetCategory(fileName) {
139
+ if (fileName.includes('critical'))
140
+ return 'critical';
141
+ if (fileName.includes('progressive'))
142
+ return 'progressive';
143
+ if (fileName.includes('vendor') || fileName.includes('node_modules'))
144
+ return 'vendor';
145
+ if (fileName.includes('component'))
146
+ return 'component';
147
+ return undefined;
148
+ }
149
+ /**
150
+ * Generates performance alerts based on thresholds
151
+ */
152
+ function generateAlerts(totals, thresholds) {
153
+ const alerts = [];
154
+ if (totals.criticalCSS > thresholds.criticalCSS) {
155
+ alerts.push(`Critical CSS size (${totals.criticalCSS}KB) exceeds threshold (${thresholds.criticalCSS}KB)`);
156
+ }
157
+ if (totals.progressiveCSS > thresholds.progressiveCSS) {
158
+ alerts.push(`Progressive CSS size (${totals.progressiveCSS}KB) exceeds threshold (${thresholds.progressiveCSS}KB)`);
159
+ }
160
+ if (totals.totalCSS > thresholds.totalCSS) {
161
+ alerts.push(`Total CSS size (${totals.totalCSS}KB) exceeds threshold (${thresholds.totalCSS}KB)`);
162
+ }
163
+ if (totals.javascript > thresholds.javascript) {
164
+ alerts.push(`JavaScript bundle size (${totals.javascript}KB) exceeds threshold (${thresholds.javascript}KB)`);
165
+ }
166
+ return alerts;
167
+ }
168
+ /**
169
+ * Logs performance report to console
170
+ */
171
+ function logPerformanceReport(analysis, config) {
172
+ console.log('\n📊 Performance Analysis Report');
173
+ console.log('================================');
174
+ console.log('\n📦 Bundle Sizes:');
175
+ console.log(` Critical CSS: ${analysis.totals.criticalCSS}KB (threshold: ${config.thresholds.criticalCSS}KB)`);
176
+ console.log(` Progressive CSS: ${analysis.totals.progressiveCSS}KB (threshold: ${config.thresholds.progressiveCSS}KB)`);
177
+ console.log(` Total CSS: ${analysis.totals.totalCSS}KB (threshold: ${config.thresholds.totalCSS}KB)`);
178
+ console.log(` JavaScript: ${analysis.totals.javascript}KB (threshold: ${config.thresholds.javascript}KB)`);
179
+ console.log(` Assets: ${analysis.totals.assets}KB`);
180
+ console.log(` Total Bundle: ${analysis.totals.total}KB`);
181
+ console.log('\n⚡ Performance Metrics:');
182
+ console.log(` Build Time: ${analysis.performance.bundleTime}ms`);
183
+ const compressibleSize = analysis.totals.totalCSS + analysis.totals.javascript;
184
+ console.log(` Gzip Ratio: ${analysis.performance.compressionRatio} (CSS+JS compression)`);
185
+ console.log(` Est. Gzip Size: ~${Math.round(compressibleSize * analysis.performance.compressionRatio)}KB (CSS+JS only)`);
186
+ console.log(`\n📝 Report saved to: ${config.reportPath}`);
187
+ if (analysis.alerts.length === 0) {
188
+ console.log('\n✅ All performance thresholds met!');
189
+ }
190
+ }
191
+ /**
192
+ * Creates performance comparison utility
193
+ */
194
+ export function comparePerformance(currentReport, baselineReport) {
195
+ if (!baselineReport || !fs.existsSync(baselineReport)) {
196
+ console.log('\n📈 Baseline report not found. Current report will serve as baseline.');
197
+ return;
198
+ }
199
+ try {
200
+ const current = JSON.parse(fs.readFileSync(currentReport, 'utf8'));
201
+ const baseline = JSON.parse(fs.readFileSync(baselineReport, 'utf8'));
202
+ console.log('\n📈 Performance Comparison (vs baseline)');
203
+ console.log('======================================');
204
+ const metrics = [
205
+ { name: 'Critical CSS', current: current.totals.criticalCSS, baseline: baseline.totals.criticalCSS },
206
+ { name: 'Progressive CSS', current: current.totals.progressiveCSS, baseline: baseline.totals.progressiveCSS },
207
+ { name: 'Total CSS', current: current.totals.totalCSS, baseline: baseline.totals.totalCSS },
208
+ { name: 'JavaScript', current: current.totals.javascript, baseline: baseline.totals.javascript },
209
+ { name: 'Total Bundle', current: current.totals.total, baseline: baseline.totals.total }
210
+ ];
211
+ metrics.forEach(metric => {
212
+ const diff = metric.current - metric.baseline;
213
+ const diffPercent = metric.baseline > 0 ? Math.round((diff / metric.baseline) * 100) : 0;
214
+ const indicator = diff > 0 ? '📈' : diff < 0 ? '📉' : '➡️';
215
+ console.log(` ${metric.name.padEnd(15)} ${indicator} ${metric.current}KB (${diff >= 0 ? '+' : ''}${diff}KB, ${diffPercent >= 0 ? '+' : ''}${diffPercent}%)`);
216
+ });
217
+ }
218
+ catch (error) {
219
+ console.error('Failed to compare performance reports:', error);
220
+ }
221
+ }
@@ -0,0 +1,21 @@
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 type { Plugin } from 'vite';
10
+ interface ProgressiveCSSConfig {
11
+ critical: string[];
12
+ progressive: string[];
13
+ progressivePath: string;
14
+ }
15
+ export default function progressiveCSSLoader(config?: Partial<ProgressiveCSSConfig>): Plugin;
16
+ /**
17
+ * Creates progressive CSS loading script for runtime
18
+ */
19
+ export declare function createProgressiveLoader(): string;
20
+ export {};
21
+ //# sourceMappingURL=progressive-css-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progressive-css-loader.d.ts","sourceRoot":"","sources":["../../src/plugins/progressive-css-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAGlC,UAAU,oBAAoB;IAE5B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAElB,WAAW,EAAE,MAAM,EAAE,CAAA;IAErB,eAAe,EAAE,MAAM,CAAA;CACxB;AAQD,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,MAAM,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAAG,MAAM,CA6C/F;AAuID;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CA0DhD"}
@@ -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
+ }