@sc4rfurryx/proteusjs 1.0.0
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/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,591 @@
|
|
1
|
+
/**
|
2
|
+
* Smart Theme System for ProteusJS
|
3
|
+
* Intelligent dark mode, adaptive contrast, and dynamic theming
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { logger } from '../utils/Logger';
|
7
|
+
|
8
|
+
export interface ThemeConfig {
|
9
|
+
autoDetect: boolean;
|
10
|
+
respectSystemPreference: boolean;
|
11
|
+
adaptiveContrast: boolean;
|
12
|
+
colorSchemes: Record<string, ColorScheme>;
|
13
|
+
transitions: boolean;
|
14
|
+
transitionDuration: number;
|
15
|
+
storage: 'localStorage' | 'sessionStorage' | 'none';
|
16
|
+
storageKey: string;
|
17
|
+
}
|
18
|
+
|
19
|
+
export interface ColorScheme {
|
20
|
+
name: string;
|
21
|
+
type: 'light' | 'dark' | 'auto';
|
22
|
+
colors: Record<string, string>;
|
23
|
+
contrast: 'normal' | 'high' | 'low';
|
24
|
+
accessibility: {
|
25
|
+
minContrast: number;
|
26
|
+
largeTextContrast: number;
|
27
|
+
focusIndicator: string;
|
28
|
+
};
|
29
|
+
}
|
30
|
+
|
31
|
+
export interface ThemeState {
|
32
|
+
currentScheme: string;
|
33
|
+
systemPreference: 'light' | 'dark';
|
34
|
+
userPreference: string | null;
|
35
|
+
contrastLevel: 'normal' | 'high' | 'low';
|
36
|
+
reducedMotion: boolean;
|
37
|
+
highContrast: boolean;
|
38
|
+
}
|
39
|
+
|
40
|
+
export class SmartThemeSystem {
|
41
|
+
private config!: Required<ThemeConfig>;
|
42
|
+
private state!: ThemeState;
|
43
|
+
private mediaQueries: Map<string, MediaQueryList> = new Map();
|
44
|
+
private observers: Set<(state: ThemeState) => void> = new Set();
|
45
|
+
|
46
|
+
private static readonly DEFAULT_SCHEMES: Record<string, ColorScheme> = {
|
47
|
+
light: {
|
48
|
+
name: 'Light',
|
49
|
+
type: 'light',
|
50
|
+
colors: {
|
51
|
+
primary: '#007bff',
|
52
|
+
secondary: '#6c757d',
|
53
|
+
success: '#28a745',
|
54
|
+
danger: '#dc3545',
|
55
|
+
warning: '#ffc107',
|
56
|
+
info: '#17a2b8',
|
57
|
+
background: '#ffffff',
|
58
|
+
surface: '#f8f9fa',
|
59
|
+
text: '#212529',
|
60
|
+
textSecondary: '#6c757d',
|
61
|
+
border: '#dee2e6'
|
62
|
+
},
|
63
|
+
contrast: 'normal',
|
64
|
+
accessibility: {
|
65
|
+
minContrast: 4.5,
|
66
|
+
largeTextContrast: 3,
|
67
|
+
focusIndicator: '#005cbf'
|
68
|
+
}
|
69
|
+
},
|
70
|
+
dark: {
|
71
|
+
name: 'Dark',
|
72
|
+
type: 'dark',
|
73
|
+
colors: {
|
74
|
+
primary: '#0d6efd',
|
75
|
+
secondary: '#6c757d',
|
76
|
+
success: '#198754',
|
77
|
+
danger: '#dc3545',
|
78
|
+
warning: '#fd7e14',
|
79
|
+
info: '#0dcaf0',
|
80
|
+
background: '#121212',
|
81
|
+
surface: '#1e1e1e',
|
82
|
+
text: '#ffffff',
|
83
|
+
textSecondary: '#adb5bd',
|
84
|
+
border: '#495057'
|
85
|
+
},
|
86
|
+
contrast: 'normal',
|
87
|
+
accessibility: {
|
88
|
+
minContrast: 4.5,
|
89
|
+
largeTextContrast: 3,
|
90
|
+
focusIndicator: '#4dabf7'
|
91
|
+
}
|
92
|
+
},
|
93
|
+
highContrast: {
|
94
|
+
name: 'High Contrast',
|
95
|
+
type: 'light',
|
96
|
+
colors: {
|
97
|
+
primary: '#0000ff',
|
98
|
+
secondary: '#000000',
|
99
|
+
success: '#008000',
|
100
|
+
danger: '#ff0000',
|
101
|
+
warning: '#ff8c00',
|
102
|
+
info: '#0000ff',
|
103
|
+
background: '#ffffff',
|
104
|
+
surface: '#ffffff',
|
105
|
+
text: '#000000',
|
106
|
+
textSecondary: '#000000',
|
107
|
+
border: '#000000'
|
108
|
+
},
|
109
|
+
contrast: 'high',
|
110
|
+
accessibility: {
|
111
|
+
minContrast: 7,
|
112
|
+
largeTextContrast: 4.5,
|
113
|
+
focusIndicator: '#ff0000'
|
114
|
+
}
|
115
|
+
}
|
116
|
+
};
|
117
|
+
|
118
|
+
constructor(config: Partial<ThemeConfig> = {}) {
|
119
|
+
this.config = {
|
120
|
+
autoDetect: true,
|
121
|
+
respectSystemPreference: true,
|
122
|
+
adaptiveContrast: true,
|
123
|
+
colorSchemes: SmartThemeSystem.DEFAULT_SCHEMES,
|
124
|
+
transitions: true,
|
125
|
+
transitionDuration: 300,
|
126
|
+
storage: 'localStorage',
|
127
|
+
storageKey: 'proteus-theme',
|
128
|
+
...config
|
129
|
+
};
|
130
|
+
|
131
|
+
this.state = this.createInitialState();
|
132
|
+
this.setupMediaQueries();
|
133
|
+
this.loadStoredPreference();
|
134
|
+
this.applyInitialTheme();
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Set theme scheme
|
139
|
+
*/
|
140
|
+
public setScheme(schemeName: string): void {
|
141
|
+
if (!this.config.colorSchemes[schemeName]) {
|
142
|
+
console.warn(`Theme scheme '${schemeName}' not found`);
|
143
|
+
return;
|
144
|
+
}
|
145
|
+
|
146
|
+
this.state.currentScheme = schemeName;
|
147
|
+
this.state.userPreference = schemeName;
|
148
|
+
|
149
|
+
this.applyTheme();
|
150
|
+
this.savePreference();
|
151
|
+
this.notifyObservers();
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* Toggle between light and dark themes
|
156
|
+
*/
|
157
|
+
public toggle(): void {
|
158
|
+
const currentScheme = this.config.colorSchemes[this.state.currentScheme];
|
159
|
+
|
160
|
+
if (currentScheme && currentScheme.type === 'light') {
|
161
|
+
this.setScheme('dark');
|
162
|
+
} else {
|
163
|
+
this.setScheme('light');
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Set contrast level
|
169
|
+
*/
|
170
|
+
public setContrast(level: 'normal' | 'high' | 'low'): void {
|
171
|
+
this.state.contrastLevel = level;
|
172
|
+
|
173
|
+
if (level === 'high' && this.config.colorSchemes['highContrast']) {
|
174
|
+
this.setScheme('highContrast');
|
175
|
+
} else {
|
176
|
+
this.applyContrastAdjustments();
|
177
|
+
}
|
178
|
+
|
179
|
+
this.notifyObservers();
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Get current theme state
|
184
|
+
*/
|
185
|
+
public getState(): ThemeState {
|
186
|
+
return { ...this.state };
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Get current color scheme
|
191
|
+
*/
|
192
|
+
public getCurrentScheme(): ColorScheme {
|
193
|
+
const scheme = this.config.colorSchemes[this.state.currentScheme];
|
194
|
+
if (!scheme) {
|
195
|
+
throw new Error(`Color scheme '${this.state.currentScheme}' not found`);
|
196
|
+
}
|
197
|
+
return scheme;
|
198
|
+
}
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Add theme observer
|
202
|
+
*/
|
203
|
+
public observe(callback: (state: ThemeState) => void): () => void {
|
204
|
+
this.observers.add(callback);
|
205
|
+
|
206
|
+
// Return unsubscribe function
|
207
|
+
return () => {
|
208
|
+
this.observers.delete(callback);
|
209
|
+
};
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Add custom color scheme
|
214
|
+
*/
|
215
|
+
public addScheme(scheme: ColorScheme): void {
|
216
|
+
this.config.colorSchemes[scheme.name.toLowerCase()] = scheme;
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Remove color scheme
|
221
|
+
*/
|
222
|
+
public removeScheme(schemeName: string): void {
|
223
|
+
if (schemeName === this.state.currentScheme) {
|
224
|
+
console.warn('Cannot remove currently active scheme');
|
225
|
+
return;
|
226
|
+
}
|
227
|
+
|
228
|
+
delete this.config.colorSchemes[schemeName];
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Get available schemes
|
233
|
+
*/
|
234
|
+
public getAvailableSchemes(): ColorScheme[] {
|
235
|
+
return Object.values(this.config.colorSchemes);
|
236
|
+
}
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Apply theme to document
|
240
|
+
*/
|
241
|
+
public applyTheme(): void {
|
242
|
+
const scheme = this.getCurrentScheme();
|
243
|
+
|
244
|
+
// Apply CSS custom properties
|
245
|
+
this.applyCSSProperties(scheme);
|
246
|
+
|
247
|
+
// Apply theme class
|
248
|
+
this.applyThemeClass(scheme);
|
249
|
+
|
250
|
+
// Apply transitions if enabled
|
251
|
+
if (this.config.transitions) {
|
252
|
+
this.applyTransitions();
|
253
|
+
}
|
254
|
+
|
255
|
+
// Apply accessibility enhancements
|
256
|
+
this.applyAccessibilityEnhancements(scheme);
|
257
|
+
}
|
258
|
+
|
259
|
+
/**
|
260
|
+
* Reset to system preference
|
261
|
+
*/
|
262
|
+
public resetToSystem(): void {
|
263
|
+
this.state.userPreference = null;
|
264
|
+
this.state.currentScheme = this.state.systemPreference;
|
265
|
+
|
266
|
+
this.applyTheme();
|
267
|
+
this.savePreference();
|
268
|
+
this.notifyObservers();
|
269
|
+
}
|
270
|
+
|
271
|
+
/**
|
272
|
+
* Destroy theme system
|
273
|
+
*/
|
274
|
+
public destroy(): void {
|
275
|
+
this.mediaQueries.forEach(mq => {
|
276
|
+
mq.removeEventListener('change', this.handleMediaQueryChange);
|
277
|
+
});
|
278
|
+
this.mediaQueries.clear();
|
279
|
+
this.observers.clear();
|
280
|
+
}
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Setup media query listeners
|
284
|
+
*/
|
285
|
+
private setupMediaQueries(): void {
|
286
|
+
// Check if matchMedia is available (polyfill may not be loaded yet)
|
287
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
288
|
+
console.warn('matchMedia not available, skipping media query setup');
|
289
|
+
return;
|
290
|
+
}
|
291
|
+
|
292
|
+
// Dark mode preference
|
293
|
+
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
294
|
+
this.mediaQueries.set('dark-mode', darkModeQuery);
|
295
|
+
darkModeQuery.addEventListener('change', this.handleMediaQueryChange.bind(this));
|
296
|
+
|
297
|
+
// High contrast preference
|
298
|
+
const highContrastQuery = window.matchMedia('(prefers-contrast: high)');
|
299
|
+
this.mediaQueries.set('high-contrast', highContrastQuery);
|
300
|
+
highContrastQuery.addEventListener('change', this.handleMediaQueryChange.bind(this));
|
301
|
+
|
302
|
+
// Reduced motion preference
|
303
|
+
const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
304
|
+
this.mediaQueries.set('reduced-motion', reducedMotionQuery);
|
305
|
+
reducedMotionQuery.addEventListener('change', this.handleMediaQueryChange.bind(this));
|
306
|
+
|
307
|
+
// Update initial state
|
308
|
+
this.updateStateFromMediaQueries();
|
309
|
+
}
|
310
|
+
|
311
|
+
/**
|
312
|
+
* Handle media query changes
|
313
|
+
*/
|
314
|
+
private handleMediaQueryChange = (): void => {
|
315
|
+
this.updateStateFromMediaQueries();
|
316
|
+
|
317
|
+
if (this.config.respectSystemPreference && !this.state.userPreference) {
|
318
|
+
this.state.currentScheme = this.state.systemPreference;
|
319
|
+
this.applyTheme();
|
320
|
+
}
|
321
|
+
|
322
|
+
this.notifyObservers();
|
323
|
+
};
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Update state from media queries
|
327
|
+
*/
|
328
|
+
private updateStateFromMediaQueries(): void {
|
329
|
+
const darkModeQuery = this.mediaQueries.get('dark-mode');
|
330
|
+
const highContrastQuery = this.mediaQueries.get('high-contrast');
|
331
|
+
const reducedMotionQuery = this.mediaQueries.get('reduced-motion');
|
332
|
+
|
333
|
+
this.state.systemPreference = darkModeQuery?.matches ? 'dark' : 'light';
|
334
|
+
this.state.highContrast = highContrastQuery?.matches || false;
|
335
|
+
this.state.reducedMotion = reducedMotionQuery?.matches || false;
|
336
|
+
|
337
|
+
if (this.state.highContrast && this.config.adaptiveContrast) {
|
338
|
+
this.state.contrastLevel = 'high';
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
/**
|
343
|
+
* Apply CSS custom properties
|
344
|
+
*/
|
345
|
+
private applyCSSProperties(scheme: ColorScheme): void {
|
346
|
+
try {
|
347
|
+
const root = document.documentElement;
|
348
|
+
|
349
|
+
// Check if root element and style property exist
|
350
|
+
if (!root || !root.style || typeof root.style.setProperty !== 'function') {
|
351
|
+
logger.warn('CSS custom properties not supported in this environment');
|
352
|
+
return;
|
353
|
+
}
|
354
|
+
|
355
|
+
Object.entries(scheme.colors).forEach(([name, value]) => {
|
356
|
+
root.style.setProperty(`--proteus-${name}`, value);
|
357
|
+
});
|
358
|
+
|
359
|
+
// Apply accessibility properties
|
360
|
+
root.style.setProperty('--proteus-min-contrast', scheme.accessibility.minContrast.toString());
|
361
|
+
root.style.setProperty('--proteus-focus-indicator', scheme.accessibility.focusIndicator);
|
362
|
+
} catch (error) {
|
363
|
+
logger.warn('Failed to apply CSS custom properties:', error);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
/**
|
368
|
+
* Apply theme class to document
|
369
|
+
*/
|
370
|
+
private applyThemeClass(scheme: ColorScheme): void {
|
371
|
+
const body = document.body;
|
372
|
+
|
373
|
+
// Remove existing theme classes
|
374
|
+
body.classList.remove('proteus-light', 'proteus-dark', 'proteus-high-contrast');
|
375
|
+
|
376
|
+
// Add current theme class
|
377
|
+
body.classList.add(`proteus-${scheme.type}`);
|
378
|
+
|
379
|
+
if (scheme.contrast === 'high') {
|
380
|
+
body.classList.add('proteus-high-contrast');
|
381
|
+
}
|
382
|
+
|
383
|
+
// Add reduced motion class if needed
|
384
|
+
if (this.state.reducedMotion) {
|
385
|
+
body.classList.add('proteus-reduced-motion');
|
386
|
+
} else {
|
387
|
+
body.classList.remove('proteus-reduced-motion');
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
/**
|
392
|
+
* Apply theme transitions
|
393
|
+
*/
|
394
|
+
private applyTransitions(): void {
|
395
|
+
if (this.state.reducedMotion) return;
|
396
|
+
|
397
|
+
const style = document.createElement('style');
|
398
|
+
style.id = 'proteus-theme-transitions';
|
399
|
+
|
400
|
+
// Remove existing transition styles
|
401
|
+
const existing = document.getElementById('proteus-theme-transitions');
|
402
|
+
if (existing) {
|
403
|
+
existing.remove();
|
404
|
+
}
|
405
|
+
|
406
|
+
style.textContent = `
|
407
|
+
* {
|
408
|
+
transition:
|
409
|
+
background-color ${this.config.transitionDuration}ms ease,
|
410
|
+
color ${this.config.transitionDuration}ms ease,
|
411
|
+
border-color ${this.config.transitionDuration}ms ease,
|
412
|
+
box-shadow ${this.config.transitionDuration}ms ease !important;
|
413
|
+
}
|
414
|
+
`;
|
415
|
+
|
416
|
+
document.head.appendChild(style);
|
417
|
+
|
418
|
+
// Remove transitions after animation completes
|
419
|
+
setTimeout(() => {
|
420
|
+
style.remove();
|
421
|
+
}, this.config.transitionDuration + 100);
|
422
|
+
}
|
423
|
+
|
424
|
+
/**
|
425
|
+
* Apply accessibility enhancements
|
426
|
+
*/
|
427
|
+
private applyAccessibilityEnhancements(scheme: ColorScheme): void {
|
428
|
+
const style = document.createElement('style');
|
429
|
+
style.id = 'proteus-accessibility-enhancements';
|
430
|
+
|
431
|
+
// Remove existing accessibility styles
|
432
|
+
const existing = document.getElementById('proteus-accessibility-enhancements');
|
433
|
+
if (existing) {
|
434
|
+
existing.remove();
|
435
|
+
}
|
436
|
+
|
437
|
+
style.textContent = `
|
438
|
+
:focus {
|
439
|
+
outline: 2px solid var(--proteus-focus-indicator) !important;
|
440
|
+
outline-offset: 2px !important;
|
441
|
+
}
|
442
|
+
|
443
|
+
.proteus-high-contrast {
|
444
|
+
filter: contrast(${scheme.contrast === 'high' ? '150%' : '100%'});
|
445
|
+
}
|
446
|
+
|
447
|
+
.proteus-reduced-motion * {
|
448
|
+
animation-duration: 0.01ms !important;
|
449
|
+
animation-iteration-count: 1 !important;
|
450
|
+
transition-duration: 0.01ms !important;
|
451
|
+
}
|
452
|
+
`;
|
453
|
+
|
454
|
+
document.head.appendChild(style);
|
455
|
+
}
|
456
|
+
|
457
|
+
/**
|
458
|
+
* Apply contrast adjustments
|
459
|
+
*/
|
460
|
+
private applyContrastAdjustments(): void {
|
461
|
+
const scheme = this.getCurrentScheme();
|
462
|
+
const adjustedScheme = { ...scheme };
|
463
|
+
|
464
|
+
if (this.state.contrastLevel === 'high') {
|
465
|
+
// Increase contrast for better accessibility
|
466
|
+
adjustedScheme.colors = this.adjustColorsForHighContrast(scheme.colors);
|
467
|
+
} else if (this.state.contrastLevel === 'low') {
|
468
|
+
// Reduce contrast for comfort
|
469
|
+
adjustedScheme.colors = this.adjustColorsForLowContrast(scheme.colors);
|
470
|
+
}
|
471
|
+
|
472
|
+
this.applyCSSProperties(adjustedScheme);
|
473
|
+
}
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Adjust colors for high contrast
|
477
|
+
*/
|
478
|
+
private adjustColorsForHighContrast(colors: Record<string, string>): Record<string, string> {
|
479
|
+
const adjusted = { ...colors };
|
480
|
+
|
481
|
+
// Make text more contrasted
|
482
|
+
if (colors['background'] === '#ffffff') {
|
483
|
+
adjusted['text'] = '#000000';
|
484
|
+
adjusted['textSecondary'] = '#333333';
|
485
|
+
} else {
|
486
|
+
adjusted['text'] = '#ffffff';
|
487
|
+
adjusted['textSecondary'] = '#cccccc';
|
488
|
+
}
|
489
|
+
|
490
|
+
return adjusted;
|
491
|
+
}
|
492
|
+
|
493
|
+
/**
|
494
|
+
* Adjust colors for low contrast
|
495
|
+
*/
|
496
|
+
private adjustColorsForLowContrast(colors: Record<string, string>): Record<string, string> {
|
497
|
+
const adjusted = { ...colors };
|
498
|
+
|
499
|
+
// Make text less contrasted
|
500
|
+
if (colors['background'] === '#ffffff') {
|
501
|
+
adjusted['text'] = '#444444';
|
502
|
+
adjusted['textSecondary'] = '#666666';
|
503
|
+
} else {
|
504
|
+
adjusted['text'] = '#dddddd';
|
505
|
+
adjusted['textSecondary'] = '#aaaaaa';
|
506
|
+
}
|
507
|
+
|
508
|
+
return adjusted;
|
509
|
+
}
|
510
|
+
|
511
|
+
/**
|
512
|
+
* Load stored preference
|
513
|
+
*/
|
514
|
+
private loadStoredPreference(): void {
|
515
|
+
if (this.config.storage === 'none') return;
|
516
|
+
|
517
|
+
try {
|
518
|
+
const storage = this.config.storage === 'localStorage' ? localStorage : sessionStorage;
|
519
|
+
const stored = storage.getItem(this.config.storageKey);
|
520
|
+
|
521
|
+
if (stored) {
|
522
|
+
const preference = JSON.parse(stored);
|
523
|
+
this.state.userPreference = preference.scheme;
|
524
|
+
this.state.contrastLevel = preference.contrast || 'normal';
|
525
|
+
}
|
526
|
+
} catch (error) {
|
527
|
+
console.warn('Failed to load theme preference:', error);
|
528
|
+
}
|
529
|
+
}
|
530
|
+
|
531
|
+
/**
|
532
|
+
* Save current preference
|
533
|
+
*/
|
534
|
+
private savePreference(): void {
|
535
|
+
if (this.config.storage === 'none') return;
|
536
|
+
|
537
|
+
try {
|
538
|
+
const storage = this.config.storage === 'localStorage' ? localStorage : sessionStorage;
|
539
|
+
const preference = {
|
540
|
+
scheme: this.state.userPreference,
|
541
|
+
contrast: this.state.contrastLevel
|
542
|
+
};
|
543
|
+
|
544
|
+
storage.setItem(this.config.storageKey, JSON.stringify(preference));
|
545
|
+
} catch (error) {
|
546
|
+
console.warn('Failed to save theme preference:', error);
|
547
|
+
}
|
548
|
+
}
|
549
|
+
|
550
|
+
/**
|
551
|
+
* Apply initial theme
|
552
|
+
*/
|
553
|
+
private applyInitialTheme(): void {
|
554
|
+
if (this.state.userPreference && this.config.colorSchemes[this.state.userPreference]) {
|
555
|
+
this.state.currentScheme = this.state.userPreference;
|
556
|
+
} else if (this.config.autoDetect) {
|
557
|
+
this.state.currentScheme = this.state.systemPreference;
|
558
|
+
} else {
|
559
|
+
this.state.currentScheme = 'light';
|
560
|
+
}
|
561
|
+
|
562
|
+
this.applyTheme();
|
563
|
+
}
|
564
|
+
|
565
|
+
/**
|
566
|
+
* Notify observers of state changes
|
567
|
+
*/
|
568
|
+
private notifyObservers(): void {
|
569
|
+
this.observers.forEach(callback => {
|
570
|
+
try {
|
571
|
+
callback(this.state);
|
572
|
+
} catch (error) {
|
573
|
+
console.error('Theme observer error:', error);
|
574
|
+
}
|
575
|
+
});
|
576
|
+
}
|
577
|
+
|
578
|
+
/**
|
579
|
+
* Create initial state
|
580
|
+
*/
|
581
|
+
private createInitialState(): ThemeState {
|
582
|
+
return {
|
583
|
+
currentScheme: 'light',
|
584
|
+
systemPreference: 'light',
|
585
|
+
userPreference: null,
|
586
|
+
contrastLevel: 'normal',
|
587
|
+
reducedMotion: false,
|
588
|
+
highContrast: false
|
589
|
+
};
|
590
|
+
}
|
591
|
+
}
|
@@ -0,0 +1,134 @@
|
|
1
|
+
/**
|
2
|
+
* Core type definitions for ProteusJS
|
3
|
+
*/
|
4
|
+
|
5
|
+
// Main configuration interface
|
6
|
+
export interface ProteusConfig {
|
7
|
+
debug?: boolean;
|
8
|
+
performance?: 'low' | 'medium' | 'high';
|
9
|
+
accessibility?: boolean;
|
10
|
+
autoInit?: boolean;
|
11
|
+
containers?: ContainerConfig;
|
12
|
+
typography?: TypographyConfig;
|
13
|
+
layout?: LayoutConfig;
|
14
|
+
animations?: AnimationConfig;
|
15
|
+
theming?: ThemingConfig;
|
16
|
+
}
|
17
|
+
|
18
|
+
// Container query configuration
|
19
|
+
export interface ContainerConfig {
|
20
|
+
autoDetect?: boolean;
|
21
|
+
breakpoints?: Record<string, string>;
|
22
|
+
units?: boolean;
|
23
|
+
isolation?: boolean;
|
24
|
+
polyfill?: boolean;
|
25
|
+
}
|
26
|
+
|
27
|
+
// Breakpoint configuration
|
28
|
+
export interface BreakpointConfig {
|
29
|
+
[key: string]: string | number;
|
30
|
+
}
|
31
|
+
|
32
|
+
// Typography configuration
|
33
|
+
export interface TypographyConfig {
|
34
|
+
fluidScaling?: boolean;
|
35
|
+
autoOptimize?: boolean;
|
36
|
+
accessibility?: boolean;
|
37
|
+
scale?: {
|
38
|
+
ratio?: number;
|
39
|
+
base?: string;
|
40
|
+
levels?: number;
|
41
|
+
};
|
42
|
+
lineHeight?: {
|
43
|
+
auto?: boolean;
|
44
|
+
density?: 'compact' | 'comfortable' | 'spacious';
|
45
|
+
};
|
46
|
+
}
|
47
|
+
|
48
|
+
// Layout configuration
|
49
|
+
export interface LayoutConfig {
|
50
|
+
grid?: {
|
51
|
+
auto?: boolean;
|
52
|
+
masonry?: boolean;
|
53
|
+
gap?: 'fluid' | string;
|
54
|
+
};
|
55
|
+
flexbox?: {
|
56
|
+
enhanced?: boolean;
|
57
|
+
autoWrap?: boolean;
|
58
|
+
};
|
59
|
+
spacing?: {
|
60
|
+
scale?: 'minor-third' | 'major-third' | 'perfect-fourth' | 'golden-ratio';
|
61
|
+
density?: 'compact' | 'comfortable' | 'spacious';
|
62
|
+
};
|
63
|
+
}
|
64
|
+
|
65
|
+
// Animation configuration
|
66
|
+
export interface AnimationConfig {
|
67
|
+
enabled?: boolean;
|
68
|
+
respectMotionPreferences?: boolean;
|
69
|
+
duration?: number;
|
70
|
+
easing?: string;
|
71
|
+
flip?: boolean;
|
72
|
+
microInteractions?: boolean;
|
73
|
+
}
|
74
|
+
|
75
|
+
// Theming configuration
|
76
|
+
export interface ThemingConfig {
|
77
|
+
darkMode?: {
|
78
|
+
auto?: boolean;
|
79
|
+
schedule?: string;
|
80
|
+
transition?: 'instant' | 'smooth';
|
81
|
+
};
|
82
|
+
contrast?: {
|
83
|
+
adaptive?: boolean;
|
84
|
+
level?: 'AA' | 'AAA';
|
85
|
+
};
|
86
|
+
}
|
87
|
+
|
88
|
+
// Accessibility configuration
|
89
|
+
export interface AccessibilityConfig {
|
90
|
+
screenReader?: boolean;
|
91
|
+
keyboardNavigation?: boolean;
|
92
|
+
colorCompliance?: 'AA' | 'AAA';
|
93
|
+
motionPreferences?: boolean;
|
94
|
+
announcements?: boolean;
|
95
|
+
}
|
96
|
+
|
97
|
+
// Performance configuration
|
98
|
+
export interface PerformanceConfig {
|
99
|
+
targetFrameRate?: number;
|
100
|
+
memoryManagement?: boolean;
|
101
|
+
lazyEvaluation?: boolean;
|
102
|
+
caching?: boolean;
|
103
|
+
monitoring?: boolean;
|
104
|
+
}
|
105
|
+
|
106
|
+
// Element query types
|
107
|
+
export type ElementQuery = {
|
108
|
+
property: 'width' | 'height' | 'aspect-ratio' | 'content-size';
|
109
|
+
operator: 'min' | 'max' | 'exact';
|
110
|
+
value: string | number;
|
111
|
+
};
|
112
|
+
|
113
|
+
// Container units
|
114
|
+
export type ContainerUnit = 'cw' | 'ch' | 'cmin' | 'cmax' | 'cqi' | 'cqb';
|
115
|
+
|
116
|
+
// Event types
|
117
|
+
export interface ProteusEvent {
|
118
|
+
type: string;
|
119
|
+
target: Element;
|
120
|
+
detail?: any;
|
121
|
+
timestamp: number;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Observer callback types
|
125
|
+
export type ResizeCallback = (entry: ResizeObserverEntry) => void;
|
126
|
+
export type IntersectionCallback = (entry: IntersectionObserverEntry) => void;
|
127
|
+
|
128
|
+
// Plugin system types
|
129
|
+
export interface ProteusPlugin {
|
130
|
+
name: string;
|
131
|
+
version: string;
|
132
|
+
install: (proteus: any) => void;
|
133
|
+
uninstall?: (proteus: any) => void;
|
134
|
+
}
|