@rakeyshgidwani/roger-ui-bank-theme-harvey 0.2.52 → 0.3.1

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 (131) 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/layouts/adaptive-layout.d.ts +1 -0
  19. package/dist/components/ui/layouts/adaptive-layout.d.ts.map +1 -1
  20. package/dist/components/ui/layouts/adaptive-layout.esm.js +2 -2
  21. package/dist/components/ui/layouts/adaptive-layout.js +2 -2
  22. package/dist/components/ui/navigation/index.d.ts +2 -1
  23. package/dist/components/ui/navigation/index.d.ts.map +1 -1
  24. package/dist/components/ui/navigation/index.esm.js +1 -0
  25. package/dist/components/ui/navigation/index.js +1 -0
  26. package/dist/components/ui/navigation/progressive-navigation.d.ts +37 -0
  27. package/dist/components/ui/navigation/progressive-navigation.d.ts.map +1 -0
  28. package/dist/components/ui/navigation/progressive-navigation.esm.js +145 -0
  29. package/dist/components/ui/navigation/progressive-navigation.js +145 -0
  30. package/dist/components/ui/navigation/types.d.ts +21 -0
  31. package/dist/components/ui/navigation/types.d.ts.map +1 -1
  32. package/dist/hooks/use-adaptive-layout.d.ts +2 -1
  33. package/dist/hooks/use-adaptive-layout.d.ts.map +1 -1
  34. package/dist/hooks/use-adaptive-layout.esm.js +13 -8
  35. package/dist/hooks/use-adaptive-layout.js +13 -8
  36. package/dist/hooks/use-device.d.ts +3 -1
  37. package/dist/hooks/use-device.d.ts.map +1 -1
  38. package/dist/hooks/use-device.esm.js +14 -7
  39. package/dist/hooks/use-device.js +14 -7
  40. package/dist/index.d.ts +19 -4
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.esm.js +9 -4
  43. package/dist/index.js +9 -4
  44. package/dist/plugins/css-purge-optimizer.d.ts +25 -0
  45. package/dist/plugins/css-purge-optimizer.d.ts.map +1 -0
  46. package/dist/plugins/css-purge-optimizer.esm.js +414 -0
  47. package/dist/plugins/css-purge-optimizer.js +414 -0
  48. package/dist/plugins/performance-monitor.d.ts +29 -0
  49. package/dist/plugins/performance-monitor.d.ts.map +1 -0
  50. package/dist/plugins/performance-monitor.esm.js +221 -0
  51. package/dist/plugins/performance-monitor.js +221 -0
  52. package/dist/plugins/progressive-css-loader.d.ts +21 -0
  53. package/dist/plugins/progressive-css-loader.d.ts.map +1 -0
  54. package/dist/plugins/progressive-css-loader.esm.js +227 -0
  55. package/dist/plugins/progressive-css-loader.js +227 -0
  56. package/dist/plugins/theme-css-generator.d.ts.map +1 -1
  57. package/dist/plugins/theme-css-generator.esm.js +19 -6
  58. package/dist/plugins/theme-css-generator.js +19 -6
  59. package/dist/styles.css +1027 -112
  60. package/dist/theme.d.ts.map +1 -1
  61. package/dist/theme.esm.js +4 -1
  62. package/dist/theme.js +4 -1
  63. package/dist/themes/phase1-constants.d.ts +23 -0
  64. package/dist/themes/phase1-constants.d.ts.map +1 -0
  65. package/dist/themes/phase1-constants.esm.js +180 -0
  66. package/dist/themes/phase1-constants.js +180 -0
  67. package/dist/themes/themes/default.d.ts.map +1 -1
  68. package/dist/themes/themes/default.esm.js +4 -1
  69. package/dist/themes/themes/default.js +4 -1
  70. package/dist/themes/themes/harvey.d.ts.map +1 -1
  71. package/dist/themes/themes/harvey.esm.js +4 -1
  72. package/dist/themes/themes/harvey.js +4 -1
  73. package/dist/themes/types.d.ts +62 -0
  74. package/dist/themes/types.d.ts.map +1 -1
  75. package/dist/themes/validation.d.ts +17 -0
  76. package/dist/themes/validation.d.ts.map +1 -1
  77. package/dist/themes/validation.esm.js +218 -0
  78. package/dist/themes/validation.js +218 -0
  79. package/dist/types.d.ts +62 -0
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/utils/progressive-css-injector.d.ts +80 -0
  82. package/dist/utils/progressive-css-injector.d.ts.map +1 -0
  83. package/dist/utils/progressive-css-injector.esm.js +217 -0
  84. package/dist/utils/progressive-css-injector.js +217 -0
  85. package/package.json +1 -1
  86. package/src/components/ui/button.tsx +9 -6
  87. package/src/components/ui/layout/container.tsx +312 -0
  88. package/src/components/ui/layout/index.ts +10 -0
  89. package/src/components/ui/layout/responsive-grid.tsx +286 -0
  90. package/src/components/ui/layouts/adaptive-layout.tsx +3 -1
  91. package/src/components/ui/navigation/index.ts +2 -0
  92. package/src/components/ui/navigation/progressive-navigation.tsx +453 -0
  93. package/src/components/ui/navigation/types.ts +41 -0
  94. package/src/hooks/use-adaptive-layout.ts +13 -9
  95. package/src/hooks/use-device.tsx +17 -10
  96. package/src/index.ts +19 -4
  97. package/src/plugins/css-purge-optimizer.ts +491 -0
  98. package/src/plugins/performance-monitor.ts +292 -0
  99. package/src/plugins/progressive-css-loader.ts +269 -0
  100. package/src/plugins/theme-css-generator.ts +22 -6
  101. package/src/styles/components/base/badge.css +2 -2
  102. package/src/styles/components/base/button.css +238 -35
  103. package/src/styles/components/base/card.css +2 -2
  104. package/src/styles/components/base/checkbox.css +3 -3
  105. package/src/styles/components/base/label.css +3 -3
  106. package/src/styles/components/feedback/skeleton.css +1 -1
  107. package/src/styles/components/feedback/toast.css +1 -1
  108. package/src/styles/components/index.css +3 -0
  109. package/src/styles/components/layout/container.css +466 -0
  110. package/src/styles/components/layout/index.css +5 -0
  111. package/src/styles/components/layout/responsive-grid.css +422 -0
  112. package/src/styles/components/navigation/breadcrumb.css +1 -1
  113. package/src/styles/components/navigation/index.css +1 -0
  114. package/src/styles/components/navigation/menu.css +2 -2
  115. package/src/styles/components/navigation/pagination.css +4 -4
  116. package/src/styles/components/navigation/progressive-navigation.css +633 -0
  117. package/src/styles/components/navigation/sidebar.css +4 -4
  118. package/src/styles/components/navigation/stepper.css +2 -2
  119. package/src/styles/components/navigation/tabs.css +1 -1
  120. package/src/styles/components/ui/theme-toggle.css +2 -2
  121. package/src/styles/progressive.css +17 -0
  122. package/src/styles/themes/harvey.css +103 -19
  123. package/src/styles/utilities/semantic-input-system.css +7 -13
  124. package/src/theme.ts +5 -1
  125. package/src/themes/phase1-constants.ts +189 -0
  126. package/src/themes/themes/default.ts +5 -1
  127. package/src/themes/themes/harvey.ts +5 -1
  128. package/src/themes/types.ts +77 -1
  129. package/src/themes/validation.ts +249 -0
  130. package/src/types.ts +77 -1
  131. package/src/utils/progressive-css-injector.ts +254 -0
