@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.1.5
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/index.d.ts +131 -131
- package/dist/index.esm.js +148 -148
- package/dist/index.js +148 -148
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/ui/accessibility-demo.tsx +271 -0
- package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
- package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
- package/src/components/ui/advanced-transition-system.tsx +395 -0
- package/src/components/ui/animation/animated-container.tsx +166 -0
- package/src/components/ui/animation/index.ts +19 -0
- package/src/components/ui/animation/staggered-container.tsx +68 -0
- package/src/components/ui/animation-demo.tsx +250 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
- package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
- package/src/components/ui/button.tsx +36 -0
- package/src/components/ui/card.tsx +207 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/color-preview.tsx +411 -0
- package/src/components/ui/data-display/chart.tsx +653 -0
- package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
- package/src/components/ui/data-display/data-grid.tsx +680 -0
- package/src/components/ui/data-display/list.tsx +456 -0
- package/src/components/ui/data-display/table.tsx +482 -0
- package/src/components/ui/data-display/timeline.tsx +441 -0
- package/src/components/ui/data-display/tree.tsx +602 -0
- package/src/components/ui/data-display/types.ts +536 -0
- package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
- package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
- package/src/components/ui/feedback/alert.tsx +157 -0
- package/src/components/ui/feedback/progress.tsx +292 -0
- package/src/components/ui/feedback/skeleton.tsx +185 -0
- package/src/components/ui/feedback/toast.tsx +280 -0
- package/src/components/ui/feedback/types.ts +125 -0
- package/src/components/ui/font-preview.tsx +288 -0
- package/src/components/ui/form-demo.tsx +553 -0
- package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
- package/src/components/ui/input.tsx +35 -0
- package/src/components/ui/label.tsx +16 -0
- package/src/components/ui/layout-demo.tsx +367 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
- package/src/components/ui/layouts/desktop-layout.tsx +224 -0
- package/src/components/ui/layouts/index.ts +10 -0
- package/src/components/ui/layouts/mobile-layout.tsx +162 -0
- package/src/components/ui/layouts/tablet-layout.tsx +197 -0
- package/src/components/ui/mobile-form-validation.tsx +451 -0
- package/src/components/ui/mobile-input-demo.tsx +201 -0
- package/src/components/ui/mobile-input.tsx +281 -0
- package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
- package/src/components/ui/navigation/breadcrumb.tsx +158 -0
- package/src/components/ui/navigation/index.ts +36 -0
- package/src/components/ui/navigation/menu.tsx +374 -0
- package/src/components/ui/navigation/navigation-demo.tsx +324 -0
- package/src/components/ui/navigation/pagination.tsx +272 -0
- package/src/components/ui/navigation/sidebar.tsx +383 -0
- package/src/components/ui/navigation/stepper.tsx +303 -0
- package/src/components/ui/navigation/tabs.tsx +205 -0
- package/src/components/ui/navigation/types.ts +299 -0
- package/src/components/ui/overlay/backdrop.tsx +81 -0
- package/src/components/ui/overlay/focus-manager.tsx +143 -0
- package/src/components/ui/overlay/index.ts +36 -0
- package/src/components/ui/overlay/modal.tsx +270 -0
- package/src/components/ui/overlay/overlay-manager.tsx +110 -0
- package/src/components/ui/overlay/popover.tsx +462 -0
- package/src/components/ui/overlay/portal.tsx +79 -0
- package/src/components/ui/overlay/tooltip.tsx +303 -0
- package/src/components/ui/overlay/types.ts +196 -0
- package/src/components/ui/performance-demo.tsx +596 -0
- package/src/components/ui/semantic-input-system-demo.tsx +502 -0
- package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
- package/src/components/ui/tablet-layout.tsx +192 -0
- package/src/components/ui/theme-customizer.tsx +386 -0
- package/src/components/ui/theme-preview.tsx +310 -0
- package/src/components/ui/theme-switcher.tsx +264 -0
- package/src/components/ui/theme-toggle.tsx +38 -0
- package/src/components/ui/token-demo.tsx +195 -0
- package/src/components/ui/touch-demo.tsx +462 -0
- package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
- package/src/components/ui/touch-friendly-interface.tsx +296 -0
- package/src/hooks/index.ts +190 -0
- package/src/hooks/use-accessibility-support.ts +518 -0
- package/src/hooks/use-adaptive-layout.ts +289 -0
- package/src/hooks/use-advanced-patterns.ts +294 -0
- package/src/hooks/use-advanced-transition-system.ts +393 -0
- package/src/hooks/use-animation-profile.ts +288 -0
- package/src/hooks/use-battery-animations.ts +384 -0
- package/src/hooks/use-battery-conscious-loading.ts +475 -0
- package/src/hooks/use-battery-optimization.ts +330 -0
- package/src/hooks/use-battery-status.ts +299 -0
- package/src/hooks/use-component-performance.ts +344 -0
- package/src/hooks/use-device-loading-states.ts +459 -0
- package/src/hooks/use-device.tsx +110 -0
- package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
- package/src/hooks/use-form-feedback.ts +403 -0
- package/src/hooks/use-form-performance.ts +513 -0
- package/src/hooks/use-frame-rate.ts +251 -0
- package/src/hooks/use-gestures.ts +338 -0
- package/src/hooks/use-hardware-acceleration.ts +341 -0
- package/src/hooks/use-input-accessibility.ts +455 -0
- package/src/hooks/use-input-performance.ts +506 -0
- package/src/hooks/use-layout-performance.ts +319 -0
- package/src/hooks/use-loading-accessibility.ts +535 -0
- package/src/hooks/use-loading-performance.ts +473 -0
- package/src/hooks/use-memory-usage.ts +287 -0
- package/src/hooks/use-mobile-form-layout.ts +464 -0
- package/src/hooks/use-mobile-form-validation.ts +518 -0
- package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
- package/src/hooks/use-mobile-layout.ts +302 -0
- package/src/hooks/use-mobile-optimization.ts +406 -0
- package/src/hooks/use-mobile-skeleton.ts +402 -0
- package/src/hooks/use-mobile-touch.ts +414 -0
- package/src/hooks/use-performance-throttling.ts +348 -0
- package/src/hooks/use-performance.ts +316 -0
- package/src/hooks/use-reusable-architecture.ts +414 -0
- package/src/hooks/use-semantic-input-types.ts +357 -0
- package/src/hooks/use-semantic-input.ts +565 -0
- package/src/hooks/use-tablet-layout.ts +384 -0
- package/src/hooks/use-touch-friendly-input.ts +524 -0
- package/src/hooks/use-touch-friendly-interface.ts +331 -0
- package/src/hooks/use-touch-optimization.ts +375 -0
- package/src/index.ts +279 -279
- package/src/lib/utils.ts +6 -0
- package/src/themes/README.md +272 -0
- package/src/themes/ThemeContext.tsx +31 -0
- package/src/themes/ThemeProvider.tsx +232 -0
- package/src/themes/accessibility/index.ts +27 -0
- package/src/themes/accessibility.ts +259 -0
- package/src/themes/aria-patterns.ts +420 -0
- package/src/themes/base-themes.ts +55 -0
- package/src/themes/colorManager.ts +380 -0
- package/src/themes/examples/dark-theme.ts +154 -0
- package/src/themes/examples/minimal-theme.ts +108 -0
- package/src/themes/focus-management.ts +701 -0
- package/src/themes/fontLoader.ts +201 -0
- package/src/themes/high-contrast.ts +621 -0
- package/src/themes/index.ts +19 -0
- package/src/themes/inheritance.ts +227 -0
- package/src/themes/keyboard-navigation.ts +550 -0
- package/src/themes/motion-reduction.ts +662 -0
- package/src/themes/navigation.ts +238 -0
- package/src/themes/screen-reader.ts +645 -0
- package/src/themes/systemThemeDetector.ts +182 -0
- package/src/themes/themeCSSUpdater.ts +262 -0
- package/src/themes/themePersistence.ts +238 -0
- package/src/themes/themes/default.ts +586 -0
- package/src/themes/themes/harvey.ts +554 -0
- package/src/themes/themes/stan-design.ts +683 -0
- package/src/themes/types.ts +460 -0
- package/src/themes/useSystemTheme.ts +48 -0
- package/src/themes/useTheme.ts +87 -0
- package/src/themes/validation.ts +462 -0
- package/src/tokens/index.ts +34 -0
- package/src/tokens/tokenExporter.ts +397 -0
- package/src/tokens/tokenGenerator.ts +276 -0
- package/src/tokens/tokenManager.ts +248 -0
- package/src/tokens/tokenValidator.ts +543 -0
- package/src/tokens/types.ts +78 -0
- package/src/utils/bundle-analyzer.ts +260 -0
- package/src/utils/bundle-splitting.ts +483 -0
- package/src/utils/lazy-loading.ts +441 -0
- package/src/utils/performance-monitor.ts +513 -0
- package/src/utils/tree-shaking.ts +274 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Base Theme Definitions
|
|
2
|
+
// This file imports and exports all theme configurations with inheritance support
|
|
3
|
+
|
|
4
|
+
import { MultiThemeConfig } from './types';
|
|
5
|
+
import { applyThemeInheritance } from './inheritance';
|
|
6
|
+
import { stanDesignTheme as stanDesignThemeRaw } from './themes/stan-design';
|
|
7
|
+
import { harveyTheme as harveyThemeRaw } from './themes/harvey';
|
|
8
|
+
import { defaultTheme } from './themes/default';
|
|
9
|
+
|
|
10
|
+
// Apply inheritance to ensure all themes have complete configurations
|
|
11
|
+
export const stanDesignTheme = applyThemeInheritance(stanDesignThemeRaw);
|
|
12
|
+
export const harveyTheme = applyThemeInheritance(harveyThemeRaw);
|
|
13
|
+
|
|
14
|
+
// Export the default theme for reference
|
|
15
|
+
export { defaultTheme };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Default themes map
|
|
19
|
+
*/
|
|
20
|
+
export const defaultThemes: Record<string, MultiThemeConfig> = {
|
|
21
|
+
'default': defaultTheme,
|
|
22
|
+
'stan-design': stanDesignTheme,
|
|
23
|
+
'harvey': harveyTheme
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Theme categories for organization
|
|
28
|
+
*/
|
|
29
|
+
export const themeCategories = {
|
|
30
|
+
brand: ['stan-design'],
|
|
31
|
+
creative: ['harvey'],
|
|
32
|
+
custom: [],
|
|
33
|
+
accessibility: []
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get theme by name
|
|
38
|
+
*/
|
|
39
|
+
export function getTheme(name: string): MultiThemeConfig | undefined {
|
|
40
|
+
return defaultThemes[name];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get all available theme names
|
|
45
|
+
*/
|
|
46
|
+
export function getAvailableThemes(): string[] {
|
|
47
|
+
return Object.keys(defaultThemes);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if theme exists
|
|
52
|
+
*/
|
|
53
|
+
export function themeExists(name: string): boolean {
|
|
54
|
+
return name in defaultThemes;
|
|
55
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Management System
|
|
3
|
+
* Handles color generation, validation, and accessibility checking
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ColorScale,
|
|
8
|
+
SemanticColors,
|
|
9
|
+
ColorAccessibility,
|
|
10
|
+
ColorGenerationConfig,
|
|
11
|
+
ColorPalette
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
export class ColorManager {
|
|
15
|
+
/**
|
|
16
|
+
* Generate a complete color scale from a base color
|
|
17
|
+
*/
|
|
18
|
+
static generateColorScale(config: ColorGenerationConfig): ColorScale {
|
|
19
|
+
const { baseColor, hueShift: _hueShift = 0, saturationAdjust = 0, lightnessAdjust = 0 } = config;
|
|
20
|
+
|
|
21
|
+
// Parse the base color
|
|
22
|
+
const hsl = this.hexToHsl(baseColor);
|
|
23
|
+
if (!hsl) {
|
|
24
|
+
throw new Error(`Invalid color format: ${baseColor}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Generate the scale - preserve base color at 500
|
|
28
|
+
const scale: ColorScale = {
|
|
29
|
+
50: this.adjustHsl(hsl, 0, saturationAdjust, 95 + lightnessAdjust),
|
|
30
|
+
100: this.adjustHsl(hsl, 0, saturationAdjust, 90 + lightnessAdjust),
|
|
31
|
+
200: this.adjustHsl(hsl, 0, saturationAdjust, 80 + lightnessAdjust),
|
|
32
|
+
300: this.adjustHsl(hsl, 0, saturationAdjust, 70 + lightnessAdjust),
|
|
33
|
+
400: this.adjustHsl(hsl, 0, saturationAdjust, 60 + lightnessAdjust),
|
|
34
|
+
500: baseColor, // Keep original base color
|
|
35
|
+
600: this.adjustHsl(hsl, 0, saturationAdjust, 40 + lightnessAdjust),
|
|
36
|
+
700: this.adjustHsl(hsl, 0, saturationAdjust, 30 + lightnessAdjust),
|
|
37
|
+
800: this.adjustHsl(hsl, 0, saturationAdjust, 20 + lightnessAdjust),
|
|
38
|
+
900: this.adjustHsl(hsl, 0, saturationAdjust, 10 + lightnessAdjust)
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Add optional variations
|
|
42
|
+
if (config.generateShades) {
|
|
43
|
+
scale.light = this.adjustHsl(hsl, 0, saturationAdjust, 98 + lightnessAdjust);
|
|
44
|
+
scale.dark = this.adjustHsl(hsl, 0, saturationAdjust, 5 + lightnessAdjust);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (config.generateContrast) {
|
|
48
|
+
scale.contrast = this.generateContrastColor(baseColor);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return scale;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate semantic colors based on a primary color
|
|
56
|
+
*/
|
|
57
|
+
static generateSemanticColors(primaryColor: string): SemanticColors {
|
|
58
|
+
const primaryHsl = this.hexToHsl(primaryColor);
|
|
59
|
+
if (!primaryHsl) {
|
|
60
|
+
throw new Error(`Invalid primary color: ${primaryColor}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Generate semantic colors with appropriate hues
|
|
64
|
+
const successHue = 120; // Green
|
|
65
|
+
const warningHue = 45; // Yellow/Orange
|
|
66
|
+
const errorHue = 0; // Red
|
|
67
|
+
const infoHue = 210; // Blue
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
success: this.hslToHex(successHue, 70, 50),
|
|
71
|
+
warning: this.hslToHex(warningHue, 90, 50),
|
|
72
|
+
error: this.hslToHex(errorHue, 80, 50),
|
|
73
|
+
info: this.hslToHex(infoHue, 80, 50),
|
|
74
|
+
|
|
75
|
+
// Light variants
|
|
76
|
+
successLight: this.hslToHex(successHue, 70, 90),
|
|
77
|
+
warningLight: this.hslToHex(warningHue, 90, 90),
|
|
78
|
+
errorLight: this.hslToHex(errorHue, 80, 90),
|
|
79
|
+
infoLight: this.hslToHex(infoHue, 80, 90),
|
|
80
|
+
|
|
81
|
+
// Dark variants
|
|
82
|
+
successDark: this.hslToHex(successHue, 70, 30),
|
|
83
|
+
warningDark: this.hslToHex(warningHue, 90, 30),
|
|
84
|
+
errorDark: this.hslToHex(errorHue, 80, 30),
|
|
85
|
+
infoDark: this.hslToHex(infoHue, 80, 30)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate neutral color scale
|
|
91
|
+
*/
|
|
92
|
+
static generateNeutralColors(baseColor: string = '#6b7280'): ColorScale {
|
|
93
|
+
const hsl = this.hexToHsl(baseColor);
|
|
94
|
+
if (!hsl) {
|
|
95
|
+
throw new Error(`Invalid neutral color: ${baseColor}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
50: this.hslToHex(hsl.h, 5, 97),
|
|
100
|
+
100: this.hslToHex(hsl.h, 5, 90),
|
|
101
|
+
200: this.hslToHex(hsl.h, 5, 80),
|
|
102
|
+
300: this.hslToHex(hsl.h, 5, 70),
|
|
103
|
+
400: this.hslToHex(hsl.h, 5, 60),
|
|
104
|
+
500: baseColor, // Keep original base color
|
|
105
|
+
600: this.hslToHex(hsl.h, 5, 40),
|
|
106
|
+
700: this.hslToHex(hsl.h, 5, 30),
|
|
107
|
+
800: this.hslToHex(hsl.h, 5, 20),
|
|
108
|
+
900: this.hslToHex(hsl.h, 5, 10)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check color contrast for accessibility
|
|
114
|
+
*/
|
|
115
|
+
static checkColorContrast(foreground: string, background: string): ColorAccessibility {
|
|
116
|
+
const ratio = this.calculateContrastRatio(foreground, background);
|
|
117
|
+
|
|
118
|
+
const aa = ratio >= 4.5; // WCAG AA standard for normal text
|
|
119
|
+
const aaa = ratio >= 7; // WCAG AAA standard for normal text
|
|
120
|
+
|
|
121
|
+
const recommendations: string[] = [];
|
|
122
|
+
if (!aa) {
|
|
123
|
+
recommendations.push('Increase contrast to meet WCAG AA standards (4.5:1)');
|
|
124
|
+
}
|
|
125
|
+
if (!aaa) {
|
|
126
|
+
recommendations.push('Increase contrast to meet WCAG AAA standards (7:1)');
|
|
127
|
+
}
|
|
128
|
+
if (ratio < 3) {
|
|
129
|
+
recommendations.push('Consider using a different color combination for better readability');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
aa,
|
|
134
|
+
aaa,
|
|
135
|
+
contrastRatio: ratio,
|
|
136
|
+
recommended: recommendations
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validate color format
|
|
142
|
+
*/
|
|
143
|
+
static isValidColor(color: string): boolean {
|
|
144
|
+
// Check hex format
|
|
145
|
+
if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check rgb format with valid range (0-255)
|
|
150
|
+
const rgbMatch = color.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
|
|
151
|
+
if (rgbMatch) {
|
|
152
|
+
const [, r, g, b] = rgbMatch;
|
|
153
|
+
return parseInt(r) <= 255 && parseInt(g) <= 255 && parseInt(b) <= 255;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Check hsl format with valid ranges (h: 0-360, s: 0-100%, l: 0-100%)
|
|
157
|
+
const hslMatch = color.match(/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$/);
|
|
158
|
+
if (hslMatch) {
|
|
159
|
+
const [, h, s, l] = hslMatch;
|
|
160
|
+
return parseInt(h) <= 360 && parseInt(s) <= 100 && parseInt(l) <= 100;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check named colors (basic set)
|
|
164
|
+
const namedColors = [
|
|
165
|
+
'black', 'white', 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta',
|
|
166
|
+
'gray', 'grey', 'orange', 'purple', 'pink', 'brown', 'navy', 'teal'
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
return namedColors.includes(color.toLowerCase());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Convert hex color to HSL
|
|
174
|
+
*/
|
|
175
|
+
private static hexToHsl(hex: string): { h: number; s: number; l: number } | null {
|
|
176
|
+
try {
|
|
177
|
+
// Remove # if present
|
|
178
|
+
hex = hex.replace('#', '');
|
|
179
|
+
|
|
180
|
+
// Convert 3-digit hex to 6-digit
|
|
181
|
+
if (hex.length === 3) {
|
|
182
|
+
hex = hex.split('').map(char => char + char).join('');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (hex.length !== 6) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const r = parseInt(hex.substr(0, 2), 16) / 255;
|
|
190
|
+
const g = parseInt(hex.substr(2, 2), 16) / 255;
|
|
191
|
+
const b = parseInt(hex.substr(4, 2), 16) / 255;
|
|
192
|
+
|
|
193
|
+
const max = Math.max(r, g, b);
|
|
194
|
+
const min = Math.min(r, g, b);
|
|
195
|
+
let h = 0;
|
|
196
|
+
let s = 0;
|
|
197
|
+
const l = (max + min) / 2;
|
|
198
|
+
|
|
199
|
+
if (max !== min) {
|
|
200
|
+
const d = max - min;
|
|
201
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
202
|
+
|
|
203
|
+
switch (max) {
|
|
204
|
+
case r:
|
|
205
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
206
|
+
break;
|
|
207
|
+
case g:
|
|
208
|
+
h = (b - r) / d + 2;
|
|
209
|
+
break;
|
|
210
|
+
case b:
|
|
211
|
+
h = (r - g) / d + 4;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
h /= 6;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
h: Math.round(h * 360),
|
|
219
|
+
s: Math.round(s * 100),
|
|
220
|
+
l: Math.round(l * 100)
|
|
221
|
+
};
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Convert HSL to hex color
|
|
229
|
+
*/
|
|
230
|
+
private static hslToHex(h: number, s: number, l: number): string {
|
|
231
|
+
h = h / 360;
|
|
232
|
+
s = s / 100;
|
|
233
|
+
l = l / 100;
|
|
234
|
+
|
|
235
|
+
const hue2rgb = (p: number, q: number, t: number): number => {
|
|
236
|
+
if (t < 0) t += 1;
|
|
237
|
+
if (t > 1) t -= 1;
|
|
238
|
+
if (t < 1/6) return p + (q - p) * 6 * t;
|
|
239
|
+
if (t < 1/2) return q;
|
|
240
|
+
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
241
|
+
return p;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
let r: number, g: number, b: number;
|
|
245
|
+
|
|
246
|
+
if (s === 0) {
|
|
247
|
+
r = g = b = l;
|
|
248
|
+
} else {
|
|
249
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
250
|
+
const p = 2 * l - q;
|
|
251
|
+
r = hue2rgb(p, q, h + 1/3);
|
|
252
|
+
g = hue2rgb(p, q, h);
|
|
253
|
+
b = hue2rgb(p, q, h - 1/3);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const toHex = (c: number): string => {
|
|
257
|
+
const hex = Math.round(c * 255).toString(16);
|
|
258
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Adjust HSL values
|
|
266
|
+
*/
|
|
267
|
+
private static adjustHsl(
|
|
268
|
+
base: { h: number; s: number; l: number },
|
|
269
|
+
hShift: number,
|
|
270
|
+
sAdjust: number,
|
|
271
|
+
lValue: number
|
|
272
|
+
): string {
|
|
273
|
+
const h = (base.h + hShift) % 360;
|
|
274
|
+
const s = Math.max(0, Math.min(100, base.s + sAdjust));
|
|
275
|
+
const l = Math.max(0, Math.min(100, lValue));
|
|
276
|
+
|
|
277
|
+
return this.hslToHex(h, s, l);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Calculate contrast ratio between two colors
|
|
282
|
+
*/
|
|
283
|
+
private static calculateContrastRatio(color1: string, color2: string): number {
|
|
284
|
+
const luminance1 = this.calculateLuminance(color1);
|
|
285
|
+
const luminance2 = this.calculateLuminance(color2);
|
|
286
|
+
|
|
287
|
+
const lighter = Math.max(luminance1, luminance2);
|
|
288
|
+
const darker = Math.min(luminance1, luminance2);
|
|
289
|
+
|
|
290
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Calculate relative luminance of a color
|
|
295
|
+
*/
|
|
296
|
+
private static calculateLuminance(color: string): number {
|
|
297
|
+
const rgb = this.hexToRgb(color);
|
|
298
|
+
if (!rgb) return 0;
|
|
299
|
+
|
|
300
|
+
const { r, g, b } = rgb;
|
|
301
|
+
|
|
302
|
+
const [rs, gs, bs] = [r, g, b].map(c => {
|
|
303
|
+
c = c / 255;
|
|
304
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Convert hex to RGB
|
|
312
|
+
*/
|
|
313
|
+
private static hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
314
|
+
try {
|
|
315
|
+
hex = hex.replace('#', '');
|
|
316
|
+
|
|
317
|
+
if (hex.length === 3) {
|
|
318
|
+
hex = hex.split('').map(char => char + char).join('');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (hex.length !== 6) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
r: parseInt(hex.substr(0, 2), 16),
|
|
327
|
+
g: parseInt(hex.substr(2, 2), 16),
|
|
328
|
+
b: parseInt(hex.substr(4, 2), 16)
|
|
329
|
+
};
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Generate a high contrast color
|
|
337
|
+
*/
|
|
338
|
+
private static generateContrastColor(baseColor: string): string {
|
|
339
|
+
const hsl = this.hexToHsl(baseColor);
|
|
340
|
+
if (!hsl) return '#000000';
|
|
341
|
+
|
|
342
|
+
// Generate a contrasting color by shifting hue by 180 degrees
|
|
343
|
+
const contrastHue = (hsl.h + 180) % 360;
|
|
344
|
+
|
|
345
|
+
// Use high saturation and appropriate lightness for contrast
|
|
346
|
+
return this.hslToHex(contrastHue, 80, hsl.l > 50 ? 20 : 80);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Create a color palette with accessibility information
|
|
351
|
+
*/
|
|
352
|
+
static createColorPalette(
|
|
353
|
+
name: string,
|
|
354
|
+
baseColor: string,
|
|
355
|
+
description: string,
|
|
356
|
+
tags: string[] = []
|
|
357
|
+
): ColorPalette {
|
|
358
|
+
const colors = this.generateColorScale({
|
|
359
|
+
baseColor,
|
|
360
|
+
generateShades: true,
|
|
361
|
+
generateContrast: true
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const accessibility = this.checkColorContrast(colors[500], '#ffffff');
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
name,
|
|
368
|
+
description,
|
|
369
|
+
colors,
|
|
370
|
+
accessibility,
|
|
371
|
+
usage: ['primary', 'accent', 'brand'],
|
|
372
|
+
tags: [...tags, 'generated', 'accessible']
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Default color manager instance
|
|
379
|
+
*/
|
|
380
|
+
export const colorManager = new ColorManager();
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { createTheme, overrideTheme, PartialThemeConfig } from '../inheritance';
|
|
2
|
+
import { stanDesignTheme } from '../base-themes';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example: Dark Theme using inheritance
|
|
6
|
+
* This theme overrides colors for a dark mode while keeping other properties
|
|
7
|
+
*/
|
|
8
|
+
const darkThemeConfig: PartialThemeConfig = {
|
|
9
|
+
meta: {
|
|
10
|
+
name: 'Dark Mode',
|
|
11
|
+
description: 'Dark variant with inheritance from default theme',
|
|
12
|
+
author: 'Theme Examples',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
category: 'custom',
|
|
15
|
+
tags: ['dark', 'night', 'inheritance'],
|
|
16
|
+
preview: '/themes/dark-preview.png',
|
|
17
|
+
createdAt: '2024-12-01T00:00:00.000Z',
|
|
18
|
+
updatedAt: '2024-12-01T00:00:00.000Z'
|
|
19
|
+
},
|
|
20
|
+
colors: {
|
|
21
|
+
primary: {
|
|
22
|
+
50: '#0f172a',
|
|
23
|
+
100: '#1e293b',
|
|
24
|
+
200: '#334155',
|
|
25
|
+
300: '#475569',
|
|
26
|
+
400: '#64748b',
|
|
27
|
+
500: '#94a3b8',
|
|
28
|
+
600: '#cbd5e1',
|
|
29
|
+
700: '#e2e8f0',
|
|
30
|
+
800: '#f1f5f9',
|
|
31
|
+
900: '#f8fafc',
|
|
32
|
+
'500-rgb': '148, 163, 184',
|
|
33
|
+
'600-rgb': '203, 213, 225',
|
|
34
|
+
'700-rgb': '226, 232, 240'
|
|
35
|
+
},
|
|
36
|
+
secondary: {
|
|
37
|
+
50: '#f8fafc',
|
|
38
|
+
100: '#f1f5f9',
|
|
39
|
+
200: '#e2e8f0',
|
|
40
|
+
300: '#cbd5e1',
|
|
41
|
+
400: '#94a3b8',
|
|
42
|
+
500: '#64748b',
|
|
43
|
+
600: '#475569',
|
|
44
|
+
700: '#334155',
|
|
45
|
+
800: '#1e293b',
|
|
46
|
+
900: '#0f172a'
|
|
47
|
+
},
|
|
48
|
+
semantic: {
|
|
49
|
+
success: '#10b981',
|
|
50
|
+
warning: '#f59e0b',
|
|
51
|
+
error: '#ef4444',
|
|
52
|
+
info: '#3b82f6',
|
|
53
|
+
successRgb: '16, 185, 129',
|
|
54
|
+
warningRgb: '245, 158, 11',
|
|
55
|
+
errorRgb: '239, 68, 68',
|
|
56
|
+
infoRgb: '59, 130, 246',
|
|
57
|
+
'success-rgb': '16, 185, 129',
|
|
58
|
+
'warning-rgb': '245, 158, 11',
|
|
59
|
+
'error-rgb': '239, 68, 68',
|
|
60
|
+
'info-rgb': '59, 130, 246'
|
|
61
|
+
},
|
|
62
|
+
neutral: {
|
|
63
|
+
50: '#fafafa',
|
|
64
|
+
100: '#f5f5f5',
|
|
65
|
+
200: '#e5e5e5',
|
|
66
|
+
300: '#d4d4d4',
|
|
67
|
+
400: '#a3a3a3',
|
|
68
|
+
500: '#737373',
|
|
69
|
+
600: '#525252',
|
|
70
|
+
700: '#404040',
|
|
71
|
+
800: '#262626',
|
|
72
|
+
900: '#171717'
|
|
73
|
+
},
|
|
74
|
+
surface: {
|
|
75
|
+
background: '#0f172a',
|
|
76
|
+
surface: '#1e293b',
|
|
77
|
+
border: '#334155',
|
|
78
|
+
divider: '#475569'
|
|
79
|
+
},
|
|
80
|
+
text: {
|
|
81
|
+
primary: '#f8fafc',
|
|
82
|
+
secondary: '#e2e8f0',
|
|
83
|
+
muted: '#cbd5e1',
|
|
84
|
+
inverse: '#0f172a',
|
|
85
|
+
onPrimary: '#0f172a',
|
|
86
|
+
onSecondary: '#0f172a',
|
|
87
|
+
onSurface: '#f8fafc'
|
|
88
|
+
},
|
|
89
|
+
interactive: {
|
|
90
|
+
hover: '#1e293b',
|
|
91
|
+
active: '#334155',
|
|
92
|
+
focus: '#94a3b8',
|
|
93
|
+
disabled: '#475569'
|
|
94
|
+
},
|
|
95
|
+
// Additional dark mode colors
|
|
96
|
+
'bg-primary': '#0f172a',
|
|
97
|
+
'bg-secondary': '#1e293b',
|
|
98
|
+
'surface-bg': '#1e293b',
|
|
99
|
+
'text-primary': '#f8fafc',
|
|
100
|
+
'text-secondary': '#e2e8f0'
|
|
101
|
+
}
|
|
102
|
+
// All other properties (fonts, navigation, spacing, etc.) inherit from default
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create dark theme with full inheritance
|
|
107
|
+
*/
|
|
108
|
+
export const darkTheme = createTheme(darkThemeConfig);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Example: Override an existing theme (Stan Design) to create a dark variant
|
|
112
|
+
*/
|
|
113
|
+
export const stanDesignDarkTheme = overrideTheme(stanDesignTheme, {
|
|
114
|
+
meta: {
|
|
115
|
+
name: 'Stan Design Dark',
|
|
116
|
+
description: 'Dark variant of Stan Design theme',
|
|
117
|
+
author: 'Theme Examples',
|
|
118
|
+
version: '1.0.0',
|
|
119
|
+
category: 'custom',
|
|
120
|
+
tags: ['dark', 'stan-design', 'professional'],
|
|
121
|
+
preview: '/themes/stan-design-dark-preview.png',
|
|
122
|
+
createdAt: '2024-12-01T00:00:00.000Z',
|
|
123
|
+
updatedAt: '2024-12-01T00:00:00.000Z'
|
|
124
|
+
},
|
|
125
|
+
colors: darkThemeConfig.colors
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Example: Partial override for quick customizations
|
|
130
|
+
*/
|
|
131
|
+
export const quickDarkVariant = overrideTheme(stanDesignTheme, {
|
|
132
|
+
colors: {
|
|
133
|
+
primary: stanDesignTheme.colors.primary,
|
|
134
|
+
secondary: stanDesignTheme.colors.secondary,
|
|
135
|
+
semantic: stanDesignTheme.colors.semantic,
|
|
136
|
+
neutral: stanDesignTheme.colors.neutral,
|
|
137
|
+
surface: {
|
|
138
|
+
background: '#0f172a',
|
|
139
|
+
surface: '#1e293b',
|
|
140
|
+
border: '#334155',
|
|
141
|
+
divider: '#475569'
|
|
142
|
+
},
|
|
143
|
+
text: {
|
|
144
|
+
primary: '#f8fafc',
|
|
145
|
+
secondary: '#e2e8f0',
|
|
146
|
+
muted: '#cbd5e1',
|
|
147
|
+
inverse: '#0f172a',
|
|
148
|
+
onPrimary: '#0f172a',
|
|
149
|
+
onSecondary: '#0f172a',
|
|
150
|
+
onSurface: '#f8fafc'
|
|
151
|
+
},
|
|
152
|
+
interactive: stanDesignTheme.colors.interactive
|
|
153
|
+
}
|
|
154
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createTheme, PartialThemeConfig } from '../inheritance';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Minimal Theme using inheritance
|
|
5
|
+
* This theme only defines the properties it wants to override.
|
|
6
|
+
* All other values will be inherited from the default theme.
|
|
7
|
+
*/
|
|
8
|
+
const minimalThemeConfig: PartialThemeConfig = {
|
|
9
|
+
meta: {
|
|
10
|
+
name: 'Minimal Blue',
|
|
11
|
+
description: 'A minimal theme that only overrides primary colors',
|
|
12
|
+
author: 'Theme Examples',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
category: 'custom',
|
|
15
|
+
tags: ['minimal', 'blue', 'simple'],
|
|
16
|
+
preview: '/themes/minimal-blue-preview.png',
|
|
17
|
+
createdAt: '2024-12-01T00:00:00.000Z',
|
|
18
|
+
updatedAt: '2024-12-01T00:00:00.000Z'
|
|
19
|
+
},
|
|
20
|
+
colors: {
|
|
21
|
+
primary: {
|
|
22
|
+
50: '#eff6ff',
|
|
23
|
+
100: '#dbeafe',
|
|
24
|
+
200: '#bfdbfe',
|
|
25
|
+
300: '#93c5fd',
|
|
26
|
+
400: '#60a5fa',
|
|
27
|
+
500: '#2563eb', // Only override the main primary color
|
|
28
|
+
600: '#1d4ed8',
|
|
29
|
+
700: '#1e40af',
|
|
30
|
+
800: '#1e3a8a',
|
|
31
|
+
900: '#1e3a8a'
|
|
32
|
+
// All other primary colors will come from default theme
|
|
33
|
+
},
|
|
34
|
+
secondary: {
|
|
35
|
+
50: '#f8fafc',
|
|
36
|
+
100: '#f1f5f9',
|
|
37
|
+
200: '#e2e8f0',
|
|
38
|
+
300: '#cbd5e1',
|
|
39
|
+
400: '#94a3b8',
|
|
40
|
+
500: '#64748b',
|
|
41
|
+
600: '#475569',
|
|
42
|
+
700: '#334155',
|
|
43
|
+
800: '#1e293b',
|
|
44
|
+
900: '#0f172a'
|
|
45
|
+
},
|
|
46
|
+
semantic: {
|
|
47
|
+
success: '#10b981',
|
|
48
|
+
warning: '#f59e0b',
|
|
49
|
+
error: '#ef4444',
|
|
50
|
+
info: '#2563eb' // Override info color to match primary
|
|
51
|
+
// success, warning, error will come from default theme
|
|
52
|
+
},
|
|
53
|
+
neutral: {
|
|
54
|
+
50: '#fafafa',
|
|
55
|
+
100: '#f5f5f5',
|
|
56
|
+
200: '#e5e5e5',
|
|
57
|
+
300: '#d4d4d4',
|
|
58
|
+
400: '#a3a3a3',
|
|
59
|
+
500: '#737373',
|
|
60
|
+
600: '#525252',
|
|
61
|
+
700: '#404040',
|
|
62
|
+
800: '#262626',
|
|
63
|
+
900: '#171717'
|
|
64
|
+
},
|
|
65
|
+
surface: {
|
|
66
|
+
background: '#ffffff',
|
|
67
|
+
surface: '#f8fafc',
|
|
68
|
+
border: '#e2e8f0',
|
|
69
|
+
divider: '#cbd5e1'
|
|
70
|
+
},
|
|
71
|
+
text: {
|
|
72
|
+
primary: '#0f172a',
|
|
73
|
+
secondary: '#475569',
|
|
74
|
+
muted: '#64748b',
|
|
75
|
+
inverse: '#ffffff',
|
|
76
|
+
onPrimary: '#ffffff',
|
|
77
|
+
onSecondary: '#ffffff',
|
|
78
|
+
onSurface: '#0f172a'
|
|
79
|
+
},
|
|
80
|
+
interactive: {
|
|
81
|
+
hover: '#f1f5f9',
|
|
82
|
+
active: '#e2e8f0',
|
|
83
|
+
focus: '#3b82f6',
|
|
84
|
+
disabled: '#cbd5e1'
|
|
85
|
+
}
|
|
86
|
+
// All other colors will be inherited from default theme
|
|
87
|
+
}
|
|
88
|
+
// fonts, navigation, spacing, shadows, transitions will all come from default theme
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create the theme with inheritance applied
|
|
93
|
+
* This will have ALL properties filled in, using defaults where not specified
|
|
94
|
+
*/
|
|
95
|
+
export const minimalTheme = createTheme(minimalThemeConfig);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Example of checking theme completeness
|
|
99
|
+
*/
|
|
100
|
+
import { themeInheritanceManager } from '../inheritance';
|
|
101
|
+
|
|
102
|
+
// This will return an empty array since inheritance fills in all missing values
|
|
103
|
+
const missingProperties = themeInheritanceManager.validateCompleteness(minimalTheme);
|
|
104
|
+
console.log('Missing properties:', missingProperties); // Should be []
|
|
105
|
+
|
|
106
|
+
// This will return true since all properties are present
|
|
107
|
+
const hasCompleteInheritance = themeInheritanceManager.hasInheritance(minimalTheme);
|
|
108
|
+
console.log('Has complete inheritance:', hasCompleteInheritance); // Should be true
|