@sc4rfurryx/proteusjs 1.0.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 (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,646 @@
1
+ /**
2
+ * CSS Optimization Engine for ProteusJS
3
+ * Style deduplication, critical CSS extraction, and performance optimization
4
+ */
5
+
6
+ import { logger } from '../utils/Logger';
7
+
8
+ export interface CSSOptimizationConfig {
9
+ deduplication: boolean;
10
+ criticalCSS: boolean;
11
+ unusedStyleRemoval: boolean;
12
+ customPropertyOptimization: boolean;
13
+ styleInvalidationTracking: boolean;
14
+ minification: boolean;
15
+ autoprefixer: boolean;
16
+ purgeUnused: boolean;
17
+ }
18
+
19
+ export interface StyleRule {
20
+ selector: string;
21
+ declarations: Map<string, string>;
22
+ specificity: number;
23
+ source: 'inline' | 'stylesheet' | 'generated';
24
+ used: boolean;
25
+ critical: boolean;
26
+ }
27
+
28
+ export interface OptimizationMetrics {
29
+ originalSize: number;
30
+ optimizedSize: number;
31
+ compressionRatio: number;
32
+ rulesRemoved: number;
33
+ duplicatesFound: number;
34
+ customPropertiesOptimized: number;
35
+ criticalRulesExtracted: number;
36
+ }
37
+
38
+ export class CSSOptimizationEngine {
39
+ private config: Required<CSSOptimizationConfig>;
40
+ private styleRules: Map<string, StyleRule> = new Map();
41
+ private customProperties: Map<string, string> = new Map();
42
+ private usageTracker: Map<string, number> = new Map();
43
+ private criticalSelectors: Set<string> = new Set();
44
+ private metrics!: OptimizationMetrics;
45
+ private mutationObserver: MutationObserver | null = null;
46
+
47
+ constructor(config: Partial<CSSOptimizationConfig> = {}) {
48
+ this.config = {
49
+ deduplication: true,
50
+ criticalCSS: true,
51
+ unusedStyleRemoval: true,
52
+ customPropertyOptimization: true,
53
+ styleInvalidationTracking: true,
54
+ minification: true,
55
+ autoprefixer: false,
56
+ purgeUnused: true,
57
+ ...config
58
+ };
59
+
60
+ this.metrics = this.createInitialMetrics();
61
+ this.setupStyleTracking();
62
+ }
63
+
64
+ /**
65
+ * Analyze and optimize all stylesheets
66
+ */
67
+ public async optimizeAll(): Promise<OptimizationMetrics> {
68
+ const startTime = performance.now();
69
+
70
+ // Extract all styles
71
+ await this.extractAllStyles();
72
+
73
+ // Apply optimizations
74
+ if (this.config.deduplication) {
75
+ this.deduplicateStyles();
76
+ }
77
+
78
+ if (this.config.unusedStyleRemoval) {
79
+ this.removeUnusedStyles();
80
+ }
81
+
82
+ if (this.config.customPropertyOptimization) {
83
+ this.optimizeCustomProperties();
84
+ }
85
+
86
+ if (this.config.criticalCSS) {
87
+ this.extractCriticalCSS();
88
+ }
89
+
90
+ // Generate optimized CSS
91
+ const optimizedCSS = this.generateOptimizedCSS();
92
+
93
+ // Update metrics
94
+ this.updateMetrics(optimizedCSS);
95
+
96
+ const endTime = performance.now();
97
+ console.log(`CSS optimization completed in ${endTime - startTime}ms`);
98
+
99
+ return this.metrics;
100
+ }
101
+
102
+ /**
103
+ * Extract critical CSS for above-the-fold content
104
+ */
105
+ public extractCriticalCSS(): string {
106
+ const criticalRules: StyleRule[] = [];
107
+ const viewportHeight = window.innerHeight;
108
+
109
+ // Find elements in viewport
110
+ const elementsInViewport = this.getElementsInViewport(viewportHeight);
111
+
112
+ // Extract selectors for viewport elements
113
+ elementsInViewport.forEach(element => {
114
+ const selectors = this.getSelectorsForElement(element);
115
+ selectors.forEach(selector => {
116
+ this.criticalSelectors.add(selector);
117
+ const rule = this.styleRules.get(selector);
118
+ if (rule) {
119
+ rule.critical = true;
120
+ criticalRules.push(rule);
121
+ }
122
+ });
123
+ });
124
+
125
+ this.metrics.criticalRulesExtracted = criticalRules.length;
126
+
127
+ return this.rulesToCSS(criticalRules);
128
+ }
129
+
130
+ /**
131
+ * Remove unused CSS rules
132
+ */
133
+ public removeUnusedStyles(): void {
134
+ if (!this.config.purgeUnused) return;
135
+
136
+ let removedCount = 0;
137
+
138
+ this.styleRules.forEach((rule, selector) => {
139
+ if (!rule.used && !rule.critical) {
140
+ // Check if selector matches any element in DOM
141
+ try {
142
+ const elements = document.querySelectorAll(selector);
143
+ if (elements.length === 0) {
144
+ this.styleRules.delete(selector);
145
+ removedCount++;
146
+ }
147
+ } catch (error) {
148
+ // Invalid selector, remove it
149
+ this.styleRules.delete(selector);
150
+ removedCount++;
151
+ }
152
+ }
153
+ });
154
+
155
+ this.metrics.rulesRemoved = removedCount;
156
+ }
157
+
158
+ /**
159
+ * Deduplicate identical style rules
160
+ */
161
+ public deduplicateStyles(): void {
162
+ const declarationGroups = new Map<string, string[]>();
163
+ let duplicatesFound = 0;
164
+
165
+ // Group rules by declarations
166
+ this.styleRules.forEach((rule, selector) => {
167
+ const declarationsKey = this.serializeDeclarations(rule.declarations);
168
+
169
+ if (!declarationGroups.has(declarationsKey)) {
170
+ declarationGroups.set(declarationsKey, []);
171
+ }
172
+
173
+ declarationGroups.get(declarationsKey)!.push(selector);
174
+ });
175
+
176
+ // Merge duplicate rules
177
+ declarationGroups.forEach((selectors) => {
178
+ if (selectors.length > 1) {
179
+ duplicatesFound += selectors.length - 1;
180
+
181
+ // Keep the first rule and merge selectors
182
+ const primarySelector = selectors[0]!;
183
+ const primaryRule = this.styleRules.get(primarySelector)!;
184
+
185
+ // Create combined selector
186
+ const combinedSelector = selectors.join(', ');
187
+
188
+ // Remove individual rules
189
+ selectors.forEach(selector => {
190
+ this.styleRules.delete(selector);
191
+ });
192
+
193
+ // Add combined rule
194
+ this.styleRules.set(combinedSelector, {
195
+ ...primaryRule,
196
+ selector: combinedSelector
197
+ });
198
+ }
199
+ });
200
+
201
+ this.metrics.duplicatesFound = duplicatesFound;
202
+ }
203
+
204
+ /**
205
+ * Optimize CSS custom properties
206
+ */
207
+ public optimizeCustomProperties(): void {
208
+ let optimizedCount = 0;
209
+
210
+ // Find all custom property declarations
211
+ this.styleRules.forEach(rule => {
212
+ rule.declarations.forEach((value, property) => {
213
+ if (property.startsWith('--')) {
214
+ this.customProperties.set(property, value);
215
+ }
216
+ });
217
+ });
218
+
219
+ // Optimize custom property usage
220
+ this.customProperties.forEach((value, property) => {
221
+ const usage = this.findCustomPropertyUsage(property);
222
+
223
+ if (usage.length === 1) {
224
+ // Inline single-use custom properties
225
+ const targetRule = usage[0]!;
226
+ const targetProperty = this.findPropertyUsingCustomProperty(targetRule, property);
227
+
228
+ if (targetProperty && targetRule) {
229
+ targetRule.declarations.set(targetProperty, value);
230
+ targetRule.declarations.delete(property);
231
+ optimizedCount++;
232
+ }
233
+ }
234
+ });
235
+
236
+ this.metrics.customPropertiesOptimized = optimizedCount;
237
+ }
238
+
239
+ /**
240
+ * Track style invalidations
241
+ */
242
+ public trackStyleInvalidations(): void {
243
+ if (!this.config.styleInvalidationTracking) return;
244
+
245
+ try {
246
+ // Monitor DOM mutations that might affect styles
247
+ if (typeof MutationObserver !== 'undefined' && document && document.body) {
248
+ this.mutationObserver = new MutationObserver((mutations) => {
249
+ mutations.forEach(mutation => {
250
+ if (mutation.type === 'attributes' &&
251
+ (mutation.attributeName === 'class' || mutation.attributeName === 'style')) {
252
+ this.handleStyleInvalidation(mutation.target as Element);
253
+ }
254
+ });
255
+ });
256
+
257
+ // Ensure document.body exists before observing
258
+ if (document.body && this.mutationObserver && typeof this.mutationObserver.observe === 'function') {
259
+ this.mutationObserver.observe(document.body, {
260
+ attributes: true,
261
+ attributeFilter: ['class', 'style'],
262
+ subtree: true
263
+ });
264
+ }
265
+ }
266
+ } catch (error) {
267
+ logger.warn('Failed to setup style invalidation tracking:', error);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Generate optimized CSS string
273
+ */
274
+ public generateOptimizedCSS(): string {
275
+ const rules = Array.from(this.styleRules.values());
276
+
277
+ // Sort rules by specificity and source
278
+ rules.sort((a, b) => {
279
+ if (a.critical !== b.critical) {
280
+ return a.critical ? -1 : 1; // Critical rules first
281
+ }
282
+ return a.specificity - b.specificity;
283
+ });
284
+
285
+ return this.rulesToCSS(rules);
286
+ }
287
+
288
+ /**
289
+ * Get optimization metrics
290
+ */
291
+ public getMetrics(): OptimizationMetrics {
292
+ return { ...this.metrics };
293
+ }
294
+
295
+ /**
296
+ * Clear optimization cache
297
+ */
298
+ public clearCache(): void {
299
+ this.styleRules.clear();
300
+ this.customProperties.clear();
301
+ this.usageTracker.clear();
302
+ this.criticalSelectors.clear();
303
+ this.metrics = this.createInitialMetrics();
304
+ }
305
+
306
+ /**
307
+ * Destroy the optimization engine
308
+ */
309
+ public destroy(): void {
310
+ this.mutationObserver?.disconnect();
311
+ this.clearCache();
312
+ }
313
+
314
+ /**
315
+ * Extract all styles from document
316
+ */
317
+ private async extractAllStyles(): Promise<void> {
318
+ const originalSize = this.calculateCurrentCSSSize();
319
+ this.metrics.originalSize = originalSize;
320
+
321
+ // Extract from stylesheets
322
+ for (const stylesheet of document.styleSheets) {
323
+ try {
324
+ await this.extractFromStylesheet(stylesheet);
325
+ } catch (error) {
326
+ console.warn('Could not access stylesheet:', error);
327
+ }
328
+ }
329
+
330
+ // Extract inline styles
331
+ this.extractInlineStyles();
332
+ }
333
+
334
+ /**
335
+ * Extract styles from a stylesheet
336
+ */
337
+ private async extractFromStylesheet(stylesheet: CSSStyleSheet): Promise<void> {
338
+ try {
339
+ const rules = stylesheet.cssRules;
340
+
341
+ for (let i = 0; i < rules.length; i++) {
342
+ const rule = rules[i];
343
+
344
+ if (rule instanceof CSSStyleRule) {
345
+ this.extractStyleRule(rule, 'stylesheet');
346
+ } else if (rule instanceof CSSMediaRule) {
347
+ // Handle media queries
348
+ for (let j = 0; j < rule.cssRules.length; j++) {
349
+ const mediaRule = rule.cssRules[j];
350
+ if (mediaRule instanceof CSSStyleRule) {
351
+ this.extractStyleRule(mediaRule, 'stylesheet');
352
+ }
353
+ }
354
+ }
355
+ }
356
+ } catch (error) {
357
+ console.warn('Error extracting from stylesheet:', error);
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Extract a single style rule
363
+ */
364
+ private extractStyleRule(rule: CSSStyleRule, source: 'inline' | 'stylesheet' | 'generated'): void {
365
+ const selector = rule.selectorText;
366
+ const declarations = new Map<string, string>();
367
+
368
+ // Extract declarations
369
+ for (let i = 0; i < rule.style.length; i++) {
370
+ const property = rule.style[i]!;
371
+ const value = rule.style.getPropertyValue(property);
372
+ declarations.set(property, value);
373
+ }
374
+
375
+ const styleRule: StyleRule = {
376
+ selector,
377
+ declarations,
378
+ specificity: this.calculateSpecificity(selector),
379
+ source,
380
+ used: false,
381
+ critical: false
382
+ };
383
+
384
+ this.styleRules.set(selector, styleRule);
385
+ }
386
+
387
+ /**
388
+ * Extract inline styles
389
+ */
390
+ private extractInlineStyles(): void {
391
+ const elementsWithStyle = document.querySelectorAll('[style]');
392
+
393
+ elementsWithStyle.forEach(element => {
394
+ const style = (element as HTMLElement).style;
395
+ const declarations = new Map<string, string>();
396
+
397
+ for (let i = 0; i < style.length; i++) {
398
+ const property = style[i];
399
+ if (property) {
400
+ const value = style.getPropertyValue(property);
401
+ declarations.set(property, value);
402
+ }
403
+ }
404
+
405
+ if (declarations.size > 0) {
406
+ const selector = this.generateSelectorForElement(element);
407
+
408
+ const styleRule: StyleRule = {
409
+ selector,
410
+ declarations,
411
+ specificity: 1000, // Inline styles have high specificity
412
+ source: 'inline',
413
+ used: true,
414
+ critical: this.isElementInViewport(element)
415
+ };
416
+
417
+ this.styleRules.set(selector, styleRule);
418
+ }
419
+ });
420
+ }
421
+
422
+ /**
423
+ * Calculate CSS specificity
424
+ */
425
+ private calculateSpecificity(selector: string): number {
426
+ let specificity = 0;
427
+
428
+ // Count IDs
429
+ specificity += (selector.match(/#/g) || []).length * 100;
430
+
431
+ // Count classes, attributes, and pseudo-classes
432
+ specificity += (selector.match(/\.|:|\[/g) || []).length * 10;
433
+
434
+ // Count elements and pseudo-elements
435
+ specificity += (selector.match(/[a-zA-Z]/g) || []).length;
436
+
437
+ return specificity;
438
+ }
439
+
440
+ /**
441
+ * Get elements in viewport
442
+ */
443
+ private getElementsInViewport(viewportHeight: number): Element[] {
444
+ const elements: Element[] = [];
445
+ const allElements = document.querySelectorAll('*');
446
+
447
+ allElements.forEach(element => {
448
+ if (this.isElementInViewport(element, viewportHeight)) {
449
+ elements.push(element);
450
+ }
451
+ });
452
+
453
+ return elements;
454
+ }
455
+
456
+ /**
457
+ * Check if element is in viewport
458
+ */
459
+ private isElementInViewport(element: Element, viewportHeight?: number): boolean {
460
+ const rect = element.getBoundingClientRect();
461
+ const height = viewportHeight || window.innerHeight;
462
+
463
+ return rect.top < height && rect.bottom > 0;
464
+ }
465
+
466
+ /**
467
+ * Get selectors that match an element
468
+ */
469
+ private getSelectorsForElement(element: Element): string[] {
470
+ const selectors: string[] = [];
471
+
472
+ this.styleRules.forEach((rule, selector) => {
473
+ try {
474
+ if (element.matches(selector)) {
475
+ selectors.push(selector);
476
+ rule.used = true;
477
+ }
478
+ } catch (error) {
479
+ // Invalid selector
480
+ }
481
+ });
482
+
483
+ return selectors;
484
+ }
485
+
486
+ /**
487
+ * Generate selector for element
488
+ */
489
+ private generateSelectorForElement(element: Element): string {
490
+ if (element.id) {
491
+ return `#${element.id}`;
492
+ }
493
+
494
+ if (element.className) {
495
+ const classes = element.className.split(' ').filter(c => c.trim());
496
+ if (classes.length > 0) {
497
+ return `.${classes.join('.')}`;
498
+ }
499
+ }
500
+
501
+ return element.tagName.toLowerCase();
502
+ }
503
+
504
+ /**
505
+ * Convert rules to CSS string
506
+ */
507
+ private rulesToCSS(rules: StyleRule[]): string {
508
+ let css = '';
509
+
510
+ rules.forEach(rule => {
511
+ css += `${rule.selector} {\n`;
512
+
513
+ rule.declarations.forEach((value, property) => {
514
+ css += ` ${property}: ${value};\n`;
515
+ });
516
+
517
+ css += '}\n\n';
518
+ });
519
+
520
+ if (this.config.minification) {
521
+ css = this.minifyCSS(css);
522
+ }
523
+
524
+ return css;
525
+ }
526
+
527
+ /**
528
+ * Minify CSS
529
+ */
530
+ private minifyCSS(css: string): string {
531
+ return css
532
+ .replace(/\s+/g, ' ')
533
+ .replace(/;\s*}/g, '}')
534
+ .replace(/{\s*/g, '{')
535
+ .replace(/}\s*/g, '}')
536
+ .replace(/:\s*/g, ':')
537
+ .replace(/;\s*/g, ';')
538
+ .trim();
539
+ }
540
+
541
+ /**
542
+ * Serialize declarations for comparison
543
+ */
544
+ private serializeDeclarations(declarations: Map<string, string>): string {
545
+ const sorted = Array.from(declarations.entries()).sort();
546
+ return JSON.stringify(sorted);
547
+ }
548
+
549
+ /**
550
+ * Find custom property usage
551
+ */
552
+ private findCustomPropertyUsage(property: string): StyleRule[] {
553
+ const usage: StyleRule[] = [];
554
+
555
+ this.styleRules.forEach(rule => {
556
+ rule.declarations.forEach(value => {
557
+ if (value.includes(`var(${property})`)) {
558
+ usage.push(rule);
559
+ }
560
+ });
561
+ });
562
+
563
+ return usage;
564
+ }
565
+
566
+ /**
567
+ * Find property using custom property
568
+ */
569
+ private findPropertyUsingCustomProperty(rule: StyleRule, customProperty: string): string | null {
570
+ for (const [property, value] of rule.declarations) {
571
+ if (value.includes(`var(${customProperty})`)) {
572
+ return property;
573
+ }
574
+ }
575
+ return null;
576
+ }
577
+
578
+ /**
579
+ * Handle style invalidation
580
+ */
581
+ private handleStyleInvalidation(element: Element): void {
582
+ const selectors = this.getSelectorsForElement(element);
583
+
584
+ selectors.forEach(selector => {
585
+ const count = this.usageTracker.get(selector) || 0;
586
+ this.usageTracker.set(selector, count + 1);
587
+ });
588
+ }
589
+
590
+ /**
591
+ * Calculate current CSS size
592
+ */
593
+ private calculateCurrentCSSSize(): number {
594
+ let size = 0;
595
+
596
+ for (const stylesheet of document.styleSheets) {
597
+ try {
598
+ const rules = stylesheet.cssRules;
599
+ for (let i = 0; i < rules.length; i++) {
600
+ size += rules[i]!.cssText.length;
601
+ }
602
+ } catch (error) {
603
+ // Can't access external stylesheets
604
+ }
605
+ }
606
+
607
+ return size;
608
+ }
609
+
610
+ /**
611
+ * Update optimization metrics
612
+ */
613
+ private updateMetrics(optimizedCSS: string): void {
614
+ this.metrics.optimizedSize = optimizedCSS.length;
615
+ this.metrics.compressionRatio = this.metrics.originalSize > 0
616
+ ? (this.metrics.originalSize - this.metrics.optimizedSize) / this.metrics.originalSize
617
+ : 0;
618
+ }
619
+
620
+ /**
621
+ * Setup style tracking
622
+ */
623
+ private setupStyleTracking(): void {
624
+ if (this.config.styleInvalidationTracking) {
625
+ // Delay tracking setup to allow DOM to settle
626
+ setTimeout(() => {
627
+ this.trackStyleInvalidations();
628
+ }, 100);
629
+ }
630
+ }
631
+
632
+ /**
633
+ * Create initial metrics
634
+ */
635
+ private createInitialMetrics(): OptimizationMetrics {
636
+ return {
637
+ originalSize: 0,
638
+ optimizedSize: 0,
639
+ compressionRatio: 0,
640
+ rulesRemoved: 0,
641
+ duplicatesFound: 0,
642
+ customPropertiesOptimized: 0,
643
+ criticalRulesExtracted: 0
644
+ };
645
+ }
646
+ }