@@ -29,6 +29,32 @@ export class ThemeValidator {
29
29
  'meta.version'
30
30
  ];
31
31
 
32
+ // Phase 1 Enhancement: Required fields for new foundation primitives
33
+ private static readonly PHASE1_REQUIRED_FIELDS = [
34
+ // Enhanced Breakpoint System
35
+ 'breakpoints.xs',
36
+ 'breakpoints.sm',
37
+ 'breakpoints.md',
38
+ 'breakpoints.lg',
39
+ 'breakpoints.xl',
40
+ 'breakpoints.2xl',
41
+ 'breakpoints.3xl',
42
+
43
+ // Content Density System
44
+ 'contentDensity.compact.spacing.section',
45
+ 'contentDensity.compact.typography.scale',
46
+ 'contentDensity.comfortable.spacing.section',
47
+ 'contentDensity.comfortable.typography.scale',
48
+ 'contentDensity.spacious.spacing.section',
49
+ 'contentDensity.spacious.typography.scale',
50
+
51
+ // Responsive Typography System
52
+ 'responsiveTypography.heading1.xs',
53
+ 'responsiveTypography.heading1.3xl',
54
+ 'responsiveTypography.body.xs',
55
+ 'responsiveTypography.body.3xl'
56
+ ];
57
+
32
58
  private static readonly COLOR_FORMATS = {
33
59
  hex: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
34
60
  rgb: /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/,
@@ -49,6 +75,12 @@ export class ThemeValidator {
49
75
  // Validate required fields
50
76
  this.validateRequiredFields(theme, errors);
51
77
 
78
+ // Phase 1 Enhancement: Validate new foundation primitives
79
+ this.validatePhase1RequiredFields(theme, errors);
80
+ this.validateBreakpointSystem(theme, errors, warnings);
81
+ this.validateContentDensitySystem(theme, errors, warnings);
82
+ this.validateResponsiveTypographySystem(theme, errors, warnings);
83
+
52
84
  // Validate color formats
53
85
  this.validateColorFormats(theme, errors, warnings);
54
86
 
@@ -459,4 +491,221 @@ export class ThemeValidator {
459
491
  return false;
460
492
  }
461
493
  }
494
+
495
+ // Phase 1 Enhancement: New validation methods for foundation primitives
496
+
497
+ /**
498
+ * Validate Phase 1 required fields
499
+ */
500
+ private static validatePhase1RequiredFields(
501
+ theme: MultiThemeConfig,
502
+ errors: ThemeValidationError[]
503
+ ): void {
504
+ this.PHASE1_REQUIRED_FIELDS.forEach(fieldPath => {
505
+ const value = this.getNestedValue(theme, fieldPath);
506
+ if (value === undefined || value === null || value === '') {
507
+ errors.push({
508
+ path: fieldPath,
509
+ message: `Required Phase 1 field '${fieldPath}' is missing`,
510
+ severity: 'critical'
511
+ });
512
+ }
513
+ });
514
+ }
515
+
516
+ /**
517
+ * Validate enhanced breakpoint system
518
+ */
519
+ private static validateBreakpointSystem(
520
+ theme: MultiThemeConfig,
521
+ errors: ThemeValidationError[],
522
+ warnings: ThemeValidationWarning[]
523
+ ): void {
524
+ if (!theme.breakpoints) {
525
+ errors.push({
526
+ path: 'breakpoints',
527
+ message: 'Breakpoint system is required for Phase 1 compliance',
528
+ severity: 'critical'
529
+ });
530
+ return;
531
+ }
532
+
533
+ const requiredBreakpoints = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
534
+ const expectedValues = {
535
+ xs: '475px',
536
+ sm: '640px',
537
+ md: '768px',
538
+ lg: '1024px',
539
+ xl: '1280px',
540
+ '2xl': '1536px',
541
+ '3xl': '1920px'
542
+ };
543
+
544
+ requiredBreakpoints.forEach(breakpoint => {
545
+ const value = theme.breakpoints![breakpoint as keyof typeof theme.breakpoints];
546
+ if (!value) {
547
+ errors.push({
548
+ path: `breakpoints.${breakpoint}`,
549
+ message: `Breakpoint '${breakpoint}' is required`,
550
+ severity: 'error'
551
+ });
552
+ } else {
553
+ // Validate format (should end with 'px')
554
+ if (!value.endsWith('px')) {
555
+ warnings.push({
556
+ path: `breakpoints.${breakpoint}`,
557
+ message: `Breakpoint '${breakpoint}' should use px units`,
558
+ severity: 'warning'
559
+ });
560
+ }
561
+
562
+ // Validate expected values for consistency
563
+ if (value !== expectedValues[breakpoint as keyof typeof expectedValues]) {
564
+ warnings.push({
565
+ path: `breakpoints.${breakpoint}`,
566
+ message: `Breakpoint '${breakpoint}' value '${value}' differs from specification '${expectedValues[breakpoint as keyof typeof expectedValues]}'`,
567
+ severity: 'info'
568
+ });
569
+ }
570
+ }
571
+ });
572
+ }
573
+
574
+ /**
575
+ * Validate content density system
576
+ */
577
+ private static validateContentDensitySystem(
578
+ theme: MultiThemeConfig,
579
+ errors: ThemeValidationError[],
580
+ warnings: ThemeValidationWarning[]
581
+ ): void {
582
+ if (!theme.contentDensity) {
583
+ errors.push({
584
+ path: 'contentDensity',
585
+ message: 'Content density system is required for Phase 1 compliance',
586
+ severity: 'critical'
587
+ });
588
+ return;
589
+ }
590
+
591
+ const densityLevels = ['compact', 'comfortable', 'spacious'];
592
+
593
+ densityLevels.forEach(level => {
594
+ const density = theme.contentDensity![level as keyof typeof theme.contentDensity];
595
+ if (!density) {
596
+ errors.push({
597
+ path: `contentDensity.${level}`,
598
+ message: `Density level '${level}' is required`,
599
+ severity: 'error'
600
+ });
601
+ return;
602
+ }
603
+
604
+ // Validate spacing structure
605
+ if (!density.spacing) {
606
+ errors.push({
607
+ path: `contentDensity.${level}.spacing`,
608
+ message: `Spacing configuration is required for density level '${level}'`,
609
+ severity: 'error'
610
+ });
611
+ } else {
612
+ ['section', 'component', 'element'].forEach(spacingType => {
613
+ const spacingValue = density.spacing[spacingType as keyof typeof density.spacing];
614
+ if (!spacingValue) {
615
+ warnings.push({
616
+ path: `contentDensity.${level}.spacing.${spacingType}`,
617
+ message: `Spacing type '${spacingType}' is recommended for density level '${level}'`,
618
+ severity: 'warning'
619
+ });
620
+ }
621
+ });
622
+ }
623
+
624
+ // Validate typography structure
625
+ if (!density.typography) {
626
+ errors.push({
627
+ path: `contentDensity.${level}.typography`,
628
+ message: `Typography configuration is required for density level '${level}'`,
629
+ severity: 'error'
630
+ });
631
+ } else {
632
+ if (typeof density.typography.scale !== 'number') {
633
+ errors.push({
634
+ path: `contentDensity.${level}.typography.scale`,
635
+ message: `Typography scale must be a number for density level '${level}'`,
636
+ severity: 'error'
637
+ });
638
+ }
639
+ if (typeof density.typography.lineHeight !== 'number') {
640
+ errors.push({
641
+ path: `contentDensity.${level}.typography.lineHeight`,
642
+ message: `Typography lineHeight must be a number for density level '${level}'`,
643
+ severity: 'error'
644
+ });
645
+ }
646
+ }
647
+
648
+ // Validate touch targets for mobile-focused densities
649
+ if (level === 'compact' && !density.touchTargets) {
650
+ warnings.push({
651
+ path: `contentDensity.${level}.touchTargets`,
652
+ message: `Touch targets configuration is recommended for compact density level`,
653
+ severity: 'warning'
654
+ });
655
+ }
656
+ });
657
+ }
658
+
659
+ /**
660
+ * Validate responsive typography system
661
+ */
662
+ private static validateResponsiveTypographySystem(
663
+ theme: MultiThemeConfig,
664
+ errors: ThemeValidationError[],
665
+ warnings: ThemeValidationWarning[]
666
+ ): void {
667
+ if (!theme.responsiveTypography) {
668
+ errors.push({
669
+ path: 'responsiveTypography',
670
+ message: 'Responsive typography system is required for Phase 1 compliance',
671
+ severity: 'critical'
672
+ });
673
+ return;
674
+ }
675
+
676
+ const typographyLevels = ['heading1', 'heading2', 'heading3', 'heading4', 'heading5', 'heading6', 'body', 'bodyLarge', 'bodySmall', 'caption'];
677
+ const breakpoints = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
678
+
679
+ typographyLevels.forEach(level => {
680
+ const typography = theme.responsiveTypography![level as keyof typeof theme.responsiveTypography];
681
+ if (!typography) {
682
+ warnings.push({
683
+ path: `responsiveTypography.${level}`,
684
+ message: `Typography level '${level}' is recommended for complete responsive scaling`,
685
+ severity: 'warning'
686
+ });
687
+ return;
688
+ }
689
+
690
+ breakpoints.forEach(breakpoint => {
691
+ const value = typography[breakpoint as keyof typeof typography];
692
+ if (!value) {
693
+ warnings.push({
694
+ path: `responsiveTypography.${level}.${breakpoint}`,
695
+ message: `Breakpoint '${breakpoint}' is missing for typography level '${level}'`,
696
+ severity: 'warning'
697
+ });
698
+ } else {
699
+ // Validate format (should end with 'px' or be a number)
700
+ if (typeof value === 'string' && !value.endsWith('px') && !value.endsWith('rem') && !value.endsWith('em')) {
701
+ warnings.push({
702
+ path: `responsiveTypography.${level}.${breakpoint}`,
703
+ message: `Typography value should include units (px, rem, or em) for '${level}.${breakpoint}'`,
704
+ severity: 'warning'
705
+ });
706
+ }
707
+ }
708
+ });
709
+ });
710
+ }
462
711
  }
