@shohojdhara/atomix 0.5.2 → 0.5.4

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.
Files changed (39) hide show
  1. package/atomix.config.ts +33 -33
  2. package/dist/config.d.ts +187 -112
  3. package/dist/config.js +7 -49
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.d.ts +1958 -900
  6. package/dist/index.esm.js +2275 -383
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +2327 -417
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/dist/theme.d.ts +1390 -276
  13. package/dist/theme.js +2129 -621
  14. package/dist/theme.js.map +1 -1
  15. package/package.json +1 -1
  16. package/scripts/cli/internal/config-loader.js +30 -20
  17. package/src/lib/config/index.ts +38 -362
  18. package/src/lib/config/loader.ts +419 -0
  19. package/src/lib/config/public-api.ts +43 -0
  20. package/src/lib/config/types.ts +389 -0
  21. package/src/lib/config/validator.ts +305 -0
  22. package/src/lib/theme/adapters/index.ts +1 -1
  23. package/src/lib/theme/adapters/themeAdapter.ts +358 -229
  24. package/src/lib/theme/components/ThemeToggle.tsx +276 -0
  25. package/src/lib/theme/config/configLoader.ts +351 -0
  26. package/src/lib/theme/config/loader.ts +221 -0
  27. package/src/lib/theme/core/createTheme.ts +126 -50
  28. package/src/lib/theme/core/createThemeObject.ts +7 -4
  29. package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
  30. package/src/lib/theme/index.ts +322 -38
  31. package/src/lib/theme/runtime/ThemeProvider.tsx +44 -10
  32. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
  33. package/src/lib/theme/runtime/useTheme.ts +1 -0
  34. package/src/lib/theme/tokens/tokens.ts +101 -1
  35. package/src/lib/theme/types.ts +91 -0
  36. package/src/lib/theme/utils/performanceMonitor.ts +315 -0
  37. package/src/lib/theme/utils/responsive.ts +280 -0
  38. package/src/lib/theme/utils/themeUtils.ts +531 -117
  39. package/src/styles/05-objects/_objects.masonry-grid.scss +3 -3
@@ -1,264 +1,422 @@
1
1
  /**
2
2
  * Theme Adapter
3
- *
4
- * Converts between Theme objects and DesignTokens.
3
+ *
4
+ * Converts between Theme objects and DesignTokens
5
5
  */
6
6
 
7
7
  import type { Theme } from '../types';
8
- import type { DesignTokens } from '../tokens/tokens';
9
- import { createTokens } from '../tokens/tokens';
10
- import { hexToRgb } from '../utils/themeUtils';
8
+ import type { AtomixConfig } from '../../config';
9
+ import { type DesignTokens, defaultTokens, createTokens } from '../tokens/tokens';
11
10
 
12
11
  /**
13
- * Convert Theme object to DesignTokens
14
- *
15
- * Extracts values from a Theme object and converts them to flat DesignTokens format.
16
- *
17
- * @param theme - Theme object to convert
18
- * @returns Partial DesignTokens object
19
- *
20
- * @example
21
- * ```typescript
22
- * const theme = createTheme({ palette: { primary: { main: '#7c3aed' } } });
23
- * const tokens = themeToDesignTokens(theme);
24
- * // Returns: { 'primary': '#7c3aed', ... }
25
- * ```
12
+ * Convert a Theme object to DesignTokens
26
13
  */
