@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.3 → 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,462 @@
|
|
|
1
|
+
// Theme Validation System
|
|
2
|
+
// This file provides comprehensive validation for theme configurations
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
MultiThemeConfig,
|
|
6
|
+
ThemeValidationResult,
|
|
7
|
+
ThemeValidationError,
|
|
8
|
+
ThemeValidationWarning
|
|
9
|
+
} from './types';
|
|
10
|
+
import { ColorManager } from './colorManager';
|
|
11
|
+
|
|
12
|
+
export class ThemeValidator {
|
|
13
|
+
private static readonly REQUIRED_FIELDS = [
|
|
14
|
+
'fonts.primary.family',
|
|
15
|
+
'fonts.primary.sizes.md',
|
|
16
|
+
'colors.primary.500',
|
|
17
|
+
'colors.semantic.success',
|
|
18
|
+
'colors.semantic.error',
|
|
19
|
+
'colors.semantic.warning',
|
|
20
|
+
'colors.semantic.info',
|
|
21
|
+
'colors.surface.background',
|
|
22
|
+
'colors.text.primary',
|
|
23
|
+
'spacing.scale.md',
|
|
24
|
+
'shadows.md',
|
|
25
|
+
'transitions.duration.normal',
|
|
26
|
+
'transitions.easing.ease',
|
|
27
|
+
'meta.name',
|
|
28
|
+
'meta.description',
|
|
29
|
+
'meta.version'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
private static readonly COLOR_FORMATS = {
|
|
33
|
+
hex: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
|
|
34
|
+
rgb: /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/,
|
|
35
|
+
rgba: /^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)$/,
|
|
36
|
+
hsl: /^hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)$/,
|
|
37
|
+
hsla: /^hsla\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*,\s*[\d.]+\s*\)$/
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
private static readonly FONT_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate a complete theme configuration
|
|
44
|
+
*/
|
|
45
|
+
static validateTheme(theme: MultiThemeConfig): ThemeValidationResult {
|
|
46
|
+
const errors: ThemeValidationError[] = [];
|
|
47
|
+
const warnings: ThemeValidationWarning[] = [];
|
|
48
|
+
|
|
49
|
+
// Validate required fields
|
|
50
|
+
this.validateRequiredFields(theme, errors);
|
|
51
|
+
|
|
52
|
+
// Validate color formats
|
|
53
|
+
this.validateColorFormats(theme, errors, warnings);
|
|
54
|
+
|
|
55
|
+
// Validate color contrast
|
|
56
|
+
this.validateColorContrast(theme, warnings);
|
|
57
|
+
|
|
58
|
+
// Validate font configurations
|
|
59
|
+
this.validateFontConfigurations(theme, warnings);
|
|
60
|
+
|
|
61
|
+
// Validate spacing scales
|
|
62
|
+
this.validateSpacingScales(theme, warnings);
|
|
63
|
+
|
|
64
|
+
// Validate navigation configurations
|
|
65
|
+
this.validateNavigationConfigurations(theme, errors, warnings);
|
|
66
|
+
|
|
67
|
+
// Validate metadata
|
|
68
|
+
this.validateMetadata(theme, errors, warnings);
|
|
69
|
+
|
|
70
|
+
// Validate semantic consistency
|
|
71
|
+
this.validateSemanticConsistency(theme, warnings);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
isValid: errors.length === 0,
|
|
75
|
+
errors,
|
|
76
|
+
warnings
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate that all required fields are present
|
|
82
|
+
*/
|
|
83
|
+
private static validateRequiredFields(theme: MultiThemeConfig, errors: ThemeValidationError[]): void {
|
|
84
|
+
for (const fieldPath of this.REQUIRED_FIELDS) {
|
|
85
|
+
const value = this.getNestedValue(theme, fieldPath);
|
|
86
|
+
if (value === undefined || value === null || value === '') {
|
|
87
|
+
errors.push({
|
|
88
|
+
path: fieldPath,
|
|
89
|
+
message: `Required field '${fieldPath}' is missing or empty`,
|
|
90
|
+
severity: 'error'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Validate color format consistency
|
|
98
|
+
*/
|
|
99
|
+
private static validateColorFormats(theme: MultiThemeConfig, errors: ThemeValidationError[], warnings: ThemeValidationWarning[]): void {
|
|
100
|
+
const colorFields = this.getAllColorFields(theme);
|
|
101
|
+
const formats = new Set<string>();
|
|
102
|
+
|
|
103
|
+
for (const { path, value } of colorFields) {
|
|
104
|
+
if (typeof value === 'string') {
|
|
105
|
+
// Use colorManager for validation
|
|
106
|
+
if (!ColorManager.isValidColor(value)) {
|
|
107
|
+
errors.push({
|
|
108
|
+
path,
|
|
109
|
+
message: `Invalid color format: '${value}'. Expected hex, rgb, hsl, or named color format`,
|
|
110
|
+
severity: 'error'
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
const format = this.detectColorFormat(value);
|
|
114
|
+
if (format) {
|
|
115
|
+
formats.add(format);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Warn if multiple color formats are used
|
|
122
|
+
if (formats.size > 1) {
|
|
123
|
+
warnings.push({
|
|
124
|
+
path: 'colors',
|
|
125
|
+
message: `Multiple color formats detected: ${Array.from(formats).join(', ')}. Consider using a consistent format for better maintainability`,
|
|
126
|
+
severity: 'warning'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate color contrast ratios
|
|
133
|
+
*/
|
|
134
|
+
private static validateColorContrast(theme: MultiThemeConfig, warnings: ThemeValidationWarning[]): void {
|
|
135
|
+
const textColors = [
|
|
136
|
+
theme.colors?.text?.primary,
|
|
137
|
+
theme.colors?.text?.secondary,
|
|
138
|
+
theme.colors?.text?.muted
|
|
139
|
+
].filter(Boolean);
|
|
140
|
+
|
|
141
|
+
const backgroundColors = [
|
|
142
|
+
theme.colors?.surface?.background,
|
|
143
|
+
theme.colors?.surface?.surface
|
|
144
|
+
].filter(Boolean);
|
|
145
|
+
|
|
146
|
+
for (const textColor of textColors) {
|
|
147
|
+
for (const bgColor of backgroundColors) {
|
|
148
|
+
if (textColor && bgColor) {
|
|
149
|
+
// Use colorManager for contrast checking
|
|
150
|
+
const accessibility = ColorManager.checkColorContrast(textColor, bgColor);
|
|
151
|
+
|
|
152
|
+
if (!accessibility.aa) {
|
|
153
|
+
warnings.push({
|
|
154
|
+
path: 'colors',
|
|
155
|
+
message: `Color contrast ratio ${accessibility.contrastRatio.toFixed(2)}:1 between '${textColor}' and '${bgColor}' is below WCAG AA standard (4.5:1)`,
|
|
156
|
+
severity: 'warning'
|
|
157
|
+
});
|
|
158
|
+
} else if (!accessibility.aaa) {
|
|
159
|
+
warnings.push({
|
|
160
|
+
path: 'colors',
|
|
161
|
+
message: `Color contrast ratio ${accessibility.contrastRatio.toFixed(2)}:1 between '${textColor}' and '${bgColor}' is below WCAG AAA standard (7:1)`,
|
|
162
|
+
severity: 'info'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Add accessibility recommendations
|
|
167
|
+
if (accessibility.recommended.length > 0) {
|
|
168
|
+
warnings.push({
|
|
169
|
+
path: 'colors',
|
|
170
|
+
message: `Accessibility recommendations: ${accessibility.recommended.join('; ')}`,
|
|
171
|
+
severity: 'info'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Validate font configurations
|
|
181
|
+
*/
|
|
182
|
+
private static validateFontConfigurations(theme: MultiThemeConfig, warnings: ThemeValidationWarning[]): void {
|
|
183
|
+
// Validate font weights
|
|
184
|
+
if (theme.fonts?.primary?.weights) {
|
|
185
|
+
for (const weight of theme.fonts.primary.weights) {
|
|
186
|
+
if (!this.FONT_WEIGHTS.includes(weight)) {
|
|
187
|
+
warnings.push({
|
|
188
|
+
path: 'fonts.primary.weights',
|
|
189
|
+
message: `Font weight ${weight} is not a standard CSS font weight. Consider using: ${this.FONT_WEIGHTS.join(', ')}`,
|
|
190
|
+
severity: 'warning'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Validate font size scale
|
|
197
|
+
if (theme.fonts?.primary?.sizes) {
|
|
198
|
+
const sizes = Object.values(theme.fonts.primary.sizes);
|
|
199
|
+
const numericSizes = sizes.map(size => parseFloat(size)).filter(size => !isNaN(size));
|
|
200
|
+
|
|
201
|
+
if (numericSizes.length > 1) {
|
|
202
|
+
for (let i = 1; i < numericSizes.length; i++) {
|
|
203
|
+
if (numericSizes[i] <= numericSizes[i - 1]) {
|
|
204
|
+
warnings.push({
|
|
205
|
+
path: 'fonts.primary.sizes',
|
|
206
|
+
message: `Font sizes should be in ascending order. Found: ${numericSizes[i - 1]} followed by ${numericSizes[i]}`,
|
|
207
|
+
severity: 'warning'
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Validate font family fallbacks
|
|
215
|
+
if (theme.fonts?.primary?.family) {
|
|
216
|
+
const family = theme.fonts.primary.family;
|
|
217
|
+
if (!family.includes(',') && !family.includes('"') && !family.includes("'")) {
|
|
218
|
+
warnings.push({
|
|
219
|
+
path: 'fonts.primary.family',
|
|
220
|
+
message: `Font family '${family}' should include fallback fonts for better cross-platform compatibility`,
|
|
221
|
+
severity: 'warning'
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Validate spacing scales
|
|
229
|
+
*/
|
|
230
|
+
private static validateSpacingScales(theme: MultiThemeConfig, warnings: ThemeValidationWarning[]): void {
|
|
231
|
+
if (theme.spacing?.scale) {
|
|
232
|
+
const scales = Object.values(theme.spacing.scale);
|
|
233
|
+
const numericScales = scales.map(scale => parseFloat(scale)).filter(scale => !isNaN(scale));
|
|
234
|
+
|
|
235
|
+
if (numericScales.length > 1) {
|
|
236
|
+
// Check if spacing follows a consistent pattern
|
|
237
|
+
const ratios = [];
|
|
238
|
+
for (let i = 1; i < numericScales.length; i++) {
|
|
239
|
+
ratios.push(numericScales[i] / numericScales[i - 1]);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const avgRatio = ratios.reduce((sum, ratio) => sum + ratio, 0) / ratios.length;
|
|
243
|
+
const variance = ratios.reduce((sum, ratio) => sum + Math.pow(ratio - avgRatio, 2), 0) / ratios.length;
|
|
244
|
+
|
|
245
|
+
if (variance > 0.1) { // High variance indicates inconsistent spacing
|
|
246
|
+
warnings.push({
|
|
247
|
+
path: 'spacing.scale',
|
|
248
|
+
message: `Spacing scale appears inconsistent. Consider using a mathematical scale (e.g., 1.5x or 2x multiplier) for better visual harmony`,
|
|
249
|
+
severity: 'warning'
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Validate navigation configurations
|
|
258
|
+
*/
|
|
259
|
+
private static validateNavigationConfigurations(theme: MultiThemeConfig, _errors: ThemeValidationError[], warnings: ThemeValidationWarning[]): void {
|
|
260
|
+
if (theme.navigation) {
|
|
261
|
+
// Validate layout and behavior combinations
|
|
262
|
+
const { layout, behavior } = theme.navigation;
|
|
263
|
+
|
|
264
|
+
if (layout === 'sidebar' && behavior === 'floating') {
|
|
265
|
+
warnings.push({
|
|
266
|
+
path: 'navigation',
|
|
267
|
+
message: 'Sidebar layout with floating behavior may cause usability issues. Consider using static or sticky behavior',
|
|
268
|
+
severity: 'warning'
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (layout === 'tabs' && behavior === 'overlay') {
|
|
273
|
+
warnings.push({
|
|
274
|
+
path: 'navigation',
|
|
275
|
+
message: 'Tab layout with overlay behavior is unusual. Consider using static or sticky behavior',
|
|
276
|
+
severity: 'warning'
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Validate responsive behavior
|
|
281
|
+
if (theme.navigation.responsive === 'desktop-first' && layout === 'horizontal') {
|
|
282
|
+
warnings.push({
|
|
283
|
+
path: 'navigation',
|
|
284
|
+
message: 'Desktop-first responsive approach with horizontal layout may cause mobile usability issues',
|
|
285
|
+
severity: 'warning'
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Validate metadata
|
|
293
|
+
*/
|
|
294
|
+
private static validateMetadata(theme: MultiThemeConfig, errors: ThemeValidationError[], warnings: ThemeValidationWarning[]): void {
|
|
295
|
+
if (theme.meta) {
|
|
296
|
+
// Validate version format
|
|
297
|
+
if (theme.meta.version && !/^\d+\.\d+\.\d+/.test(theme.meta.version)) {
|
|
298
|
+
warnings.push({
|
|
299
|
+
path: 'meta.version',
|
|
300
|
+
message: `Version '${theme.meta.version}' should follow semantic versioning (e.g., 1.0.0)`,
|
|
301
|
+
severity: 'warning'
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Validate category
|
|
306
|
+
if (theme.meta.category && !['brand', 'enterprise', 'custom', 'accessibility'].includes(theme.meta.category)) {
|
|
307
|
+
errors.push({
|
|
308
|
+
path: 'meta.category',
|
|
309
|
+
message: `Invalid category '${theme.meta.category}'. Must be one of: brand, enterprise, custom, accessibility`,
|
|
310
|
+
severity: 'error'
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validate tags
|
|
315
|
+
if (theme.meta.tags && theme.meta.tags.length > 10) {
|
|
316
|
+
warnings.push({
|
|
317
|
+
path: 'meta.tags',
|
|
318
|
+
message: `Theme has ${theme.meta.tags.length} tags. Consider limiting to 10 tags for better organization`,
|
|
319
|
+
severity: 'warning'
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Validate semantic consistency
|
|
327
|
+
*/
|
|
328
|
+
private static validateSemanticConsistency(theme: MultiThemeConfig, warnings: ThemeValidationWarning[]): void {
|
|
329
|
+
// Check if semantic colors are distinct from primary colors
|
|
330
|
+
if (theme.colors?.semantic && theme.colors?.primary) {
|
|
331
|
+
const semanticColors = Object.values(theme.colors.semantic);
|
|
332
|
+
const primaryColors = Object.values(theme.colors.primary);
|
|
333
|
+
|
|
334
|
+
for (const semanticColor of semanticColors) {
|
|
335
|
+
for (const primaryColor of primaryColors) {
|
|
336
|
+
if (this.colorsAreSimilar(semanticColor, primaryColor)) {
|
|
337
|
+
warnings.push({
|
|
338
|
+
path: 'colors.semantic',
|
|
339
|
+
message: `Semantic color '${semanticColor}' is too similar to primary color '${primaryColor}'. Consider using more distinct colors for better semantic clarity`,
|
|
340
|
+
severity: 'warning'
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Helper: Get nested value from object using dot notation
|
|
350
|
+
*/
|
|
351
|
+
private static getNestedValue(obj: any, path: string): any {
|
|
352
|
+
return path.split('.').reduce((current, key) => current?.[key], obj);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Helper: Get all color fields from theme
|
|
357
|
+
*/
|
|
358
|
+
private static getAllColorFields(theme: MultiThemeConfig): Array<{ path: string; value: any }> {
|
|
359
|
+
const fields: Array<{ path: string; value: any }> = [];
|
|
360
|
+
|
|
361
|
+
const traverseColors = (obj: any, path: string) => {
|
|
362
|
+
if (obj && typeof obj === 'object') {
|
|
363
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
364
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
365
|
+
if (typeof value === 'string' && this.isColorValue(value)) {
|
|
366
|
+
fields.push({ path: currentPath, value });
|
|
367
|
+
} else if (typeof value === 'object') {
|
|
368
|
+
traverseColors(value, currentPath);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
traverseColors(theme.colors, 'colors');
|
|
375
|
+
return fields;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Helper: Detect color format
|
|
380
|
+
*/
|
|
381
|
+
private static detectColorFormat(color: string): string | null {
|
|
382
|
+
for (const [format, regex] of Object.entries(this.COLOR_FORMATS)) {
|
|
383
|
+
if (regex.test(color)) {
|
|
384
|
+
return format;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Helper: Check if value looks like a color
|
|
392
|
+
*/
|
|
393
|
+
private static isColorValue(value: string): boolean {
|
|
394
|
+
// Accept any string that might be a color, let detectColorFormat validate it
|
|
395
|
+
return value.startsWith('#') ||
|
|
396
|
+
value.startsWith('rgb') ||
|
|
397
|
+
value.startsWith('hsl') ||
|
|
398
|
+
value.startsWith('var(--') ||
|
|
399
|
+
value === 'transparent' ||
|
|
400
|
+
value === 'currentColor' ||
|
|
401
|
+
value === 'inherit' ||
|
|
402
|
+
// Also accept any string that might be a color (for validation)
|
|
403
|
+
value.includes('color') ||
|
|
404
|
+
value.includes('invalid');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Helper: Calculate color contrast ratio
|
|
409
|
+
*/
|
|
410
|
+
private static calculateColorContrast(color1: string, color2: string): number {
|
|
411
|
+
try {
|
|
412
|
+
const luminance1 = this.getLuminance(color1);
|
|
413
|
+
const luminance2 = this.getLuminance(color2);
|
|
414
|
+
|
|
415
|
+
const lighter = Math.max(luminance1, luminance2);
|
|
416
|
+
const darker = Math.min(luminance1, luminance2);
|
|
417
|
+
|
|
418
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
419
|
+
} catch {
|
|
420
|
+
return 1; // Default fallback
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Helper: Get luminance of a color
|
|
426
|
+
*/
|
|
427
|
+
private static getLuminance(color: string): number {
|
|
428
|
+
// Handle CSS variables and other formats
|
|
429
|
+
if (color.startsWith('var(--') || color === 'transparent' || color === 'currentColor' || color === 'inherit') {
|
|
430
|
+
return 0.5; // Default fallback
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Convert hex to RGB and calculate luminance
|
|
434
|
+
try {
|
|
435
|
+
const hex = color.replace('#', '');
|
|
436
|
+
const r = parseInt(hex.substr(0, 2), 16) / 255;
|
|
437
|
+
const g = parseInt(hex.substr(2, 2), 16) / 255;
|
|
438
|
+
const b = parseInt(hex.substr(4, 2), 16) / 255;
|
|
439
|
+
|
|
440
|
+
const [rs, gs, bs] = [r, g, b].map(c => {
|
|
441
|
+
if (c <= 0.03928) return c / 12.92;
|
|
442
|
+
return Math.pow((c + 0.055) / 1.055, 2.4);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
446
|
+
} catch {
|
|
447
|
+
return 0.5; // Default fallback
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Helper: Check if two colors are similar
|
|
453
|
+
*/
|
|
454
|
+
private static colorsAreSimilar(color1: string, color2: string): boolean {
|
|
455
|
+
try {
|
|
456
|
+
const contrast = this.calculateColorContrast(color1, color2);
|
|
457
|
+
return contrast < 2.0; // Very low contrast indicates similar colors
|
|
458
|
+
} catch {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Core token types and interfaces
|
|
2
|
+
export type {
|
|
3
|
+
DesignToken,
|
|
4
|
+
TokenType,
|
|
5
|
+
ColorToken,
|
|
6
|
+
FontToken,
|
|
7
|
+
SpacingToken,
|
|
8
|
+
ShadowToken,
|
|
9
|
+
TransitionToken,
|
|
10
|
+
TokenGroup,
|
|
11
|
+
ThemeTokens,
|
|
12
|
+
TokenExportOptions
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
// Token generation
|
|
16
|
+
export { TokenGenerator } from './tokenGenerator';
|
|
17
|
+
|
|
18
|
+
// Token export functionality
|
|
19
|
+
export { TokenExporter } from './tokenExporter';
|
|
20
|
+
|
|
21
|
+
// Token validation
|
|
22
|
+
export type { ValidationResult, ValidationError, ValidationWarning } from './tokenValidator';
|
|
23
|
+
export { TokenValidator } from './tokenValidator';
|
|
24
|
+
|
|
25
|
+
// Main token manager
|
|
26
|
+
export type { TokenManagerOptions } from './tokenManager';
|
|
27
|
+
export { TokenManager } from './tokenManager';
|
|
28
|
+
|
|
29
|
+
// Convenience functions
|
|
30
|
+
import { TokenManager, type TokenManagerOptions } from './tokenManager';
|
|
31
|
+
export const createTokenManager = (options?: TokenManagerOptions) => new TokenManager(options);
|
|
32
|
+
|
|
33
|
+
// Default token manager instance
|
|
34
|
+
export const defaultTokenManager = new TokenManager();
|