@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.1.6
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,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Theme Detection Utility
|
|
3
|
+
* Detects and manages system theme preferences
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type SystemTheme = 'light' | 'dark';
|
|
7
|
+
|
|
8
|
+
export interface SystemThemeOptions {
|
|
9
|
+
defaultTheme?: SystemTheme;
|
|
10
|
+
storageKey?: string;
|
|
11
|
+
enablePersistence?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class SystemThemeDetector {
|
|
15
|
+
private mediaQuery: MediaQueryList | null = null;
|
|
16
|
+
private storageKey: string;
|
|
17
|
+
private enablePersistence: boolean;
|
|
18
|
+
private listeners: Set<(theme: SystemTheme) => void> = new Set();
|
|
19
|
+
|
|
20
|
+
constructor(options: SystemThemeOptions = {}) {
|
|
21
|
+
this.storageKey = options.storageKey || 'system-theme-preference';
|
|
22
|
+
this.enablePersistence = options.enablePersistence ?? true;
|
|
23
|
+
|
|
24
|
+
// Initialize media query if available
|
|
25
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
26
|
+
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
27
|
+
this.mediaQuery.addEventListener('change', this.handleMediaQueryChange);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the current system theme preference
|
|
33
|
+
*/
|
|
34
|
+
getSystemTheme(): SystemTheme {
|
|
35
|
+
if (this.mediaQuery) {
|
|
36
|
+
return this.mediaQuery.matches ? 'dark' : 'light';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Fallback to stored preference or default
|
|
40
|
+
if (this.enablePersistence) {
|
|
41
|
+
const stored = this.getStoredPreference();
|
|
42
|
+
if (stored) return stored;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 'light';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get stored theme preference
|
|
50
|
+
*/
|
|
51
|
+
getStoredPreference(): SystemTheme | null {
|
|
52
|
+
if (!this.enablePersistence || typeof window === 'undefined') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
58
|
+
if (stored === 'light' || stored === 'dark') {
|
|
59
|
+
return stored;
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.warn('Failed to read system theme preference from storage:', error);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Store theme preference
|
|
70
|
+
*/
|
|
71
|
+
setStoredPreference(theme: SystemTheme): void {
|
|
72
|
+
if (!this.enablePersistence || typeof window === 'undefined') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
localStorage.setItem(this.storageKey, theme);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.warn('Failed to store system theme preference:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if system theme detection is supported
|
|
85
|
+
*/
|
|
86
|
+
isSupported(): boolean {
|
|
87
|
+
return typeof window !== 'undefined' &&
|
|
88
|
+
'matchMedia' in window &&
|
|
89
|
+
'addEventListener' in MediaQueryList.prototype;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Subscribe to system theme changes
|
|
94
|
+
*/
|
|
95
|
+
subscribe(callback: (theme: SystemTheme) => void): () => void {
|
|
96
|
+
this.listeners.add(callback);
|
|
97
|
+
|
|
98
|
+
// Return unsubscribe function
|
|
99
|
+
return () => {
|
|
100
|
+
this.listeners.delete(callback);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Notify all listeners of theme change
|
|
106
|
+
*/
|
|
107
|
+
private notifyListeners(theme: SystemTheme): void {
|
|
108
|
+
this.listeners.forEach(callback => {
|
|
109
|
+
try {
|
|
110
|
+
callback(theme);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Error in system theme change callback:', error);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handle media query changes
|
|
119
|
+
*/
|
|
120
|
+
private handleMediaQueryChange = (event: MediaQueryListEvent): void => {
|
|
121
|
+
const newTheme: SystemTheme = event.matches ? 'dark' : 'light';
|
|
122
|
+
|
|
123
|
+
if (this.enablePersistence) {
|
|
124
|
+
this.setStoredPreference(newTheme);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.notifyListeners(newTheme);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get theme-aware CSS variables
|
|
132
|
+
*/
|
|
133
|
+
getThemeCSSVariables(theme: SystemTheme): Record<string, string> {
|
|
134
|
+
const baseVars = {
|
|
135
|
+
'--system-theme': theme,
|
|
136
|
+
'--system-theme-opposite': theme === 'light' ? 'dark' : 'light',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Add system-specific color adjustments
|
|
140
|
+
if (theme === 'dark') {
|
|
141
|
+
return {
|
|
142
|
+
...baseVars,
|
|
143
|
+
'--system-bg': '#000000',
|
|
144
|
+
'--system-text': '#ffffff',
|
|
145
|
+
'--system-border': '#333333',
|
|
146
|
+
};
|
|
147
|
+
} else {
|
|
148
|
+
return {
|
|
149
|
+
...baseVars,
|
|
150
|
+
'--system-bg': '#ffffff',
|
|
151
|
+
'--system-text': '#000000',
|
|
152
|
+
'--system-border': '#e5e7eb',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Apply system theme CSS variables to document
|
|
159
|
+
*/
|
|
160
|
+
applySystemThemeCSS(theme: SystemTheme): void {
|
|
161
|
+
if (typeof document === 'undefined') return;
|
|
162
|
+
|
|
163
|
+
const cssVars = this.getThemeCSSVariables(theme);
|
|
164
|
+
|
|
165
|
+
Object.entries(cssVars).forEach(([property, value]) => {
|
|
166
|
+
document.documentElement.style.setProperty(property, value);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Clean up event listeners
|
|
172
|
+
*/
|
|
173
|
+
destroy(): void {
|
|
174
|
+
if (this.mediaQuery) {
|
|
175
|
+
this.mediaQuery.removeEventListener('change', this.handleMediaQueryChange);
|
|
176
|
+
}
|
|
177
|
+
this.listeners.clear();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Note: React hook is exported separately in a React-specific file
|
|
182
|
+
// This file contains only the core SystemThemeDetector class
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme CSS Variable Updater
|
|
3
|
+
* Applies theme configurations to CSS custom properties
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CompleteThemeConfig } from './types';
|
|
7
|
+
|
|
8
|
+
export class ThemeCSSUpdater {
|
|
9
|
+
private root: HTMLElement;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.root = document.documentElement;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Apply a complete theme configuration to CSS custom properties
|
|
17
|
+
*/
|
|
18
|
+
applyTheme(theme: CompleteThemeConfig): void {
|
|
19
|
+
this.applyColors(theme.colors);
|
|
20
|
+
this.applyFonts(theme.fonts);
|
|
21
|
+
this.applySpacing(theme.spacing);
|
|
22
|
+
this.applyShadows(theme.shadows);
|
|
23
|
+
this.applyTransitions(theme.transitions);
|
|
24
|
+
this.applyBorderRadius(theme.borderRadius);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Apply color variables to CSS custom properties
|
|
29
|
+
*/
|
|
30
|
+
private applyColors(colors: CompleteThemeConfig['colors']): void {
|
|
31
|
+
// Primary colors - set both flat and nested versions for compatibility
|
|
32
|
+
Object.entries(colors.primary).forEach(([shade, value]) => {
|
|
33
|
+
// Set flat version (for backward compatibility)
|
|
34
|
+
this.root.style.setProperty(`--cs-primary-${shade}`, value);
|
|
35
|
+
// Set nested version (for badge CSS and other components)
|
|
36
|
+
this.root.style.setProperty(`--cs-colors-primary-${shade}`, value);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Secondary colors - set both flat and nested versions
|
|
40
|
+
Object.entries(colors.secondary).forEach(([shade, value]) => {
|
|
41
|
+
// Set flat version (for backward compatibility)
|
|
42
|
+
this.root.style.setProperty(`--cs-secondary-${shade}`, value);
|
|
43
|
+
// Set nested version (for badge CSS and other components)
|
|
44
|
+
this.root.style.setProperty(`--cs-colors-secondary-${shade}`, value);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Semantic colors - set both flat and nested versions
|
|
48
|
+
this.root.style.setProperty('--cs-success', colors.semantic.success);
|
|
49
|
+
this.root.style.setProperty('--cs-colors-semantic-success', colors.semantic.success);
|
|
50
|
+
this.root.style.setProperty('--cs-warning', colors.semantic.warning);
|
|
51
|
+
this.root.style.setProperty('--cs-colors-semantic-warning', colors.semantic.warning);
|
|
52
|
+
this.root.style.setProperty('--cs-error', colors.semantic.error);
|
|
53
|
+
this.root.style.setProperty('--cs-colors-semantic-error', colors.semantic.error);
|
|
54
|
+
this.root.style.setProperty('--cs-info', colors.semantic.info);
|
|
55
|
+
this.root.style.setProperty('--cs-colors-semantic-info', colors.semantic.info);
|
|
56
|
+
|
|
57
|
+
// Neutral colors - set both flat and nested versions
|
|
58
|
+
Object.entries(colors.neutral).forEach(([shade, value]) => {
|
|
59
|
+
// Set flat version (for backward compatibility)
|
|
60
|
+
this.root.style.setProperty(`--cs-neutral-${shade}`, value);
|
|
61
|
+
// Set nested version (for components that expect it)
|
|
62
|
+
this.root.style.setProperty(`--cs-colors-neutral-${shade}`, value);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Surface colors - set both flat and nested versions
|
|
66
|
+
this.root.style.setProperty('--cs-page-bg', colors.surface.background);
|
|
67
|
+
this.root.style.setProperty('--cs-colors-surface-background', colors.surface.background);
|
|
68
|
+
this.root.style.setProperty('--cs-surface-bg', colors.surface.surface);
|
|
69
|
+
this.root.style.setProperty('--cs-colors-surface-surface', colors.surface.surface);
|
|
70
|
+
this.root.style.setProperty('--cs-border', colors.surface.border);
|
|
71
|
+
this.root.style.setProperty('--cs-colors-border', colors.surface.border);
|
|
72
|
+
this.root.style.setProperty('--cs-divider', colors.surface.divider);
|
|
73
|
+
this.root.style.setProperty('--cs-colors-divider', colors.surface.divider);
|
|
74
|
+
|
|
75
|
+
// Text colors - set both flat and nested versions
|
|
76
|
+
this.root.style.setProperty('--cs-text-primary', colors.text.primary);
|
|
77
|
+
this.root.style.setProperty('--cs-colors-text-primary', colors.text.primary);
|
|
78
|
+
this.root.style.setProperty('--cs-text-secondary', colors.text.secondary);
|
|
79
|
+
this.root.style.setProperty('--cs-colors-text-secondary', colors.text.secondary);
|
|
80
|
+
this.root.style.setProperty('--cs-text-muted', colors.text.muted);
|
|
81
|
+
this.root.style.setProperty('--cs-colors-text-muted', colors.text.muted);
|
|
82
|
+
this.root.style.setProperty('--cs-text-inverse', colors.text.inverse);
|
|
83
|
+
this.root.style.setProperty('--cs-colors-text-inverse', colors.text.inverse);
|
|
84
|
+
this.root.style.setProperty('--cs-text-on-primary', colors.text.onPrimary);
|
|
85
|
+
this.root.style.setProperty('--cs-colors-text-on-primary', colors.text.onPrimary);
|
|
86
|
+
this.root.style.setProperty('--cs-text-on-secondary', colors.text.onSecondary);
|
|
87
|
+
this.root.style.setProperty('--cs-colors-text-on-secondary', colors.text.onSecondary);
|
|
88
|
+
this.root.style.setProperty('--cs-text-on-surface', colors.text.onSurface);
|
|
89
|
+
this.root.style.setProperty('--cs-colors-text-on-surface', colors.text.onSurface);
|
|
90
|
+
|
|
91
|
+
// Interactive colors - set both flat and nested versions
|
|
92
|
+
this.root.style.setProperty('--cs-hover-bg', colors.interactive.hover);
|
|
93
|
+
this.root.style.setProperty('--cs-colors-interactive-hover', colors.interactive.hover);
|
|
94
|
+
this.root.style.setProperty('--cs-active-bg', colors.interactive.active);
|
|
95
|
+
this.root.style.setProperty('--cs-colors-interactive-active', colors.interactive.active);
|
|
96
|
+
this.root.style.setProperty('--cs-focus', colors.interactive.focus);
|
|
97
|
+
this.root.style.setProperty('--cs-colors-interactive-focus', colors.interactive.focus);
|
|
98
|
+
this.root.style.setProperty('--cs-disabled', colors.interactive.disabled);
|
|
99
|
+
this.root.style.setProperty('--cs-colors-interactive-disabled', colors.interactive.disabled);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Apply font variables to CSS custom properties
|
|
104
|
+
*/
|
|
105
|
+
private applyFonts(fonts: CompleteThemeConfig['fonts']): void {
|
|
106
|
+
// Helper function to build font family string with fallbacks
|
|
107
|
+
const buildFontFamily = (config: any) => {
|
|
108
|
+
if (config.fallbacks && config.fallbacks.length > 0) {
|
|
109
|
+
return `${config.family}, ${config.fallbacks.join(', ')}`;
|
|
110
|
+
}
|
|
111
|
+
return config.family;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Primary font
|
|
115
|
+
this.root.style.setProperty('--cs-fonts-primary-family', buildFontFamily(fonts.primary));
|
|
116
|
+
this.root.style.setProperty('--cs-fonts-primary-weights', fonts.primary.weights.join(', '));
|
|
117
|
+
|
|
118
|
+
// Font sizes
|
|
119
|
+
Object.entries(fonts.primary.sizes).forEach(([size, value]) => {
|
|
120
|
+
this.root.style.setProperty(`--cs-fonts-primary-sizes-${size}`, value);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Line heights
|
|
124
|
+
Object.entries(fonts.primary.lineHeights).forEach(([height, value]) => {
|
|
125
|
+
this.root.style.setProperty(`--cs-fonts-primary-line-heights-${height}`, value);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Letter spacing
|
|
129
|
+
Object.entries(fonts.primary.letterSpacing).forEach(([spacing, value]) => {
|
|
130
|
+
this.root.style.setProperty(`--cs-fonts-primary-letter-spacing-${spacing}`, value);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Secondary font
|
|
134
|
+
this.root.style.setProperty('--cs-fonts-secondary-family', buildFontFamily(fonts.secondary));
|
|
135
|
+
|
|
136
|
+
// Display font
|
|
137
|
+
this.root.style.setProperty('--cs-fonts-display-family', buildFontFamily(fonts.display));
|
|
138
|
+
|
|
139
|
+
// Body font
|
|
140
|
+
this.root.style.setProperty('--cs-fonts-body-family', buildFontFamily(fonts.body));
|
|
141
|
+
|
|
142
|
+
// Mono font
|
|
143
|
+
this.root.style.setProperty('--cs-fonts-mono-family', buildFontFamily(fonts.mono));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Apply spacing variables to CSS custom properties
|
|
148
|
+
*/
|
|
149
|
+
private applySpacing(spacing: CompleteThemeConfig['spacing']): void {
|
|
150
|
+
// Scale spacing - set both flat and nested versions
|
|
151
|
+
Object.entries(spacing.scale).forEach(([size, value]) => {
|
|
152
|
+
// Set flat version (for backward compatibility)
|
|
153
|
+
this.root.style.setProperty(`--cs-spacing-${size}`, value);
|
|
154
|
+
// Set nested version (for badge CSS and other components)
|
|
155
|
+
this.root.style.setProperty(`--cs-spacing-scale-${size}`, value);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Component spacing
|
|
159
|
+
this.root.style.setProperty('--cs-button-padding', spacing.component.button.padding);
|
|
160
|
+
this.root.style.setProperty('--cs-button-margin', spacing.component.button.margin);
|
|
161
|
+
this.root.style.setProperty('--cs-button-gap', spacing.component.button.gap);
|
|
162
|
+
|
|
163
|
+
this.root.style.setProperty('--cs-input-padding', spacing.component.input.padding);
|
|
164
|
+
this.root.style.setProperty('--cs-input-margin', spacing.component.input.margin);
|
|
165
|
+
this.root.style.setProperty('--cs-input-gap', spacing.component.input.gap);
|
|
166
|
+
|
|
167
|
+
this.root.style.setProperty('--cs-card-padding', spacing.component.card.padding);
|
|
168
|
+
this.root.style.setProperty('--cs-card-margin', spacing.component.card.margin);
|
|
169
|
+
this.root.style.setProperty('--cs-card-gap', spacing.component.card.gap);
|
|
170
|
+
|
|
171
|
+
// Layout spacing
|
|
172
|
+
this.root.style.setProperty('--cs-page-spacing', spacing.layout.page);
|
|
173
|
+
this.root.style.setProperty('--cs-section-spacing', spacing.layout.section);
|
|
174
|
+
this.root.style.setProperty('--cs-container-spacing', spacing.layout.container);
|
|
175
|
+
this.root.style.setProperty('--cs-grid-spacing', spacing.layout.grid);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Apply shadow variables to CSS custom properties
|
|
180
|
+
*/
|
|
181
|
+
private applyShadows(shadows: CompleteThemeConfig['shadows']): void {
|
|
182
|
+
Object.entries(shadows).forEach(([shadow, value]) => {
|
|
183
|
+
// Set flat version (for backward compatibility)
|
|
184
|
+
this.root.style.setProperty(`--cs-shadow-${shadow}`, value);
|
|
185
|
+
// Set nested version (for badge CSS and other components)
|
|
186
|
+
this.root.style.setProperty(`--cs-shadows-${shadow}`, value);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Apply transition variables to CSS custom properties
|
|
192
|
+
*/
|
|
193
|
+
private applyTransitions(transitions: CompleteThemeConfig['transitions']): void {
|
|
194
|
+
// Duration - set both flat and nested versions
|
|
195
|
+
Object.entries(transitions.duration).forEach(([duration, value]) => {
|
|
196
|
+
// Set flat version (for backward compatibility)
|
|
197
|
+
this.root.style.setProperty(`--cs-transition-duration-${duration}`, value);
|
|
198
|
+
// Set nested version (for badge CSS and other components)
|
|
199
|
+
this.root.style.setProperty(`--cs-transitions-duration-${duration}`, value);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Easing - set both flat and nested versions
|
|
203
|
+
Object.entries(transitions.easing).forEach(([ease, value]) => {
|
|
204
|
+
// Set flat version (for backward compatibility)
|
|
205
|
+
this.root.style.setProperty(`--cs-transition-easing-${ease}`, value);
|
|
206
|
+
// Set nested version (for badge CSS and other components)
|
|
207
|
+
this.root.style.setProperty(`--cs-transitions-easing-${ease}`, value);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Properties - set both flat and nested versions
|
|
211
|
+
Object.entries(transitions.properties).forEach(([property, value]) => {
|
|
212
|
+
// Set flat version (for backward compatibility)
|
|
213
|
+
this.root.style.setProperty(`--cs-transition-${property}`, value);
|
|
214
|
+
// Set nested version (for badge CSS and other components)
|
|
215
|
+
this.root.style.setProperty(`--cs-transitions-properties-${property}`, value);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Apply border radius variables to CSS custom properties
|
|
221
|
+
*/
|
|
222
|
+
private applyBorderRadius(borderRadius: CompleteThemeConfig['borderRadius']): void {
|
|
223
|
+
Object.entries(borderRadius).forEach(([size, value]) => {
|
|
224
|
+
// Set flat version (for backward compatibility)
|
|
225
|
+
this.root.style.setProperty(`--cs-border-radius-${size}`, value);
|
|
226
|
+
// Set nested version (for badge CSS and other components)
|
|
227
|
+
this.root.style.setProperty(`--cs-border-radius-${size}`, value);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Clear all theme CSS variables
|
|
233
|
+
*/
|
|
234
|
+
clearTheme(): void {
|
|
235
|
+
const cssVars = [
|
|
236
|
+
'--cs-primary-50', '--cs-primary-100', '--cs-primary-200', '--cs-primary-300',
|
|
237
|
+
'--cs-primary-400', '--cs-primary-500', '--cs-primary-600', '--cs-primary-700',
|
|
238
|
+
'--cs-primary-800', '--cs-primary-900',
|
|
239
|
+
'--cs-secondary-50', '--cs-secondary-100', '--cs-secondary-200', '--cs-secondary-300',
|
|
240
|
+
'--cs-secondary-400', '--cs-secondary-500', '--cs-secondary-600', '--cs-secondary-700',
|
|
241
|
+
'--cs-secondary-800', '--cs-secondary-900',
|
|
242
|
+
'--cs-success', '--cs-warning', '--cs-error', '--cs-info',
|
|
243
|
+
'--cs-neutral-50', '--cs-neutral-100', '--cs-neutral-200', '--cs-neutral-300',
|
|
244
|
+
'--cs-neutral-400', '--cs-neutral-500', '--cs-neutral-600', '--cs-neutral-700',
|
|
245
|
+
'--cs-neutral-800', '--cs-neutral-900',
|
|
246
|
+
'--cs-page-bg', '--cs-surface-bg', '--cs-border', '--cs-divider',
|
|
247
|
+
'--cs-text-primary', '--cs-text-secondary', '--cs-text-muted', '--cs-text-inverse',
|
|
248
|
+
'--cs-text-on-primary', '--cs-text-on-secondary', '--cs-text-on-surface',
|
|
249
|
+
'--cs-hover-bg', '--cs-active-bg', '--cs-focus', '--cs-disabled',
|
|
250
|
+
'--cs-font-primary', '--cs-font-secondary', '--cs-font-display', '--cs-font-body', '--cs-font-mono'
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
cssVars.forEach(varName => {
|
|
254
|
+
this.root.style.removeProperty(varName);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Default theme CSS updater instance
|
|
261
|
+
*/
|
|
262
|
+
export const themeCSSUpdater = new ThemeCSSUpdater();
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme persistence utilities for managing theme preferences in localStorage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ThemePersistenceOptions {
|
|
6
|
+
storageKey?: string;
|
|
7
|
+
defaultTheme?: string;
|
|
8
|
+
enableSystemPreference?: boolean;
|
|
9
|
+
enableSystemThemeDetection?: boolean;
|
|
10
|
+
systemThemeStorageKey?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ThemePersistence {
|
|
14
|
+
private storageKey: string;
|
|
15
|
+
private defaultTheme: string;
|
|
16
|
+
private enableSystemPreference: boolean;
|
|
17
|
+
private enableSystemThemeDetection: boolean;
|
|
18
|
+
private systemThemeStorageKey: string;
|
|
19
|
+
|
|
20
|
+
constructor(options: ThemePersistenceOptions = {}) {
|
|
21
|
+
this.storageKey = options.storageKey || 'stan-design-theme';
|
|
22
|
+
this.defaultTheme = options.defaultTheme || 'stan-design';
|
|
23
|
+
this.enableSystemPreference = options.enableSystemPreference ?? true;
|
|
24
|
+
this.enableSystemThemeDetection = options.enableSystemThemeDetection ?? true;
|
|
25
|
+
this.systemThemeStorageKey = options.systemThemeStorageKey || 'stan-design-system-theme';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the stored theme preference
|
|
30
|
+
* Falls back to system preference if enabled, then default theme
|
|
31
|
+
*/
|
|
32
|
+
getStoredTheme(): string {
|
|
33
|
+
try {
|
|
34
|
+
// Check localStorage first
|
|
35
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
36
|
+
if (stored) {
|
|
37
|
+
return stored;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fall back to system preference if enabled
|
|
41
|
+
if (this.enableSystemPreference) {
|
|
42
|
+
const systemPreference = this.getSystemPreference();
|
|
43
|
+
if (systemPreference) {
|
|
44
|
+
return systemPreference;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Finally fall back to default
|
|
49
|
+
return this.defaultTheme;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn('Failed to read theme from storage:', error);
|
|
52
|
+
return this.defaultTheme;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Store the theme preference
|
|
58
|
+
*/
|
|
59
|
+
setStoredTheme(themeName: string): void {
|
|
60
|
+
try {
|
|
61
|
+
localStorage.setItem(this.storageKey, themeName);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.warn('Failed to store theme preference:', error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Clear the stored theme preference
|
|
69
|
+
*/
|
|
70
|
+
clearStoredTheme(): void {
|
|
71
|
+
try {
|
|
72
|
+
localStorage.removeItem(this.storageKey);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.warn('Failed to clear theme preference:', error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get system color scheme preference
|
|
80
|
+
*/
|
|
81
|
+
private getSystemPreference(): string | null {
|
|
82
|
+
try {
|
|
83
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
84
|
+
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
85
|
+
return darkModeQuery.matches ? 'dark' : 'light';
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.warn('Failed to detect system preference:', error);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Listen for system preference changes
|
|
95
|
+
*/
|
|
96
|
+
onSystemPreferenceChange(callback: (preference: string) => void): (() => void) | null {
|
|
97
|
+
try {
|
|
98
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
99
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
100
|
+
|
|
101
|
+
const handleChange = (event: MediaQueryListEvent) => {
|
|
102
|
+
const preference = event.matches ? 'dark' : 'light';
|
|
103
|
+
callback(preference);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Modern browsers
|
|
107
|
+
if (mediaQuery.addEventListener) {
|
|
108
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
109
|
+
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
110
|
+
}
|
|
111
|
+
// Legacy browsers
|
|
112
|
+
else if (mediaQuery.addListener) {
|
|
113
|
+
mediaQuery.addListener(handleChange);
|
|
114
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.warn('Failed to set up system preference listener:', error);
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if localStorage is available
|
|
125
|
+
*/
|
|
126
|
+
isStorageAvailable(): boolean {
|
|
127
|
+
try {
|
|
128
|
+
const test = '__test__';
|
|
129
|
+
localStorage.setItem(test, test);
|
|
130
|
+
localStorage.removeItem(test);
|
|
131
|
+
return true;
|
|
132
|
+
} catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get all available storage keys related to themes
|
|
139
|
+
*/
|
|
140
|
+
getThemeKeys(): string[] {
|
|
141
|
+
try {
|
|
142
|
+
const keys: string[] = [];
|
|
143
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
144
|
+
const key = localStorage.key(i);
|
|
145
|
+
if (key && key.includes('theme')) {
|
|
146
|
+
keys.push(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return keys;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn('Failed to get theme keys:', error);
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get stored system theme preference
|
|
158
|
+
*/
|
|
159
|
+
getStoredSystemTheme(): 'light' | 'dark' | null {
|
|
160
|
+
try {
|
|
161
|
+
const stored = localStorage.getItem(this.systemThemeStorageKey);
|
|
162
|
+
if (stored === 'light' || stored === 'dark') {
|
|
163
|
+
return stored;
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn('Failed to read system theme from storage:', error);
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Store system theme preference
|
|
173
|
+
*/
|
|
174
|
+
setStoredSystemTheme(theme: 'light' | 'dark'): void {
|
|
175
|
+
try {
|
|
176
|
+
localStorage.setItem(this.systemThemeStorageKey, theme);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.warn('Failed to store system theme preference:', error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Clear stored system theme preference
|
|
184
|
+
*/
|
|
185
|
+
clearStoredSystemTheme(): void {
|
|
186
|
+
try {
|
|
187
|
+
localStorage.removeItem(this.systemThemeStorageKey);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.warn('Failed to clear system theme preference:', error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get comprehensive theme state including system theme
|
|
195
|
+
*/
|
|
196
|
+
getThemeState(): {
|
|
197
|
+
theme: string;
|
|
198
|
+
systemTheme: 'light' | 'dark' | null;
|
|
199
|
+
isSystemThemeEnabled: boolean;
|
|
200
|
+
} {
|
|
201
|
+
return {
|
|
202
|
+
theme: this.getStoredTheme(),
|
|
203
|
+
systemTheme: this.getStoredSystemTheme(),
|
|
204
|
+
isSystemThemeEnabled: this.enableSystemThemeDetection
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Set comprehensive theme state
|
|
210
|
+
*/
|
|
211
|
+
setThemeState(state: {
|
|
212
|
+
theme?: string;
|
|
213
|
+
systemTheme?: 'light' | 'dark';
|
|
214
|
+
enableSystemTheme?: boolean;
|
|
215
|
+
}): void {
|
|
216
|
+
if (state.theme !== undefined) {
|
|
217
|
+
this.setStoredTheme(state.theme);
|
|
218
|
+
}
|
|
219
|
+
if (state.systemTheme !== undefined) {
|
|
220
|
+
this.setStoredSystemTheme(state.systemTheme);
|
|
221
|
+
}
|
|
222
|
+
if (state.enableSystemTheme !== undefined) {
|
|
223
|
+
this.enableSystemThemeDetection = state.enableSystemTheme;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Default theme persistence instance
|
|
230
|
+
*/
|
|
231
|
+
export const defaultThemePersistence = new ThemePersistence();
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create a custom theme persistence instance
|
|
235
|
+
*/
|
|
236
|
+
export const createThemePersistence = (options: ThemePersistenceOptions) => {
|
|
237
|
+
return new ThemePersistence(options);
|
|
238
|
+
};
|