@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.
- package/CHANGELOG.md +1 -1
- package/dist/components/ui/button.d.ts +3 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.esm.js +3 -2
- package/dist/components/ui/button.js +3 -2
- package/dist/components/ui/layout/container.d.ts +57 -0
- package/dist/components/ui/layout/container.d.ts.map +1 -0
- package/dist/components/ui/layout/container.esm.js +173 -0
- package/dist/components/ui/layout/container.js +173 -0
- package/dist/components/ui/layout/index.d.ts +9 -0
- package/dist/components/ui/layout/index.d.ts.map +1 -0
- package/dist/components/ui/layout/index.esm.js +6 -0
- package/dist/components/ui/layout/index.js +6 -0
- package/dist/components/ui/layout/responsive-grid.d.ts +93 -0
- package/dist/components/ui/layout/responsive-grid.d.ts.map +1 -0
- package/dist/components/ui/layout/responsive-grid.esm.js +124 -0
- package/dist/components/ui/layout/responsive-grid.js +124 -0
- package/dist/components/ui/layouts/adaptive-layout.d.ts +1 -0
- package/dist/components/ui/layouts/adaptive-layout.d.ts.map +1 -1
- package/dist/components/ui/layouts/adaptive-layout.esm.js +2 -2
- package/dist/components/ui/layouts/adaptive-layout.js +2 -2
- package/dist/components/ui/navigation/index.d.ts +2 -1
- package/dist/components/ui/navigation/index.d.ts.map +1 -1
- package/dist/components/ui/navigation/index.esm.js +1 -0
- package/dist/components/ui/navigation/index.js +1 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts +37 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts.map +1 -0
- package/dist/components/ui/navigation/progressive-navigation.esm.js +145 -0
- package/dist/components/ui/navigation/progressive-navigation.js +145 -0
- package/dist/components/ui/navigation/types.d.ts +21 -0
- package/dist/components/ui/navigation/types.d.ts.map +1 -1
- package/dist/hooks/use-adaptive-layout.d.ts +2 -1
- package/dist/hooks/use-adaptive-layout.d.ts.map +1 -1
- package/dist/hooks/use-adaptive-layout.esm.js +13 -8
- package/dist/hooks/use-adaptive-layout.js +13 -8
- package/dist/hooks/use-device.d.ts +3 -1
- package/dist/hooks/use-device.d.ts.map +1 -1
- package/dist/hooks/use-device.esm.js +14 -7
- package/dist/hooks/use-device.js +14 -7
- package/dist/index.d.ts +19 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +9 -4
- package/dist/index.js +9 -4
- package/dist/plugins/css-purge-optimizer.d.ts +25 -0
- package/dist/plugins/css-purge-optimizer.d.ts.map +1 -0
- package/dist/plugins/css-purge-optimizer.esm.js +414 -0
- package/dist/plugins/css-purge-optimizer.js +414 -0
- package/dist/plugins/performance-monitor.d.ts +29 -0
- package/dist/plugins/performance-monitor.d.ts.map +1 -0
- package/dist/plugins/performance-monitor.esm.js +221 -0
- package/dist/plugins/performance-monitor.js +221 -0
- package/dist/plugins/progressive-css-loader.d.ts +21 -0
- package/dist/plugins/progressive-css-loader.d.ts.map +1 -0
- package/dist/plugins/progressive-css-loader.esm.js +227 -0
- package/dist/plugins/progressive-css-loader.js +227 -0
- package/dist/plugins/theme-css-generator.d.ts.map +1 -1
- package/dist/plugins/theme-css-generator.esm.js +19 -6
- package/dist/plugins/theme-css-generator.js +19 -6
- package/dist/styles.css +1027 -112
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.esm.js +4 -1
- package/dist/theme.js +4 -1
- package/dist/themes/phase1-constants.d.ts +23 -0
- package/dist/themes/phase1-constants.d.ts.map +1 -0
- package/dist/themes/phase1-constants.esm.js +180 -0
- package/dist/themes/phase1-constants.js +180 -0
- package/dist/themes/themes/default.d.ts.map +1 -1
- package/dist/themes/themes/default.esm.js +4 -1
- package/dist/themes/themes/default.js +4 -1
- package/dist/themes/themes/harvey.d.ts.map +1 -1
- package/dist/themes/themes/harvey.esm.js +4 -1
- package/dist/themes/themes/harvey.js +4 -1
- package/dist/themes/types.d.ts +62 -0
- package/dist/themes/types.d.ts.map +1 -1
- package/dist/themes/validation.d.ts +17 -0
- package/dist/themes/validation.d.ts.map +1 -1
- package/dist/themes/validation.esm.js +218 -0
- package/dist/themes/validation.js +218 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/progressive-css-injector.d.ts +80 -0
- package/dist/utils/progressive-css-injector.d.ts.map +1 -0
- package/dist/utils/progressive-css-injector.esm.js +217 -0
- package/dist/utils/progressive-css-injector.js +217 -0
- package/package.json +1 -1
- package/src/components/ui/button.tsx +9 -6
- package/src/components/ui/layout/container.tsx +312 -0
- package/src/components/ui/layout/index.ts +10 -0
- package/src/components/ui/layout/responsive-grid.tsx +286 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +3 -1
- package/src/components/ui/navigation/index.ts +2 -0
- package/src/components/ui/navigation/progressive-navigation.tsx +453 -0
- package/src/components/ui/navigation/types.ts +41 -0
- package/src/hooks/use-adaptive-layout.ts +13 -9
- package/src/hooks/use-device.tsx +17 -10
- package/src/index.ts +19 -4
- package/src/plugins/css-purge-optimizer.ts +491 -0
- package/src/plugins/performance-monitor.ts +292 -0
- package/src/plugins/progressive-css-loader.ts +269 -0
- package/src/plugins/theme-css-generator.ts +22 -6
- package/src/styles/components/base/badge.css +2 -2
- package/src/styles/components/base/button.css +238 -35
- package/src/styles/components/base/card.css +2 -2
- package/src/styles/components/base/checkbox.css +3 -3
- package/src/styles/components/base/label.css +3 -3
- package/src/styles/components/feedback/skeleton.css +1 -1
- package/src/styles/components/feedback/toast.css +1 -1
- package/src/styles/components/index.css +3 -0
- package/src/styles/components/layout/container.css +466 -0
- package/src/styles/components/layout/index.css +5 -0
- package/src/styles/components/layout/responsive-grid.css +422 -0
- package/src/styles/components/navigation/breadcrumb.css +1 -1
- package/src/styles/components/navigation/index.css +1 -0
- package/src/styles/components/navigation/menu.css +2 -2
- package/src/styles/components/navigation/pagination.css +4 -4
- package/src/styles/components/navigation/progressive-navigation.css +633 -0
- package/src/styles/components/navigation/sidebar.css +4 -4
- package/src/styles/components/navigation/stepper.css +2 -2
- package/src/styles/components/navigation/tabs.css +1 -1
- package/src/styles/components/ui/theme-toggle.css +2 -2
- package/src/styles/progressive.css +17 -0
- package/src/styles/themes/harvey.css +103 -19
- package/src/styles/utilities/semantic-input-system.css +7 -13
- package/src/theme.ts +5 -1
- package/src/themes/phase1-constants.ts +189 -0
- package/src/themes/themes/default.ts +5 -1
- package/src/themes/themes/harvey.ts +5 -1
- package/src/themes/types.ts +77 -1
- package/src/themes/validation.ts +249 -0
- package/src/types.ts +77 -1
- package/src/utils/progressive-css-injector.ts +254 -0
package/src/themes/validation.ts
CHANGED
|
@@ -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
|
+
}
|