27
- export function themeToDesignTokens(theme: Theme): Partial<DesignTokens> {
14
+ export function themeToDesignTokens(theme: Theme): DesignTokens {
28
15
  const tokens: Partial<DesignTokens> = {};
29
16
 
30
- // Convert palette colors
17
+ // Convert colors
31
18
  if (theme.palette) {
32
- // Primary colors
19
+ // Primary color
33
20
  if (theme.palette.primary) {
34
- tokens['primary'] = theme.palette.primary.main;
35
- if (theme.palette.primary.light) {
36
- tokens['primary-3'] = theme.palette.primary.light;
37
- }
38
- if (theme.palette.primary.dark) {
39
- tokens['primary-9'] = theme.palette.primary.dark;
40
- }
41
- // Extract RGB if available
42
- if (theme.palette.primary.main) {
43
- const rgb = hexToRgb(theme.palette.primary.main);
44
- if (rgb) {
45
- tokens['primary-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
46
- }
47
- }
21
+ const primaryMain = theme.palette.primary.main;
22
+ tokens.primary = primaryMain;
23
+ const rgb = hexToRgb(primaryMain);
24
+ if (rgb) tokens['primary-rgb'] = rgb;
48
25
  }
49
26
 
50
- // Secondary colors
27
+ // Secondary color
51
28
  if (theme.palette.secondary) {
52
- tokens['secondary'] = theme.palette.secondary.main;
53
- if (theme.palette.secondary.light) {
54
- tokens['gray-1'] = theme.palette.secondary.light;
55
- }
56
- if (theme.palette.secondary.dark) {
57
- tokens['gray-3'] = theme.palette.secondary.dark;
58
- }
59
- const rgb = hexToRgb(theme.palette.secondary.main);
60
- if (rgb) {
61
- tokens['secondary-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
62
- }
63
- }
64
-
65
- // Error colors
66
- if (theme.palette.error) {
67
- tokens['error'] = theme.palette.error.main;
68
- tokens['red-6'] = theme.palette.error.main;
69
- if (theme.palette.error.light) {
70
- tokens['red-4'] = theme.palette.error.light;
71
- }
72
- if (theme.palette.error.dark) {
73
- tokens['red-9'] = theme.palette.error.dark;
74
- }
75
- const rgb = hexToRgb(theme.palette.error.main);
76
- if (rgb) {
77
- tokens['error-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
78
- }
29
+ const secondaryMain = theme.palette.secondary.main;
30
+ tokens.secondary = secondaryMain;
31
+ const rgb = hexToRgb(secondaryMain);
32
+ if (rgb) tokens['secondary-rgb'] = rgb;
79
33
  }
80
34
 
81
- // Success colors
82
- if (theme.palette.success) {
83
- tokens['success'] = theme.palette.success.main;
84
- tokens['green-6'] = theme.palette.success.main;
85
- if (theme.palette.success.light) {
86
- tokens['green-4'] = theme.palette.success.light;
87
- }
88
- if (theme.palette.success.dark) {
89
- tokens['green-9'] = theme.palette.success.dark;
90
- }
91
- const rgb = hexToRgb(theme.palette.success.main);
92
- if (rgb) {
93
- tokens['success-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
94
- }
95
- }
96
-
97
- // Warning colors
98
- if (theme.palette.warning) {
99
- tokens['warning'] = theme.palette.warning.main;
100
- tokens['yellow-6'] = theme.palette.warning.main;
101
- if (theme.palette.warning.light) {
102
- tokens['yellow-4'] = theme.palette.warning.light;
103
- }
104
- if (theme.palette.warning.dark) {
105
- tokens['yellow-9'] = theme.palette.warning.dark;
106
- }
107
- const rgb = hexToRgb(theme.palette.warning.main);
108
- if (rgb) {
109
- tokens['warning-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
110
- }
111
- }
112
-
113
- // Info colors
114
- if (theme.palette.info) {
115
- tokens['info'] = theme.palette.info.main;
116
- tokens['blue-6'] = theme.palette.info.main;
117
- if (theme.palette.info.light) {
118
- tokens['blue-4'] = theme.palette.info.light;
119
- }
120
- if (theme.palette.info.dark) {
121
- tokens['blue-9'] = theme.palette.info.dark;
122
- }
123
- const rgb = hexToRgb(theme.palette.info.main);
124
- if (rgb) {
125
- tokens['info-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
35
+ // Other colors
36
+ const colorKeys = ['error', 'warning', 'info', 'success'] as const;
37
+ for (const key of colorKeys) {
38
+ if (theme.palette[key]) {
39
+ const colorMain = theme.palette[key]!.main;
40
+ tokens[key] = colorMain;
41
+ const rgb = hexToRgb(colorMain);
42
+ if (rgb) tokens[`${key}-rgb` as keyof DesignTokens] = rgb as any;
126
43
  }
127
44
  }
128
45
 
129
46
  // Background colors
130
47
  if (theme.palette.background) {
131
48
  tokens['body-bg'] = theme.palette.background.default;
132
- tokens['primary-bg-subtle'] = theme.palette.background.default;
133
- tokens['secondary-bg-subtle'] = theme.palette.background.paper;
134
- tokens['tertiary-bg-subtle'] = theme.palette.background.subtle;
135
49
  }
136
50
 
137
51
  // Text colors
138
52
  if (theme.palette.text) {
139
53
  tokens['body-color'] = theme.palette.text.primary;
140
- tokens['heading-color'] = theme.palette.text.primary;
141
- tokens['primary-text-emphasis'] = theme.palette.text.primary;
142
- tokens['secondary-text-emphasis'] = theme.palette.text.secondary;
143
- tokens['disabled-text-emphasis'] = theme.palette.text.disabled;
144
54
  }
145
55
  }
146
56
 
147
57
  // Convert typography
148
58
  if (theme.typography) {
149
59
  tokens['body-font-family'] = theme.typography.fontFamily;
150
- tokens['font-sans-serif'] = theme.typography.fontFamily;
151
60
  tokens['body-font-size'] = `${theme.typography.fontSize}px`;
152
- tokens['body-font-weight'] = String(theme.typography.fontWeightRegular);
153
-
154
- // Font weights
155
- tokens['font-weight-light'] = String(theme.typography.fontWeightLight);
156
- tokens['font-weight-normal'] = String(theme.typography.fontWeightRegular);
157
- tokens['font-weight-medium'] = String(theme.typography.fontWeightMedium);
158
- tokens['font-weight-semibold'] = String(theme.typography.fontWeightSemiBold);
159
- tokens['font-weight-bold'] = String(theme.typography.fontWeightBold);
160
-
161
- // Line heights
162
- if (theme.typography.h1?.lineHeight) {
163
- tokens['line-height-base'] = String(theme.typography.h1.lineHeight);
164
- }
61
+ tokens['font-weight-normal'] = `${theme.typography.fontWeightRegular}`;
62
+ tokens['font-weight-bold'] = `${theme.typography.fontWeightBold}`;
165
63
  }
166
64
 
167
- // Convert spacing (if available as object)
168
- if (
169
- theme.spacing &&
170
- typeof theme.spacing === 'object' &&
171
- !('__isSpacingFunction' in theme.spacing)
172
- ) {
173
- const spacing = theme.spacing as Record<string, string | number>;
174
- Object.entries(spacing).forEach(([key, value]) => {
175
- tokens[`spacing-${key}` as keyof DesignTokens] = String(value);
176
- });
65
+ // Convert spacing
66
+ if (typeof theme.spacing === 'function') {
67
+ // If spacing is a function, call it with some values to get results
68
+ tokens['spacing-1'] = theme.spacing(1);
69
+ tokens['spacing-2'] = theme.spacing(2);
70
+ tokens['spacing-4'] = theme.spacing(4);
177
71
  }
178
72
 
179
- // Convert border radius
180
- if (theme.borderRadius) {
181
- Object.entries(theme.borderRadius).forEach(([key, value]) => {
182
- const tokenKey =
183
- key === 'sm'
184
- ? 'border-radius-sm'
185
- : key === 'md'
186
- ? 'border-radius'
187
- : key === 'lg'
188
- ? 'border-radius-lg'
189
- : key === 'xl'
190
- ? 'border-radius-xl'
191
- : key === 'xxl'
192
- ? 'border-radius-xxl'
193
- : `border-radius-${key}`;
194
- tokens[tokenKey as keyof DesignTokens] = String(value);
195
- });
73
+ // Convert breakpoints
74
+ if (theme.breakpoints?.values) {
75
+ tokens['breakpoint-xs'] = `${theme.breakpoints.values.xs}px`;
76
+ tokens['breakpoint-sm'] = `${theme.breakpoints.values.sm}px`;
77
+ tokens['breakpoint-md'] = `${theme.breakpoints.values.md}px`;
78
+ tokens['breakpoint-lg'] = `${theme.breakpoints.values.lg}px`;
79
+ tokens['breakpoint-xl'] = `${theme.breakpoints.values.xl}px`;
196
80
  }
197
81
 
198
82
  // Convert shadows
199
83
  if (theme.shadows) {
200
- Object.entries(theme.shadows).forEach(([key, value]) => {
201
- const tokenKey =
202
- key === 'xs'
203
- ? 'box-shadow-xs'
204
- : key === 'sm'
205
- ? 'box-shadow-sm'
206
- : key === 'md'
207
- ? 'box-shadow'
208
- : key === 'lg'
209
- ? 'box-shadow-lg'
210
- : key === 'xl'
211
- ? 'box-shadow-xl'
212
- : `box-shadow-${key}`;
213
- tokens[tokenKey as keyof DesignTokens] = String(value);
214
- });
84
+ tokens['box-shadow'] = theme.shadows[2]; // Use a moderate shadow
85
+ tokens['box-shadow-sm'] = theme.shadows[1];
86
+ tokens['box-shadow-lg'] = theme.shadows[3];
87
+ }
88
+
89
+ // Convert transitions
90
+ if (theme.transitions) {
91
+ tokens['transition-duration-base'] = `${theme.transitions.duration.standard}ms`;
215
92
  }
216
93
 
217
94
  // Convert z-index
218
95
  if (theme.zIndex) {
219
- Object.entries(theme.zIndex).forEach(([key, value]) => {
220
- tokens[`z-${key}` as keyof DesignTokens] = String(value);
221
- });
96
+ tokens['z-modal'] = `${theme.zIndex.modal}`;
97
+ tokens['z-popover'] = `${theme.zIndex.popover}`;
98
+ tokens['z-tooltip'] = `${theme.zIndex.tooltip}`;
222
99
  }
223
100
 
224
- // Convert transitions
225
- if (theme.transitions) {
226
- if (theme.transitions.duration) {
227
- Object.entries(theme.transitions.duration).forEach(([key, value]) => {
228
- tokens[`transition-duration-${key}` as keyof DesignTokens] = String(value);
229
- });
101
+ // Convert border radius
102
+ if (theme.borderRadius) {
103
+ const baseRadius = theme.borderRadius.base;
104
+ tokens['border-radius'] = typeof baseRadius === 'number'
105
+ ? `${baseRadius}px`
106
+ : baseRadius;
107
+ }
108
+
109
+ // Add advanced feature tokens if available in theme
110
+ if (theme.custom) {
111
+ // Interactive Effects (Phase 2)
112
+ if (theme.custom.interactiveEffects) {
113
+ const ie = theme.custom.interactiveEffects;
114
+
115
+ // Vortex effects
116
+ if (ie.vortex) {
117
+ tokens['interactive-vortex-enabled'] = String(ie.vortex.enabled ?? false);
118
+ tokens['interactive-vortex-strength'] = String(ie.vortex.strength ?? 0.5);
119
+ tokens['interactive-vortex-radius'] = String(ie.vortex.radius ?? 100);
120
+ tokens['interactive-vortex-decay'] = String(ie.vortex.decay ?? 0.8);
121
+ }
122
+
123
+ // Chromatic aberration
124
+ if (ie.chromaticAberration) {
125
+ tokens['interactive-chromatic-enabled'] = String(ie.chromaticAberration.enabled ?? false);
126
+ tokens['interactive-chromatic-mode'] = ie.chromaticAberration.mode ?? 'lateral';
127
+ tokens['interactive-chromatic-red-shift'] = String(ie.chromaticAberration.redShift ?? 0.02);
128
+ tokens['interactive-chromatic-green-shift'] = String(ie.chromaticAberration.greenShift ?? 0);
129
+ tokens['interactive-chromatic-blue-shift'] = String(ie.chromaticAberration.blueShift ?? -0.02);
130
+ tokens['interactive-chromatic-edge-only'] = String(ie.chromaticAberration.edgeOnly ?? false);
131
+ tokens['interactive-chromatic-edge-threshold'] = String(ie.chromaticAberration.edgeThreshold ?? 0.5);
132
+ }
133
+
134
+ // Mouse interaction
135
+ if (ie.mouseInteraction) {
136
+ tokens['interactive-mouse-sensitivity'] = String(ie.mouseInteraction.sensitivity ?? 1.0);
137
+ tokens['interactive-mouse-trail-effect'] = String(ie.mouseInteraction.trailEffect ?? false);
138
+ }
139
+
140
+ // Animation speed
141
+ if (ie.animationSpeed) {
142
+ tokens['interactive-animation-speed-base'] = String(ie.animationSpeed.base ?? 1.0);
143
+ tokens['interactive-animation-speed-multiplier'] = String(ie.animationSpeed.timeMultiplier ?? 1.0);
144
+ }
230
145
  }
231
- if (theme.transitions.easing) {
232
- Object.entries(theme.transitions.easing).forEach(([key, value]) => {
233
- tokens[`easing-${key}` as keyof DesignTokens] = String(value);
234
- });
146
+
147
+ // Optimization (Phase 3)
148
+ if (theme.custom.optimization) {
149
+ const opt = theme.custom.optimization;
150
+
151
+ // Responsive breakpoints
152
+ if (opt.responsive) {
153
+ if (opt.responsive.breakpoints) {
154
+ tokens['optimization-breakpoint-mobile'] = opt.responsive.breakpoints.mobile ?? '0px';
155
+ tokens['optimization-breakpoint-tablet'] = opt.responsive.breakpoints.tablet ?? '768px';
156
+ tokens['optimization-breakpoint-desktop'] = opt.responsive.breakpoints.desktop ?? '1024px';
157
+ tokens['optimization-breakpoint-wide'] = opt.responsive.breakpoints.wide ?? '1440px';
158
+ }
159
+
160
+ if (opt.responsive.deviceScaling) {
161
+ tokens['optimization-device-scaling-mobile'] = String(opt.responsive.deviceScaling.mobile ?? 0.5);
162
+ tokens['optimization-device-scaling-tablet'] = String(opt.responsive.deviceScaling.tablet ?? 0.75);
163
+ tokens['optimization-device-scaling-desktop'] = String(opt.responsive.deviceScaling.desktop ?? 1.0);
164
+ }
165
+ }
166
+
167
+ // Performance settings
168
+ if (opt.performance) {
169
+ tokens['optimization-performance-fps-target'] = String(opt.performance.fpsTarget ?? 60);
170
+ tokens['optimization-auto-scaling-enabled'] = String(opt.performance.autoScaling ?? false);
171
+ }
172
+
173
+ // Auto-scaling settings
174
+ if (opt.autoScaling) {
175
+ tokens['optimization-auto-scaling-enabled'] = String(opt.autoScaling.enabled ?? false);
176
+ tokens['optimization-auto-scaling-low-end'] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? 0.5);
177
+ tokens['optimization-auto-scaling-mid-range'] = String(opt.autoScaling.qualityThresholds?.midRange ?? 0.75);
178
+ tokens['optimization-auto-scaling-high-end'] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1.0);
179
+ }
180
+ }
181
+
182
+ // Visual Polish (Phase 4)
183
+ if (theme.custom.visualPolish) {
184
+ const vp = theme.custom.visualPolish;
185
+
186
+ if (vp.borders) {
187
+ tokens['visual-polish-border-iridescent-glow'] = String(vp.borders.iridescentGlow ?? false);
188
+ tokens['visual-polish-border-shimmer-effect'] = String(vp.borders.shimmerEffect ?? false);
189
+ tokens['visual-polish-border-beveled-edges'] = String(vp.borders.beveledEdges ?? false);
190
+ tokens['visual-polish-border-pulsing-glow'] = String(vp.borders.pulsingGlow ?? false);
191
+ }
192
+
193
+ if (vp.contentAwareBlur) {
194
+ tokens['visual-polish-content-aware-blur-enabled'] = String(vp.contentAwareBlur.enabled ?? false);
195
+ tokens['visual-polish-content-aware-depth-detection'] = String(vp.contentAwareBlur.depthDetection ?? false);
196
+ tokens['visual-polish-content-aware-edge-preservation'] = String(vp.contentAwareBlur.edgePreservation ?? false);
197
+ tokens['visual-polish-content-aware-variable-radius'] = String(vp.contentAwareBlur.variableRadius ?? false);
198
+ }
199
+
200
+ if (vp.holographicEffects) {
201
+ tokens['visual-polish-holographic-enabled'] = String(vp.holographicEffects.enabled ?? false);
202
+ tokens['visual-polish-holographic-rainbow-diffraction'] = String(vp.holographicEffects.rainbowDiffraction ?? false);
203
+ tokens['visual-polish-holographic-scanline-animation'] = String(vp.holographicEffects.scanlineAnimation ?? false);
204
+ tokens['visual-polish-holographic-grid-overlay'] = String(vp.holographicEffects.gridOverlay ?? false);
205
+ tokens['visual-polish-holographic-data-stream'] = String(vp.holographicEffects.dataStream ?? false);
206
+ tokens['visual-polish-holographic-pulse-rings'] = String(vp.holographicEffects.pulseRings ?? false);
207
+ }
235
208
  }
236
209
  }
237
210
 
238
- // Convert breakpoints
239
- if (theme.breakpoints?.values) {
240
- Object.entries(theme.breakpoints.values).forEach(([key, value]) => {
241
- tokens[`breakpoint-${key}` as keyof DesignTokens] = String(value);
242
- });
243
- }
211
+ // Create full tokens object with defaults
212
+ return createTokens(tokens);
213
+ }
214
+
215
+ /**
216
+ * Convert DesignTokens to a Theme object
217
+ */
218
+ export function designTokensToTheme(tokens: DesignTokens): Theme {
219
+ // Implementation would go here if needed
220
+ // For now, we're primarily concerned with the direction from theme to tokens
221
+ throw new Error('designTokensToTheme not yet implemented');
222
+ }
244
223
 
245
- // Merge any existing cssVars from theme
246
- if (theme.cssVars) {
247
- Object.entries(theme.cssVars).forEach(([key, value]) => {
248
- // Remove --atomix- prefix if present
249
- const cleanKey = key.replace(/^--atomix-/, '').replace(/^--/, '');
250
- tokens[cleanKey as keyof DesignTokens] = String(value);
224
+ /**
225
+ * Converts an AtomixConfig to DesignTokens
226
+ *
227
+ * This function maps the configuration from the user-facing format
228
+ * to the internal DesignTokens format used by the theme system.
229
+ *
230
+ * @param config - The configuration object to convert
231
+ * @returns DesignTokens object ready for theme generation
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * import { configToTokens } from '@shohojdhara/atomix/theme';
236
+ *
237
+ * const config = {
238
+ * prefix: 'myapp',
239
+ * theme: { extend: { colors: { primary: { main: '#7AFFD7' } } } }
240
+ * };
241
+ * const tokens = configToTokens(config);
242
+ * ```
243
+ */
244
+ export function configToTokens(config: AtomixConfig): DesignTokens {
245
+ const prefix = config.prefix || 'atomix';
246
+ const theme = config.theme || {};
247
+
248
+ // Start with default tokens
249
+ let tokens: DesignTokens = { ...defaultTokens };
250
+
251
+ // Apply theme extensions
252
+ if (theme.extend) {
253
+ // Apply extensions to tokens
254
+ Object.entries(theme.extend).forEach(([category, values]) => {
255
+ if (typeof values === 'object' && values !== null) {
256
+ Object.entries(values).forEach(([key, value]) => {
257
+ // Map theme categories to token names
258
+ const tokenName = `${category}-${key}`;
259
+
260
+ if (typeof value === 'string' || typeof value === 'number') {
261
+ tokens[tokenName as keyof DesignTokens] = String(value);
262
+ } else if (typeof value === 'object' && value !== null) {
263
+ // Handle nested objects like color scales
264
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
265
+ if (typeof nestedValue === 'string' || typeof nestedValue === 'number') {
266
+ tokens[`${tokenName}-${nestedKey}` as keyof DesignTokens] = String(nestedValue);
267
+ }
268
+ });
269
+ }
270
+ });
271
+ }
251
272
  });
252
273
  }
274
+
275
+ // Apply theme tokens if provided (completely replacing defaults)
276
+ if (theme.tokens) {
277
+ tokens = { ...tokens, ...theme.tokens } as DesignTokens;
278
+ }
279
+
280
+ // Apply advanced features if available in config
281
+ if (config) {
282
+ // Interactive Effects (Phase 2)
283
+ if (config.interactiveEffects) {
284
+ const ie = config.interactiveEffects;
285
+
286
+ // Vortex effects
287
+ if (ie.vortex) {
288
+ tokens['interactive-vortex-enabled'] = String(ie.vortex.enabled ?? false);
289
+ tokens['interactive-vortex-strength'] = String(ie.vortex.strength ?? 0.5);
290
+ tokens['interactive-vortex-radius'] = String(ie.vortex.radius ?? 100);
291
+ tokens['interactive-vortex-decay'] = String(ie.vortex.decay ?? 0.8);
292
+ tokens['interactive-vortex-curl-noise'] = String(ie.vortex.curlNoise ?? false);
293
+ tokens['interactive-vortex-velocity-tracking'] = String(ie.vortex.velocityTracking ?? false);
294
+ }
295
+
296
+ // Chromatic aberration
297
+ if (ie.chromaticAberration) {
298
+ tokens['interactive-chromatic-enabled'] = String(ie.chromaticAberration.enabled ?? false);
299
+ tokens['interactive-chromatic-mode'] = ie.chromaticAberration.mode ?? 'lateral';
300
+ tokens['interactive-chromatic-red-shift'] = String(ie.chromaticAberration.redShift ?? 0.02);
301
+ tokens['interactive-chromatic-green-shift'] = String(ie.chromaticAberration.greenShift ?? 0);
302
+ tokens['interactive-chromatic-blue-shift'] = String(ie.chromaticAberration.blueShift ?? -0.02);
303
+ tokens['interactive-chromatic-edge-only'] = String(ie.chromaticAberration.edgeOnly ?? false);
304
+ tokens['interactive-chromatic-edge-threshold'] = String(ie.chromaticAberration.edgeThreshold ?? 0.5);
305
+ }
306
+
307
+ // Mouse interaction
308
+ if (ie.mouseInteraction) {
309
+ tokens['interactive-mouse-sensitivity'] = String(ie.mouseInteraction.sensitivity ?? 1.0);
310
+ tokens['interactive-mouse-trail-effect'] = String(ie.mouseInteraction.trailEffect ?? false);
311
+ tokens['interactive-mouse-pressure-sensitivity'] = String(ie.mouseInteraction.pressureSensitivity ?? false);
312
+ }
313
+
314
+ // Animation speed
315
+ if (ie.animationSpeed) {
316
+ tokens['interactive-animation-speed-base'] = String(ie.animationSpeed.base ?? 1.0);
317
+ tokens['interactive-animation-speed-multiplier'] = String(ie.animationSpeed.timeMultiplier ?? 1.0);
318
+ }
319
+ }
320
+
321
+ // Optimization (Phase 3)
322
+ if (config.optimization) {
323
+ const opt = config.optimization;
324
+
325
+ // Responsive breakpoints
326
+ if (opt.responsive) {
327
+ if (opt.responsive.breakpoints) {
328
+ tokens['optimization-breakpoint-mobile'] = opt.responsive.breakpoints.mobile ?? '0px';
329
+ tokens['optimization-breakpoint-tablet'] = opt.responsive.breakpoints.tablet ?? '768px';
330
+ tokens['optimization-breakpoint-desktop'] = opt.responsive.breakpoints.desktop ?? '1024px';
331
+ tokens['optimization-breakpoint-wide'] = opt.responsive.breakpoints.wide ?? '1440px';
332
+ }
333
+
334
+ if (opt.responsive.deviceScaling) {
335
+ tokens['optimization-device-scaling-mobile'] = String(opt.responsive.deviceScaling.mobile ?? 0.5);
336
+ tokens['optimization-device-scaling-tablet'] = String(opt.responsive.deviceScaling.tablet ?? 0.75);
337
+ tokens['optimization-device-scaling-desktop'] = String(opt.responsive.deviceScaling.desktop ?? 1.0);
338
+ }
339
+ }
340
+
341
+ // Performance settings
342
+ if (opt.performance) {
343
+ tokens['optimization-performance-fps-target'] = String(opt.performance.fpsTarget ?? 60);
344
+ tokens['optimization-auto-scaling-enabled'] = String(opt.performance.autoScaling ?? false);
345
+ tokens['optimization-monitor-dashboard-enabled'] = String(opt.performance.monitorDashboard ?? false);
346
+ }
347
+
348
+ // Auto-scaling settings
349
+ if (opt.autoScaling) {
350
+ tokens['optimization-auto-scaling-enabled'] = String(opt.autoScaling.enabled ?? false);
351
+ tokens['optimization-auto-scaling-low-end'] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? 0.5);
352
+ tokens['optimization-auto-scaling-mid-range'] = String(opt.autoScaling.qualityThresholds?.midRange ?? 0.75);
353
+ tokens['optimization-auto-scaling-high-end'] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1.0);
354
+ }
355
+ }
356
+
357
+ // Visual Polish (Phase 4)
358
+ if (config.visualPolish) {
359
+ const vp = config.visualPolish;
360
+
361
+ if (vp.borders) {
362
+ tokens['visual-polish-border-iridescent-glow'] = String(vp.borders.iridescentGlow ?? false);
363
+ tokens['visual-polish-border-shimmer-effect'] = String(vp.borders.shimmerEffect ?? false);
364
+ tokens['visual-polish-border-beveled-edges'] = String(vp.borders.beveledEdges ?? false);
365
+ tokens['visual-polish-border-pulsing-glow'] = String(vp.borders.pulsingGlow ?? false);
366
+ }
367
+
368
+ if (vp.contentAwareBlur) {
369
+ tokens['visual-polish-content-aware-blur-enabled'] = String(vp.contentAwareBlur.enabled ?? false);
370
+ tokens['visual-polish-content-aware-depth-detection'] = String(vp.contentAwareBlur.depthDetection ?? false);
371
+ tokens['visual-polish-content-aware-edge-preservation'] = String(vp.contentAwareBlur.edgePreservation ?? false);
372
+ tokens['visual-polish-content-aware-variable-radius'] = String(vp.contentAwareBlur.variableRadius ?? false);
373
+ }
374
+
375
+ if (vp.holographicEffects) {
376
+ tokens['visual-polish-holographic-enabled'] = String(vp.holographicEffects.enabled ?? false);
377
+ tokens['visual-polish-holographic-rainbow-diffraction'] = String(vp.holographicEffects.rainbowDiffraction ?? false);
378
+ tokens['visual-polish-holographic-scanline-animation'] = String(vp.holographicEffects.scanlineAnimation ?? false);
379
+ tokens['visual-polish-holographic-grid-overlay'] = String(vp.holographicEffects.gridOverlay ?? false);
380
+ tokens['visual-polish-holographic-data-stream'] = String(vp.holographicEffects.dataStream ?? false);
381
+ tokens['visual-polish-holographic-pulse-rings'] = String(vp.holographicEffects.pulseRings ?? false);
382
+ }
383
+ }
384
+ }
385
+
386
+ // Apply prefix to all tokens
387
+ const prefixedTokens: DesignTokens = {} as DesignTokens;
388
+ Object.entries(tokens).forEach(([key, value]) => {
389
+ // If the token key already starts with the prefix, use as-is
390
+ // Otherwise, add the prefix
391
+ const prefixedKey = key.startsWith(prefix) ? key : `${prefix}-${key}`;
392
+ prefixedTokens[prefixedKey as keyof DesignTokens] = value;
393
+ });
394
+
395
+ return prefixedTokens;
396
+ }
253
397
 
254
- return tokens;
398
+ /**
399
+ * Convert hex color to RGB
400
+ */
401
+ function hexToRgb(hex: string): string {
402
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
403
+ const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
404
+ hex = hex.replace(shorthandRegex, (m, r, g, b) => {
405
+ return r + r + g + g + b + b;
406
+ });
407
+
408
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
409
+ if (!result || !result[1] || !result[2] || !result[3]) {
410
+ return '0, 0, 0';
411
+ }
412
+ return `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`;
255
413
  }
256
414
 
257
415
  /**
258
- * Convert DesignTokens to Theme-compatible CSS variables
416
+ * Converts DesignTokens to CSS variables
259
417
  *
260
- * @param tokens - DesignTokens object
261
- * @returns CSS variables object compatible with Theme.cssVars
418
+ * @param tokens - The tokens to convert
419
+ * @returns A record of CSS variable names and values
262
420
  */
263
421
  export function designTokensToCSSVars(tokens: Partial<DesignTokens>): Record<string, string> {
264
422
  const cssVars: Record<string, string> = {};
@@ -271,32 +429,3 @@ export function designTokensToCSSVars(tokens: Partial<DesignTokens>): Record<str
271
429
 
272
430
  return cssVars;
273
431
  }
274
-
275
- /**
276
- * Create DesignTokens from Theme with defaults
277
- *
278
- * Converts a Theme to DesignTokens and merges with default tokens.
279
- *
280
- * @param theme - Theme object to convert
281
- * @returns Complete DesignTokens object
282
- */
283
- export function createDesignTokensFromTheme(theme: Theme): DesignTokens {
284
- const partialTokens = themeToDesignTokens(theme);
285
- return createTokens(partialTokens);
286
- }
287
-
288
- /**
289
- * Create a minimal Theme object from DesignTokens
290
- *
291
- * @param tokens - DesignTokens to convert
292
- * @returns Minimal Theme object with cssVars populated
293
- */
294
- export function designTokensToTheme(tokens: Partial<DesignTokens>): Partial<Theme> {
295
- const cssVars = designTokensToCSSVars(tokens);
296
-
297
- return {
298
- name: 'Design Tokens Theme',
299
- cssVars,
300
- __isJSTheme: true,
301
- } as Partial<Theme>;
302
- }