@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,1076 @@
1
+ /**
2
+ * Comprehensive Browser Compatibility System for ProteusJS
3
+ * Feature detection, polyfills, and graceful degradation
4
+ */
5
+
6
+ import { logger } from '../utils/Logger';
7
+
8
+ export interface BrowserInfo {
9
+ name: string;
10
+ version: string;
11
+ engine: string;
12
+ platform: string;
13
+ mobile: boolean;
14
+ supported: boolean;
15
+ }
16
+
17
+ export interface FeatureSupport {
18
+ containerQueries: boolean;
19
+ clampFunction: boolean;
20
+ customProperties: boolean;
21
+ flexbox: boolean;
22
+ grid: boolean;
23
+ intersectionObserver: boolean;
24
+ resizeObserver: boolean;
25
+ mutationObserver: boolean;
26
+ requestAnimationFrame: boolean;
27
+ webAnimations: boolean;
28
+ prefersReducedMotion: boolean;
29
+ prefersColorScheme: boolean;
30
+ viewportUnits: boolean;
31
+ calc: boolean;
32
+ transforms: boolean;
33
+ transitions: boolean;
34
+ animations: boolean;
35
+ webFonts: boolean;
36
+ fontDisplay: boolean;
37
+ fontVariationSettings: boolean;
38
+ }
39
+
40
+ export interface CompatibilityConfig {
41
+ enablePolyfills: boolean;
42
+ gracefulDegradation: boolean;
43
+ fallbackStrategies: boolean;
44
+ performanceOptimizations: boolean;
45
+ legacySupport: boolean;
46
+ modernFeatures: boolean;
47
+ autoDetection: boolean;
48
+ }
49
+
50
+ export interface PolyfillInfo {
51
+ name: string;
52
+ required: boolean;
53
+ loaded: boolean;
54
+ size: number;
55
+ url?: string;
56
+ fallback?: () => void;
57
+ }
58
+
59
+ export class BrowserCompatibility {
60
+ private browserInfo: BrowserInfo;
61
+ private featureSupport: FeatureSupport;
62
+ private config: Required<CompatibilityConfig>;
63
+ private polyfills: Map<string, PolyfillInfo> = new Map();
64
+ private fallbacks: Map<string, () => void> = new Map();
65
+ private modernFeatures: Set<string> = new Set();
66
+ private legacyFeatures: Set<string> = new Set();
67
+
68
+ constructor(config: Partial<CompatibilityConfig> = {}) {
69
+ this.config = {
70
+ enablePolyfills: true,
71
+ gracefulDegradation: true,
72
+ fallbackStrategies: true,
73
+ performanceOptimizations: true,
74
+ legacySupport: true,
75
+ modernFeatures: true,
76
+ autoDetection: true,
77
+ ...config
78
+ };
79
+
80
+ this.browserInfo = this.detectBrowser();
81
+ this.featureSupport = this.detectFeatures();
82
+
83
+ if (this.config.autoDetection) {
84
+ this.initializeCompatibility();
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Initialize compatibility system
90
+ */
91
+ public async initializeCompatibility(): Promise<void> {
92
+ logger.info('Initializing browser compatibility system');
93
+
94
+ // Apply browser-specific fixes
95
+ this.applyBrowserFixes();
96
+
97
+ // Load required polyfills
98
+ if (this.config.enablePolyfills) {
99
+ await this.loadPolyfills();
100
+ }
101
+
102
+ // Setup fallback strategies
103
+ if (this.config.fallbackStrategies) {
104
+ this.setupFallbacks();
105
+ }
106
+
107
+ // Apply performance optimizations
108
+ if (this.config.performanceOptimizations) {
109
+ this.applyPerformanceOptimizations();
110
+ }
111
+
112
+ // Setup modern feature detection
113
+ if (this.config.modernFeatures) {
114
+ this.setupModernFeatures();
115
+ }
116
+
117
+ logger.info('Browser compatibility system initialized', {
118
+ browser: this.browserInfo,
119
+ features: this.featureSupport
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Detect browser information
125
+ */
126
+ private detectBrowser(): BrowserInfo {
127
+ const userAgent = navigator.userAgent;
128
+ const platform = navigator.platform;
129
+
130
+ let name = 'Unknown';
131
+ let version = '0';
132
+ let engine = 'Unknown';
133
+
134
+ // Chrome
135
+ if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {
136
+ name = 'Chrome';
137
+ const match = userAgent.match(/Chrome\/(\d+)/);
138
+ version = match?.[1] || '0';
139
+ engine = 'Blink';
140
+ }
141
+ // Firefox
142
+ else if (userAgent.includes('Firefox')) {
143
+ name = 'Firefox';
144
+ const match = userAgent.match(/Firefox\/(\d+)/);
145
+ version = match?.[1] || '0';
146
+ engine = 'Gecko';
147
+ }
148
+ // Safari
149
+ else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
150
+ name = 'Safari';
151
+ const match = userAgent.match(/Version\/(\d+)/);
152
+ version = match?.[1] || '0';
153
+ engine = 'WebKit';
154
+ }
155
+ // Edge
156
+ else if (userAgent.includes('Edg')) {
157
+ name = 'Edge';
158
+ const match = userAgent.match(/Edg\/(\d+)/);
159
+ version = match?.[1] || '0';
160
+ engine = 'Blink';
161
+ }
162
+ // Internet Explorer
163
+ else if (userAgent.includes('MSIE') || userAgent.includes('Trident')) {
164
+ name = 'Internet Explorer';
165
+ const match = userAgent.match(/(?:MSIE |rv:)(\d+)/);
166
+ version = match?.[1] || '0';
167
+ engine = 'Trident';
168
+ }
169
+
170
+ const mobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
171
+ const supported = this.isBrowserSupported(name, parseInt(version));
172
+
173
+ return {
174
+ name,
175
+ version,
176
+ engine,
177
+ platform,
178
+ mobile,
179
+ supported
180
+ };
181
+ }
182
+
183
+ /**
184
+ * Check if browser is supported
185
+ */
186
+ private isBrowserSupported(name: string, version: number): boolean {
187
+ const minimumVersions: Record<string, number> = {
188
+ 'Chrome': 60,
189
+ 'Firefox': 55,
190
+ 'Safari': 12,
191
+ 'Edge': 79,
192
+ 'Internet Explorer': 11
193
+ };
194
+
195
+ return version >= (minimumVersions[name] || 0);
196
+ }
197
+
198
+ /**
199
+ * Detect feature support
200
+ */
201
+ private detectFeatures(): FeatureSupport {
202
+ return {
203
+ containerQueries: this.supportsContainerQueries(),
204
+ clampFunction: this.supportsClamp(),
205
+ customProperties: this.supportsCustomProperties(),
206
+ flexbox: this.supportsFlexbox(),
207
+ grid: this.supportsGrid(),
208
+ intersectionObserver: 'IntersectionObserver' in window,
209
+ resizeObserver: 'ResizeObserver' in window,
210
+ mutationObserver: 'MutationObserver' in window,
211
+ requestAnimationFrame: 'requestAnimationFrame' in window,
212
+ webAnimations: 'animate' in document.createElement('div'),
213
+ prefersReducedMotion: this.supportsMediaQuery('(prefers-reduced-motion: reduce)'),
214
+ prefersColorScheme: this.supportsMediaQuery('(prefers-color-scheme: dark)'),
215
+ viewportUnits: this.supportsViewportUnits(),
216
+ calc: this.supportsCalc(),
217
+ transforms: this.supportsTransforms(),
218
+ transitions: this.supportsTransitions(),
219
+ animations: this.supportsAnimations(),
220
+ webFonts: this.supportsWebFonts(),
221
+ fontDisplay: this.supportsFontDisplay(),
222
+ fontVariationSettings: this.supportsFontVariationSettings()
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Check container queries support
228
+ */
229
+ private supportsContainerQueries(): boolean {
230
+ try {
231
+ return CSS.supports('container-type', 'inline-size');
232
+ } catch {
233
+ return false;
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Check clamp() function support
239
+ */
240
+ private supportsClamp(): boolean {
241
+ try {
242
+ return CSS.supports('width', 'clamp(1px, 2px, 3px)');
243
+ } catch {
244
+ return false;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Check CSS custom properties support
250
+ */
251
+ private supportsCustomProperties(): boolean {
252
+ try {
253
+ return CSS.supports('--test', '0');
254
+ } catch {
255
+ return false;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Check flexbox support
261
+ */
262
+ private supportsFlexbox(): boolean {
263
+ try {
264
+ return CSS.supports('display', 'flex');
265
+ } catch {
266
+ return false;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Check CSS Grid support
272
+ */
273
+ private supportsGrid(): boolean {
274
+ try {
275
+ return CSS.supports('display', 'grid');
276
+ } catch {
277
+ return false;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Check media query support
283
+ */
284
+ private supportsMediaQuery(query: string): boolean {
285
+ try {
286
+ return window.matchMedia(query).media === query;
287
+ } catch {
288
+ return false;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Check viewport units support
294
+ */
295
+ private supportsViewportUnits(): boolean {
296
+ try {
297
+ return CSS.supports('width', '1vw');
298
+ } catch {
299
+ return false;
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Check calc() function support
305
+ */
306
+ private supportsCalc(): boolean {
307
+ try {
308
+ return CSS.supports('width', 'calc(1px + 1px)');
309
+ } catch {
310
+ return false;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Check CSS transforms support
316
+ */
317
+ private supportsTransforms(): boolean {
318
+ try {
319
+ return CSS.supports('transform', 'translateX(1px)');
320
+ } catch {
321
+ return false;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Check CSS transitions support
327
+ */
328
+ private supportsTransitions(): boolean {
329
+ try {
330
+ return CSS.supports('transition', 'all 1s');
331
+ } catch {
332
+ return false;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Check CSS animations support
338
+ */
339
+ private supportsAnimations(): boolean {
340
+ try {
341
+ return CSS.supports('animation', 'test 1s');
342
+ } catch {
343
+ return false;
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Check web fonts support
349
+ */
350
+ private supportsWebFonts(): boolean {
351
+ return 'fonts' in document;
352
+ }
353
+
354
+ /**
355
+ * Check font-display support
356
+ */
357
+ private supportsFontDisplay(): boolean {
358
+ try {
359
+ return CSS.supports('font-display', 'swap');
360
+ } catch {
361
+ return false;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Check font-variation-settings support
367
+ */
368
+ private supportsFontVariationSettings(): boolean {
369
+ try {
370
+ return CSS.supports('font-variation-settings', '"wght" 400');
371
+ } catch {
372
+ return false;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Apply browser-specific fixes
378
+ */
379
+ private applyBrowserFixes(): void {
380
+ // Internet Explorer fixes
381
+ if (this.browserInfo.name === 'Internet Explorer') {
382
+ this.applyIEFixes();
383
+ }
384
+
385
+ // Safari fixes
386
+ if (this.browserInfo.name === 'Safari') {
387
+ this.applySafariFixes();
388
+ }
389
+
390
+ // Firefox fixes
391
+ if (this.browserInfo.name === 'Firefox') {
392
+ this.applyFirefoxFixes();
393
+ }
394
+
395
+ // Mobile browser fixes
396
+ if (this.browserInfo.mobile) {
397
+ this.applyMobileFixes();
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Apply Internet Explorer specific fixes
403
+ */
404
+ private applyIEFixes(): void {
405
+ // Add IE-specific CSS class
406
+ document.documentElement.classList.add('ie');
407
+
408
+ // Fix viewport units in IE
409
+ if (!this.featureSupport.viewportUnits) {
410
+ this.addPolyfill('viewport-units', {
411
+ name: 'Viewport Units Polyfill',
412
+ required: true,
413
+ loaded: false,
414
+ size: 15000,
415
+ fallback: this.viewportUnitsFallback.bind(this)
416
+ });
417
+ }
418
+
419
+ // Fix flexbox in IE
420
+ if (!this.featureSupport.flexbox) {
421
+ this.addPolyfill('flexbox', {
422
+ name: 'Flexbox Polyfill',
423
+ required: true,
424
+ loaded: false,
425
+ size: 25000,
426
+ fallback: this.flexboxFallback.bind(this)
427
+ });
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Apply Safari specific fixes
433
+ */
434
+ private applySafariFixes(): void {
435
+ // Add Safari-specific CSS class
436
+ document.documentElement.classList.add('safari');
437
+
438
+ // Fix backdrop-filter in older Safari
439
+ const safariVersion = parseInt(this.browserInfo.version);
440
+ if (safariVersion < 14) {
441
+ document.documentElement.classList.add('safari-old');
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Apply Firefox specific fixes
447
+ */
448
+ private applyFirefoxFixes(): void {
449
+ // Add Firefox-specific CSS class
450
+ document.documentElement.classList.add('firefox');
451
+
452
+ // Fix scrollbar styling in Firefox
453
+ const style = document.createElement('style');
454
+ style.textContent = `
455
+ /* Firefox scrollbar styling */
456
+ * {
457
+ scrollbar-width: thin;
458
+ scrollbar-color: rgba(0,0,0,0.3) transparent;
459
+ }
460
+ `;
461
+ document.head.appendChild(style);
462
+ }
463
+
464
+ /**
465
+ * Apply mobile browser fixes
466
+ */
467
+ private applyMobileFixes(): void {
468
+ // Add mobile-specific CSS class
469
+ document.documentElement.classList.add('mobile');
470
+
471
+ // Fix viewport height on mobile
472
+ const setVH = () => {
473
+ const vh = window.innerHeight * 0.01;
474
+ document.documentElement.style.setProperty('--vh', `${vh}px`);
475
+ };
476
+
477
+ setVH();
478
+ window.addEventListener('resize', setVH);
479
+ window.addEventListener('orientationchange', setVH);
480
+
481
+ // Prevent zoom on input focus
482
+ const viewport = document.querySelector('meta[name="viewport"]');
483
+ if (viewport) {
484
+ viewport.setAttribute('content',
485
+ 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'
486
+ );
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Load required polyfills
492
+ */
493
+ private async loadPolyfills(): Promise<void> {
494
+ const requiredPolyfills = Array.from(this.polyfills.values())
495
+ .filter(polyfill => polyfill.required && !polyfill.loaded);
496
+
497
+ if (requiredPolyfills.length === 0) {
498
+ return;
499
+ }
500
+
501
+ logger.info(`Loading ${requiredPolyfills.length} polyfills`);
502
+
503
+ for (const polyfill of requiredPolyfills) {
504
+ try {
505
+ if (polyfill.url) {
506
+ await this.loadPolyfillScript(polyfill);
507
+ } else if (polyfill.fallback) {
508
+ polyfill.fallback();
509
+ }
510
+
511
+ polyfill.loaded = true;
512
+ logger.info(`Loaded polyfill: ${polyfill.name}`);
513
+ } catch (error) {
514
+ logger.error(`Failed to load polyfill ${polyfill.name}:`, error);
515
+
516
+ // Try fallback if available
517
+ if (polyfill.fallback) {
518
+ polyfill.fallback();
519
+ polyfill.loaded = true;
520
+ }
521
+ }
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Load polyfill script
527
+ */
528
+ private loadPolyfillScript(polyfill: PolyfillInfo): Promise<void> {
529
+ return new Promise((resolve, reject) => {
530
+ const script = document.createElement('script');
531
+ script.src = polyfill.url!;
532
+ script.async = true;
533
+
534
+ script.onload = () => resolve();
535
+ script.onerror = () => reject(new Error(`Failed to load ${polyfill.url}`));
536
+
537
+ document.head.appendChild(script);
538
+ });
539
+ }
540
+
541
+ /**
542
+ * Add polyfill to the system
543
+ */
544
+ private addPolyfill(name: string, info: PolyfillInfo): void {
545
+ this.polyfills.set(name, info);
546
+ }
547
+
548
+ /**
549
+ * Setup fallback strategies
550
+ */
551
+ private setupFallbacks(): void {
552
+ // Container queries fallback
553
+ if (!this.featureSupport.containerQueries) {
554
+ this.fallbacks.set('container-queries', this.containerQueriesFallback.bind(this));
555
+ }
556
+
557
+ // Clamp function fallback
558
+ if (!this.featureSupport.clampFunction) {
559
+ this.fallbacks.set('clamp', this.clampFallback.bind(this));
560
+ }
561
+
562
+ // Custom properties fallback
563
+ if (!this.featureSupport.customProperties) {
564
+ this.fallbacks.set('custom-properties', this.customPropertiesFallback.bind(this));
565
+ }
566
+
567
+ // Intersection Observer fallback
568
+ if (!this.featureSupport.intersectionObserver) {
569
+ this.fallbacks.set('intersection-observer', this.intersectionObserverFallback.bind(this));
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Container queries fallback
575
+ */
576
+ private containerQueriesFallback(): void {
577
+ // Implement ResizeObserver-based container queries
578
+ if (this.featureSupport.resizeObserver) {
579
+ logger.info('Using ResizeObserver fallback for container queries');
580
+
581
+ // Create a polyfill using ResizeObserver
582
+ const style = document.createElement('style');
583
+ style.id = 'container-queries-fallback';
584
+ style.textContent = `
585
+ /* Container queries fallback using ResizeObserver */
586
+ [data-container-fallback] {
587
+ position: relative;
588
+ }
589
+
590
+ [data-container="small"] {
591
+ --container-size: small;
592
+ }
593
+
594
+ [data-container="medium"] {
595
+ --container-size: medium;
596
+ }
597
+
598
+ [data-container="large"] {
599
+ --container-size: large;
600
+ }
601
+ `;
602
+
603
+ if (!document.getElementById('container-queries-fallback')) {
604
+ document.head.appendChild(style);
605
+ }
606
+
607
+ // Setup ResizeObserver to simulate container queries
608
+ const resizeObserver = new ResizeObserver((entries) => {
609
+ entries.forEach((entry) => {
610
+ const element = entry.target;
611
+ const width = entry.contentRect.width;
612
+
613
+ // Apply container size classes based on width
614
+ element.removeAttribute('data-container');
615
+
616
+ if (width <= 400) {
617
+ element.setAttribute('data-container', 'small');
618
+ } else if (width <= 800) {
619
+ element.setAttribute('data-container', 'medium');
620
+ } else {
621
+ element.setAttribute('data-container', 'large');
622
+ }
623
+ });
624
+ });
625
+
626
+ // Observe all elements with container query attributes
627
+ document.querySelectorAll('[data-container-fallback]').forEach((element) => {
628
+ resizeObserver.observe(element);
629
+ });
630
+
631
+ } else {
632
+ logger.warn('No fallback available for container queries');
633
+
634
+ // Use media queries as last resort
635
+ const style = document.createElement('style');
636
+ style.textContent = `
637
+ /* Media query fallback for container queries */
638
+ @media (max-width: 400px) {
639
+ [data-container-fallback] {
640
+ --container-size: small;
641
+ }
642
+ }
643
+
644
+ @media (min-width: 401px) and (max-width: 800px) {
645
+ [data-container-fallback] {
646
+ --container-size: medium;
647
+ }
648
+ }
649
+
650
+ @media (min-width: 801px) {
651
+ [data-container-fallback] {
652
+ --container-size: large;
653
+ }
654
+ }
655
+ `;
656
+
657
+ document.head.appendChild(style);
658
+ }
659
+ }
660
+
661
+ /**
662
+ * Clamp function fallback
663
+ */
664
+ private clampFallback(): void {
665
+ // Replace clamp() with calc() and media queries
666
+ logger.info('Using calc() and media queries fallback for clamp()');
667
+
668
+ const style = document.createElement('style');
669
+ style.id = 'clamp-fallback';
670
+ style.textContent = `
671
+ /* Clamp fallback styles */
672
+ .proteus-clamp {
673
+ font-size: var(--min-size, 16px);
674
+ }
675
+
676
+ @media (min-width: 320px) {
677
+ .proteus-clamp {
678
+ font-size: calc(var(--min-size, 16px) + (var(--max-size, 24px) - var(--min-size, 16px)) * ((100vw - 320px) / (1200px - 320px)));
679
+ }
680
+ }
681
+
682
+ @media (min-width: 1200px) {
683
+ .proteus-clamp {
684
+ font-size: var(--max-size, 24px);
685
+ }
686
+ }
687
+ `;
688
+
689
+ document.head.appendChild(style);
690
+ }
691
+
692
+ /**
693
+ * Custom properties fallback
694
+ */
695
+ private customPropertiesFallback(): void {
696
+ logger.info('Using JavaScript fallback for CSS custom properties');
697
+
698
+ // Create a simple CSS custom properties polyfill
699
+ const customPropertiesMap = new Map<string, string>();
700
+
701
+ // Parse existing custom properties
702
+ const parseCustomProperties = (element: Element) => {
703
+ const computedStyle = window.getComputedStyle(element);
704
+ const cssText = (element as HTMLElement).style.cssText;
705
+
706
+ // Extract custom properties from inline styles
707
+ const customPropRegex = /--([\w-]+):\s*([^;]+)/g;
708
+ let match;
709
+
710
+ while ((match = customPropRegex.exec(cssText)) !== null) {
711
+ if (match[1] && match[2]) {
712
+ customPropertiesMap.set(`--${match[1]}`, match[2].trim());
713
+ }
714
+ }
715
+ };
716
+
717
+ // Apply custom properties polyfill
718
+ const applyCustomProperties = (element: Element) => {
719
+ const htmlElement = element as HTMLElement;
720
+ const style = htmlElement.style;
721
+
722
+ // Replace var() functions with actual values
723
+ const cssText = style.cssText;
724
+ const varRegex = /var\((--[\w-]+)(?:,\s*([^)]+))?\)/g;
725
+
726
+ let updatedCssText = cssText;
727
+ let varMatch;
728
+
729
+ while ((varMatch = varRegex.exec(cssText)) !== null) {
730
+ const varName = varMatch[1];
731
+ const fallbackValue = varMatch[2] || '';
732
+ const value = customPropertiesMap.get(varName || '') || fallbackValue;
733
+
734
+ if (value) {
735
+ updatedCssText = updatedCssText.replace(varMatch[0], value);
736
+ }
737
+ }
738
+
739
+ if (updatedCssText !== cssText) {
740
+ htmlElement.style.cssText = updatedCssText;
741
+ }
742
+ };
743
+
744
+ // Setup mutation observer to handle dynamic changes
745
+ const observer = new MutationObserver((mutations) => {
746
+ mutations.forEach((mutation) => {
747
+ if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
748
+ const element = mutation.target as Element;
749
+ parseCustomProperties(element);
750
+ applyCustomProperties(element);
751
+ }
752
+ });
753
+ });
754
+
755
+ // Start observing
756
+ observer.observe(document.body, {
757
+ attributes: true,
758
+ subtree: true,
759
+ attributeFilter: ['style']
760
+ });
761
+
762
+ // Initial processing
763
+ document.querySelectorAll('*').forEach((element) => {
764
+ parseCustomProperties(element);
765
+ applyCustomProperties(element);
766
+ });
767
+ }
768
+
769
+ /**
770
+ * Intersection Observer fallback
771
+ */
772
+ private intersectionObserverFallback(): void {
773
+ logger.info('Using scroll event fallback for Intersection Observer');
774
+
775
+ // Create a polyfill using scroll events
776
+ class IntersectionObserverPolyfill {
777
+ private callback: IntersectionObserverCallback;
778
+ private elements: Set<Element> = new Set();
779
+ private rootMargin: string;
780
+ private threshold: number | number[];
781
+
782
+ constructor(callback: IntersectionObserverCallback, options: IntersectionObserverInit = {}) {
783
+ this.callback = callback;
784
+ this.rootMargin = options.rootMargin || '0px';
785
+ this.threshold = options.threshold || 0;
786
+
787
+ this.setupScrollListener();
788
+ }
789
+
790
+ observe(element: Element): void {
791
+ this.elements.add(element);
792
+ this.checkIntersection();
793
+ }
794
+
795
+ unobserve(element: Element): void {
796
+ this.elements.delete(element);
797
+ }
798
+
799
+ disconnect(): void {
800
+ this.elements.clear();
801
+ window.removeEventListener('scroll', this.handleScroll);
802
+ window.removeEventListener('resize', this.handleScroll);
803
+ }
804
+
805
+ private setupScrollListener(): void {
806
+ this.handleScroll = this.handleScroll.bind(this);
807
+ window.addEventListener('scroll', this.handleScroll, { passive: true });
808
+ window.addEventListener('resize', this.handleScroll, { passive: true });
809
+ }
810
+
811
+ private handleScroll = (): void => {
812
+ this.checkIntersection();
813
+ };
814
+
815
+ private checkIntersection(): void {
816
+ const entries: IntersectionObserverEntry[] = [];
817
+
818
+ this.elements.forEach((element) => {
819
+ const rect = element.getBoundingClientRect();
820
+ const windowHeight = window.innerHeight;
821
+ const windowWidth = window.innerWidth;
822
+
823
+ // Calculate intersection
824
+ const isIntersecting = rect.top < windowHeight &&
825
+ rect.bottom > 0 &&
826
+ rect.left < windowWidth &&
827
+ rect.right > 0;
828
+
829
+ // Calculate intersection ratio (simplified)
830
+ let intersectionRatio = 0;
831
+ if (isIntersecting) {
832
+ const visibleHeight = Math.min(rect.bottom, windowHeight) - Math.max(rect.top, 0);
833
+ const visibleWidth = Math.min(rect.right, windowWidth) - Math.max(rect.left, 0);
834
+ const visibleArea = visibleHeight * visibleWidth;
835
+ const totalArea = rect.height * rect.width;
836
+ intersectionRatio = totalArea > 0 ? visibleArea / totalArea : 0;
837
+ }
838
+
839
+ entries.push({
840
+ target: element,
841
+ isIntersecting,
842
+ intersectionRatio,
843
+ boundingClientRect: rect,
844
+ intersectionRect: rect, // Simplified
845
+ rootBounds: { top: 0, left: 0, bottom: windowHeight, right: windowWidth, width: windowWidth, height: windowHeight },
846
+ time: Date.now()
847
+ } as IntersectionObserverEntry);
848
+ });
849
+
850
+ if (entries.length > 0) {
851
+ this.callback(entries, this as any);
852
+ }
853
+ }
854
+ }
855
+
856
+ // Replace global IntersectionObserver if not available
857
+ if (!(window as any).IntersectionObserver) {
858
+ (window as any).IntersectionObserver = IntersectionObserverPolyfill;
859
+ }
860
+ }
861
+
862
+ /**
863
+ * Viewport units fallback
864
+ */
865
+ private viewportUnitsFallback(): void {
866
+ logger.info('Using JavaScript fallback for viewport units');
867
+
868
+ const updateViewportUnits = () => {
869
+ const vw = window.innerWidth / 100;
870
+ const vh = window.innerHeight / 100;
871
+
872
+ document.documentElement.style.setProperty('--vw', `${vw}px`);
873
+ document.documentElement.style.setProperty('--vh', `${vh}px`);
874
+ };
875
+
876
+ updateViewportUnits();
877
+ window.addEventListener('resize', updateViewportUnits);
878
+ }
879
+
880
+ /**
881
+ * Flexbox fallback
882
+ */
883
+ private flexboxFallback(): void {
884
+ logger.info('Using table/inline-block fallback for flexbox');
885
+
886
+ const style = document.createElement('style');
887
+ style.textContent = `
888
+ /* Flexbox fallback */
889
+ .proteus-flex {
890
+ display: table;
891
+ width: 100%;
892
+ }
893
+
894
+ .proteus-flex-item {
895
+ display: table-cell;
896
+ vertical-align: middle;
897
+ }
898
+ `;
899
+
900
+ document.head.appendChild(style);
901
+ }
902
+
903
+ /**
904
+ * Apply performance optimizations
905
+ */
906
+ private applyPerformanceOptimizations(): void {
907
+ // Optimize for older browsers
908
+ if (!this.browserInfo.supported) {
909
+ this.applyLegacyOptimizations();
910
+ }
911
+
912
+ // Optimize for mobile
913
+ if (this.browserInfo.mobile) {
914
+ this.applyMobileOptimizations();
915
+ }
916
+
917
+ // Optimize based on feature support
918
+ this.applyFeatureBasedOptimizations();
919
+ }
920
+
921
+ /**
922
+ * Apply legacy browser optimizations
923
+ */
924
+ private applyLegacyOptimizations(): void {
925
+ // Reduce animation complexity
926
+ document.documentElement.classList.add('legacy-browser');
927
+
928
+ const style = document.createElement('style');
929
+ style.textContent = `
930
+ .legacy-browser * {
931
+ animation-duration: 0.1s !important;
932
+ transition-duration: 0.1s !important;
933
+ }
934
+ `;
935
+
936
+ document.head.appendChild(style);
937
+ }
938
+
939
+ /**
940
+ * Apply mobile optimizations
941
+ */
942
+ private applyMobileOptimizations(): void {
943
+ // Optimize touch interactions
944
+ const style = document.createElement('style');
945
+ style.textContent = `
946
+ /* Mobile optimizations */
947
+ * {
948
+ -webkit-tap-highlight-color: transparent;
949
+ touch-action: manipulation;
950
+ }
951
+
952
+ button, a, [role="button"] {
953
+ min-height: 44px;
954
+ min-width: 44px;
955
+ }
956
+ `;
957
+
958
+ document.head.appendChild(style);
959
+ }
960
+
961
+ /**
962
+ * Apply feature-based optimizations
963
+ */
964
+ private applyFeatureBasedOptimizations(): void {
965
+ // Use hardware acceleration when available
966
+ if (this.featureSupport.transforms) {
967
+ document.documentElement.classList.add('transforms-supported');
968
+ }
969
+
970
+ // Optimize animations
971
+ if (!this.featureSupport.webAnimations) {
972
+ document.documentElement.classList.add('no-web-animations');
973
+ }
974
+ }
975
+
976
+ /**
977
+ * Setup modern features
978
+ */
979
+ private setupModernFeatures(): void {
980
+ // Enable modern features based on support
981
+ Object.entries(this.featureSupport).forEach(([feature, supported]) => {
982
+ if (supported) {
983
+ this.modernFeatures.add(feature);
984
+ document.documentElement.classList.add(`supports-${feature.replace(/([A-Z])/g, '-$1').toLowerCase()}`);
985
+ } else {
986
+ this.legacyFeatures.add(feature);
987
+ document.documentElement.classList.add(`no-${feature.replace(/([A-Z])/g, '-$1').toLowerCase()}`);
988
+ }
989
+ });
990
+ }
991
+
992
+ /**
993
+ * Get browser information
994
+ */
995
+ public getBrowserInfo(): BrowserInfo {
996
+ return { ...this.browserInfo };
997
+ }
998
+
999
+ /**
1000
+ * Get feature support information
1001
+ */
1002
+ public getFeatureSupport(): FeatureSupport {
1003
+ return { ...this.featureSupport };
1004
+ }
1005
+
1006
+ /**
1007
+ * Check if specific feature is supported
1008
+ */
1009
+ public isFeatureSupported(feature: keyof FeatureSupport): boolean {
1010
+ return this.featureSupport[feature];
1011
+ }
1012
+
1013
+ /**
1014
+ * Get compatibility report
1015
+ */
1016
+ public getCompatibilityReport(): {
1017
+ browser: BrowserInfo;
1018
+ features: FeatureSupport;
1019
+ polyfills: PolyfillInfo[];
1020
+ modernFeatures: string[];
1021
+ legacyFeatures: string[];
1022
+ recommendations: string[];
1023
+ } {
1024
+ const recommendations: string[] = [];
1025
+
1026
+ if (!this.browserInfo.supported) {
1027
+ recommendations.push('Consider upgrading to a modern browser for better performance');
1028
+ }
1029
+
1030
+ if (this.legacyFeatures.size > 5) {
1031
+ recommendations.push('Many modern features are not supported - consider using polyfills');
1032
+ }
1033
+
1034
+ if (this.browserInfo.name === 'Internet Explorer') {
1035
+ recommendations.push('Internet Explorer support is limited - consider migrating to Edge');
1036
+ }
1037
+
1038
+ return {
1039
+ browser: this.getBrowserInfo(),
1040
+ features: this.getFeatureSupport(),
1041
+ polyfills: Array.from(this.polyfills.values()),
1042
+ modernFeatures: Array.from(this.modernFeatures),
1043
+ legacyFeatures: Array.from(this.legacyFeatures),
1044
+ recommendations
1045
+ };
1046
+ }
1047
+
1048
+ /**
1049
+ * Enable graceful degradation for specific feature
1050
+ */
1051
+ public enableGracefulDegradation(feature: string, fallback: () => void): void {
1052
+ this.fallbacks.set(feature, fallback);
1053
+
1054
+ // Apply fallback if feature is not supported
1055
+ if (!this.modernFeatures.has(feature)) {
1056
+ fallback();
1057
+ }
1058
+ }
1059
+
1060
+ /**
1061
+ * Cleanup compatibility system
1062
+ */
1063
+ public destroy(): void {
1064
+ // Remove added styles
1065
+ const styles = document.querySelectorAll('style[id*="proteus"], style[id*="clamp-fallback"]');
1066
+ styles.forEach(style => style.remove());
1067
+
1068
+ // Clear maps
1069
+ this.polyfills.clear();
1070
+ this.fallbacks.clear();
1071
+ this.modernFeatures.clear();
1072
+ this.legacyFeatures.clear();
1073
+
1074
+ logger.info('Browser compatibility system destroyed');
1075
+ }
1076
+ }