package/src/types.ts CHANGED
@@ -22,7 +22,12 @@ export interface MultiThemeConfig {
22
22
  shimmer: ShimmerThemeConfig;
23
23
  meta: ThemeMetadata;
24
24
  extends?: string; // Optional property for theme inheritance
25
-
25
+
26
+ // Phase 1 Enhancements: Breakpoint and Content Density Systems
27
+ breakpoints?: BreakpointThemeConfig;
28
+ contentDensity?: ContentDensityThemeConfig;
29
+ responsiveTypography?: ResponsiveTypographyThemeConfig;
30
+
26
31
  // NEW: Add light/dark mode configuration (colors only)
27
32
  modes?: {
28
33
  light?: {
@@ -462,3 +467,74 @@ export interface CompleteThemeConfig extends MultiThemeConfig {
462
467
  inheritance?: ThemeInheritance;
463
468
  validation: ThemeValidationResult;
464
469
  }
470
+
471
+ // Phase 1 Enhancement: Enhanced Breakpoint System
472
+ export interface BreakpointThemeConfig {
473
+ xs: string; // 475px - Large phones (iPhone 14 Plus, etc.)
474
+ sm: string; // 640px - Small tablets, large phones landscape
475
+ md: string; // 768px - Tablets (iPad Mini, etc.)
476
+ lg: string; // 1024px - Desktop, large tablets
477
+ xl: string; // 1280px - Large desktop (MacBook Pro 13")
478
+ '2xl': string; // 1536px - Ultra-wide (MacBook Pro 16", external monitors)
479
+ '3xl': string; // 1920px - Large external monitors, TV displays
480
+ }
481
+
482
+ // Phase 1 Enhancement: Content Density System
483
+ export interface ContentDensityThemeConfig {
484
+ compact: DensityLevel; // xs to md breakpoints
485
+ comfortable: DensityLevel; // md to lg breakpoints
486
+ spacious: DensityLevel; // lg+ breakpoints
487
+ }
488
+
489
+ export interface DensityLevel {
490
+ spacing: DensitySpacing;
491
+ typography: DensityTypography;
492
+ touchTargets?: DensityTouchTargets;
493
+ interactions?: DensityInteractions;
494
+ }
495
+
496
+ export interface DensitySpacing {
497
+ section: string; // Section-level spacing
498
+ component: string; // Component-level spacing
499
+ element: string; // Element-level spacing
500
+ }
501
+
502
+ export interface DensityTypography {
503
+ scale: number; // Typography scale multiplier
504
+ lineHeight: number; // Line height ratio
505
+ }
506
+
507
+ export interface DensityTouchTargets {
508
+ minimum: string; // Minimum touch target size
509
+ preferred: string; // Preferred touch target size
510
+ }
511
+
512
+ export interface DensityInteractions {
513
+ hover?: boolean; // Enable hover states
514
+ tooltips?: boolean; // Enable tooltips
515
+ shortcuts?: boolean; // Enable keyboard shortcuts
516
+ }
517
+
518
+ // Phase 1 Enhancement: Responsive Typography System
519
+ export interface ResponsiveTypographyThemeConfig {
520
+ heading1: ResponsiveTypographyScale;
521
+ heading2: ResponsiveTypographyScale;
522
+ heading3: ResponsiveTypographyScale;
523
+ heading4: ResponsiveTypographyScale;
524
+ heading5: ResponsiveTypographyScale;
525
+ heading6: ResponsiveTypographyScale;
526
+ body: ResponsiveTypographyScale;
527
+ bodyLarge: ResponsiveTypographyScale;
528
+ bodySmall: ResponsiveTypographyScale;
529
+ caption: ResponsiveTypographyScale;
530
+ }
531
+
532
+ export interface ResponsiveTypographyScale {
533
+ xs: string; // Large phone
534
+ sm: string; // Small tablet
535
+ md: string; // Tablet
536
+ lg: string; // Desktop
537
+ xl: string; // Large desktop
538
+ '2xl': string; // Ultra-wide
539
+ '3xl': string; // Large displays
540
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Progressive CSS Injector
3
+ * Runtime utility to load progressive CSS for desktop enhancement
4
+ *
5
+ * This is a simplified, production-ready approach that:
6
+ * 1. Detects desktop viewport (>= 1024px)
7
+ * 2. Dynamically loads progressive.css
8
+ * 3. Handles viewport changes and preloading
9
+ */
10
+
11
+ interface ProgressiveLoaderConfig {
12
+ desktopBreakpoint: number
13
+ progressiveCSSPath: string
14
+ enablePreload: boolean
15
+ enableViewportDetection: boolean
16
+ }
17
+
18
+ const DEFAULT_CONFIG: ProgressiveLoaderConfig = {
19
+ desktopBreakpoint: 1024, // lg breakpoint
20
+ progressiveCSSPath: '/progressive.css',
21
+ enablePreload: true,
22
+ enableViewportDetection: true
23
+ }
24
+
25
+ class ProgressiveCSSInjector {
26
+ private config: ProgressiveLoaderConfig
27
+ private isLoaded = false
28
+ private linkElement: HTMLLinkElement | null = null
29
+ private preloadElement: HTMLLinkElement | null = null
30
+
31
+ constructor(config: Partial<ProgressiveLoaderConfig> = {}) {
32
+ this.config = { ...DEFAULT_CONFIG, ...config }
33
+ }
34
+
35
+ /**
36
+ * Initialize the progressive CSS loader
37
+ */
38
+ init(): void {
39
+ if (typeof window === 'undefined') return // SSR safety
40
+
41
+ // Only run in production or when explicitly enabled
42
+ // In development, progressive.css likely doesn't exist
43
+ const isProduction = typeof import.meta !== 'undefined' && (import.meta as any).env?.PROD === true
44
+ const isDevelopment = typeof import.meta !== 'undefined' && (import.meta as any).env?.DEV === true
45
+
46
+ if (isDevelopment && !this.config.enableViewportDetection) {
47
+ console.log('Progressive CSS: Skipping in development (set enableViewportDetection: true to override)')
48
+ return
49
+ }
50
+
51
+ // In production, check if progressive CSS file exists
52
+ if (isProduction) {
53
+ this.checkFileExists()
54
+ .then(exists => {
55
+ if (!exists) {
56
+ console.log('Progressive CSS file not found, skipping enhancement')
57
+ return
58
+ }
59
+ this.initializeFeatures()
60
+ })
61
+ .catch(error => {
62
+ console.warn('Progressive CSS loader initialization failed:', error)
63
+ })
64
+ } else {
65
+ // In development with explicit enablement, skip file check
66
+ this.initializeFeatures()
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Initialize progressive CSS features
72
+ */
73
+ private initializeFeatures(): void {
74
+ // Initial viewport check
75
+ this.checkAndLoadProgressiveCSS()
76
+
77
+ // Setup viewport change listener if enabled
78
+ if (this.config.enableViewportDetection) {
79
+ this.setupViewportListener()
80
+ }
81
+
82
+ // Setup preloading if enabled
83
+ if (this.config.enablePreload) {
84
+ this.setupPreloading()
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Check if the progressive CSS file exists
90
+ */
91
+ private async checkFileExists(): Promise<boolean> {
92
+ try {
93
+ const response = await fetch(this.config.progressiveCSSPath, { method: 'HEAD' })
94
+ return response.ok
95
+ } catch {
96
+ return false
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Check viewport and load progressive CSS if needed
102
+ */
103
+ private checkAndLoadProgressiveCSS(): void {
104
+ const isDesktop = window.innerWidth >= this.config.desktopBreakpoint
105
+
106
+ if (isDesktop && !this.isLoaded) {
107
+ this.loadProgressiveCSS()
108
+ } else if (!isDesktop && this.isLoaded) {
109
+ this.unloadProgressiveCSS()
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Load progressive CSS
115
+ */
116
+ private loadProgressiveCSS(): void {
117
+ if (this.isLoaded || this.linkElement) return
118
+
119
+ this.linkElement = document.createElement('link')
120
+ this.linkElement.rel = 'stylesheet'
121
+ this.linkElement.href = this.config.progressiveCSSPath
122
+ this.linkElement.media = `screen and (min-width: ${this.config.desktopBreakpoint}px)`
123
+
124
+ this.linkElement.onload = () => {
125
+ this.isLoaded = true
126
+ console.log(`Progressive CSS loaded for viewport >= ${this.config.desktopBreakpoint}px`)
127
+ }
128
+
129
+ this.linkElement.onerror = () => {
130
+ console.warn('Failed to load progressive CSS')
131
+ this.cleanup()
132
+ }
133
+
134
+ document.head.appendChild(this.linkElement)
135
+ }
136
+
137
+ /**
138
+ * Unload progressive CSS (for mobile/tablet)
139
+ */
140
+ private unloadProgressiveCSS(): void {
141
+ if (this.linkElement) {
142
+ document.head.removeChild(this.linkElement)
143
+ this.linkElement = null
144
+ this.isLoaded = false
145
+ console.log('Progressive CSS unloaded for mobile/tablet viewport')
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Setup viewport change listener
151
+ */
152
+ private setupViewportListener(): void {
153
+ let resizeTimeout: number
154
+
155
+ const handleResize = () => {
156
+ clearTimeout(resizeTimeout)
157
+ resizeTimeout = window.setTimeout(() => {
158
+ this.checkAndLoadProgressiveCSS()
159
+ }, 150) // Debounce resize events
160
+ }
161
+
162
+ window.addEventListener('resize', handleResize)
163
+ }
164
+
165
+ /**
166
+ * Setup preloading for fast desktop switches
167
+ */
168
+ private setupPreloading(): void {
169
+ // Only preload on tablet+ to avoid mobile data usage
170
+ if (window.innerWidth >= 768) {
171
+ this.preloadElement = document.createElement('link')
172
+ this.preloadElement.rel = 'preload'
173
+ this.preloadElement.href = this.config.progressiveCSSPath
174
+ this.preloadElement.as = 'style'
175
+
176
+ document.head.appendChild(this.preloadElement)
177
+ console.log('Progressive CSS preloaded for fast desktop enhancement')
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Cleanup resources
183
+ */
184
+ private cleanup(): void {
185
+ if (this.linkElement) {
186
+ this.linkElement = null
187
+ }
188
+ if (this.preloadElement) {
189
+ document.head.removeChild(this.preloadElement)
190
+ this.preloadElement = null
191
+ }
192
+ this.isLoaded = false
193
+ }
194
+
195
+ /**
196
+ * Force load progressive CSS (for testing/debugging)
197
+ */
198
+ forceLoad(): void {
199
+ this.loadProgressiveCSS()
200
+ }
201
+
202
+ /**
203
+ * Force unload progressive CSS (for testing/debugging)
204
+ */
205
+ forceUnload(): void {
206
+ this.unloadProgressiveCSS()
207
+ }
208
+
209
+ /**
210
+ * Get current load status
211
+ */
212
+ getStatus() {
213
+ return {
214
+ isLoaded: this.isLoaded,
215
+ hasLinkElement: !!this.linkElement,
216
+ hasPreloadElement: !!this.preloadElement,
217
+ currentViewport: window.innerWidth,
218
+ isDesktopViewport: window.innerWidth >= this.config.desktopBreakpoint
219
+ }
220
+ }
221
+ }
222
+
223
+ // Export singleton instance for easy use
224
+ export const progressiveLoader = new ProgressiveCSSInjector()
225
+
226
+ // Export class for custom instances
227
+ export { ProgressiveCSSInjector }
228
+
229
+ // Export initialization function for manual control
230
+ export function initializeProgressiveCSS(config?: Partial<ProgressiveLoaderConfig>): void {
231
+ // Only initialize in browser environment
232
+ if (typeof window === 'undefined') {
233
+ console.log('Progressive CSS: Skipping initialization (non-browser environment)')
234
+ return
235
+ }
236
+
237
+ // Respect production gating by default
238
+ const isProduction = typeof import.meta !== 'undefined' && (import.meta as any).env?.PROD === true
239
+ const isDevelopment = typeof import.meta !== 'undefined' && (import.meta as any).env?.DEV === true
240
+
241
+ if (isDevelopment && !config?.enableViewportDetection) {
242
+ console.log('Progressive CSS: Skipping in development (pass enableViewportDetection: true to override)')
243
+ return
244
+ }
245
+
246
+ void isProduction // Use the variable to avoid unused warning
247
+
248
+ if (config) {
249
+ const customLoader = new ProgressiveCSSInjector(config)
250
+ customLoader.init()
251
+ } else {
252
+ progressiveLoader.init()
253
+ }
254
+ }