@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
package/dist/theme.js CHANGED
@@ -1,6 +1,10 @@
1
+ import "fs";
2
+
3
+ import "path";
4
+
1
5
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
6
 
3
- import { createContext, useRef, useEffect, useCallback, useMemo, useState, useContext, Component } from "react";
7
+ import React, { createContext, useRef, useEffect, useCallback, useMemo, useState, useContext, Component } from "react";
4
8
 
5
9
  /**
6
10
  * Design Tokens
@@ -296,7 +300,51 @@ import { createContext, useRef, useEffect, useCallback, useMemo, useState, useCo
296
300
  "breakpoint-md": "768px",
297
301
  "breakpoint-lg": "992px",
298
302
  "breakpoint-xl": "1200px",
299
- "breakpoint-xxl": "1440px"
303
+ "breakpoint-xxl": "1440px",
304
+ // Advanced Features - Interactive Effects (Phase 2)
305
+ "interactive-vortex-enabled": "false",
306
+ "interactive-vortex-strength": "0.5",
307
+ "interactive-vortex-radius": "100",
308
+ "interactive-vortex-decay": "0.8",
309
+ "interactive-chromatic-enabled": "false",
310
+ "interactive-chromatic-mode": "lateral",
311
+ "interactive-chromatic-red-shift": "0.02",
312
+ "interactive-chromatic-green-shift": "0",
313
+ "interactive-chromatic-blue-shift": "-0.02",
314
+ "interactive-chromatic-edge-only": "false",
315
+ "interactive-chromatic-edge-threshold": "0.5",
316
+ "interactive-mouse-sensitivity": "1.0",
317
+ "interactive-mouse-trail-effect": "false",
318
+ "interactive-animation-speed-base": "1.0",
319
+ "interactive-animation-speed-multiplier": "1.0",
320
+ // Advanced Features - Optimization (Phase 3)
321
+ "optimization-breakpoint-mobile": "0px",
322
+ "optimization-breakpoint-tablet": "768px",
323
+ "optimization-breakpoint-desktop": "1024px",
324
+ "optimization-breakpoint-wide": "1440px",
325
+ "optimization-device-scaling-mobile": "0.5",
326
+ "optimization-device-scaling-tablet": "0.75",
327
+ "optimization-device-scaling-desktop": "1.0",
328
+ "optimization-performance-fps-target": "60",
329
+ "optimization-auto-scaling-enabled": "false",
330
+ "optimization-auto-scaling-low-end": "0.5",
331
+ "optimization-auto-scaling-mid-range": "0.75",
332
+ "optimization-auto-scaling-high-end": "1.0",
333
+ // Advanced Features - Visual Polish (Phase 4)
334
+ "visual-polish-border-iridescent-glow": "false",
335
+ "visual-polish-border-shimmer-effect": "false",
336
+ "visual-polish-border-beveled-edges": "false",
337
+ "visual-polish-border-pulsing-glow": "false",
338
+ "visual-polish-content-aware-blur-enabled": "false",
339
+ "visual-polish-content-aware-depth-detection": "false",
340
+ "visual-polish-content-aware-edge-preservation": "false",
341
+ "visual-polish-content-aware-variable-radius": "false",
342
+ "visual-polish-holographic-enabled": "false",
343
+ "visual-polish-holographic-rainbow-diffraction": "false",
344
+ "visual-polish-holographic-scanline-animation": "false",
345
+ "visual-polish-holographic-grid-overlay": "false",
346
+ "visual-polish-holographic-data-stream": "false",
347
+ "visual-polish-holographic-pulse-rings": "false"
300
348
  };
301
349
 
302
350
  /**
@@ -376,412 +424,278 @@ import { createContext, useRef, useEffect, useCallback, useMemo, useState, useCo
376
424
  }
377
425
 
378
426
  /**
379
- * Theme System Error Handling
427
+ * Theme Adapter
380
428
  *
381
- * Centralized error handling for the Atomix theme system.
382
- * Provides custom error classes and logging utilities.
383
- */
384
- /**
385
- * Theme error codes
386
- */ var ThemeErrorCode, LogLevel;
387
-
388
- !function(ThemeErrorCode) {
389
- /** Theme not found in registry */
390
- ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
391
- /** Theme failed to load */
392
- ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
393
- /** Theme validation failed */
394
- ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
395
- /** Configuration loading failed */
396
- ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
397
- /** Configuration validation failed */
398
- ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
399
- /** Circular dependency detected */
400
- ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
401
- /** Missing dependency */
402
- ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
403
- /** Storage operation failed */
404
- ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
405
- /** Invalid theme name */
406
- ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
407
- /** CSS injection failed */
408
- ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
409
- /** Invalid color format */
410
- ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
411
- /** Missing required token */
412
- ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
413
- /** Accessibility contrast violation */
414
- ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
415
- /** Invalid token type */
416
- ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
417
- /** Unknown error */
418
- ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
419
- }(ThemeErrorCode || (ThemeErrorCode = {}));
420
-
421
- /**
422
- * Custom error class for theme-related errors
429
+ * Converts between Theme objects and DesignTokens
423
430
  */
424
- class ThemeError extends Error {
425
- constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
426
- super(message), this.name = "ThemeError", this.code = code, this.context = context,
427
- this.timestamp = Date.now(),
428
- // Maintains proper stack trace for where our error was thrown (only available on V8)
429
- Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
430
- }
431
- /**
432
- * Convert error to JSON for logging
433
- */ toJSON() {
434
- return {
435
- name: this.name,
436
- message: this.message,
437
- code: this.code,
438
- context: this.context,
439
- timestamp: this.timestamp,
440
- stack: this.stack
441
- };
442
- }
443
- }
444
-
445
- /**
446
- * Log level
447
- */ !function(LogLevel) {
448
- LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
449
- LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
450
- }(LogLevel || (LogLevel = {}));
451
-
452
431
  /**
453
- * Theme Logger
454
- *
455
- * Centralized logging for the theme system.
456
- * Replaces console statements with structured logging.
457
- */
458
- class ThemeLogger {
459
- constructor(config = {}) {
460
- this.config = {
461
- level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
462
- enableConsole: config.enableConsole ?? !0,
463
- onError: config.onError,
464
- onWarn: config.onWarn,
465
- onInfo: config.onInfo,
466
- onDebug: config.onDebug
467
- };
468
- }
469
- /**
470
- * Log an error
471
- */ error(message, error, context) {
472
- if (this.config.level < LogLevel.ERROR) return;
473
- const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
474
- this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
475
- error: errorObj,
476
- context: {
477
- ...context,
478
- ...themeError.context
479
- },
480
- code: themeError.code
481
- }), this.config.onError?.(themeError, context);
482
- }
483
- /**
484
- * Log a warning
485
- */ warn(message, context) {
486
- this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
487
- this.config.onWarn?.(message, context));
432
+ * Convert a Theme object to DesignTokens
433
+ */ function themeToDesignTokens(theme) {
434
+ const tokens = {};
435
+ // Convert colors
436
+ if (theme.palette) {
437
+ // Primary color
438
+ if (theme.palette.primary) {
439
+ const primaryMain = theme.palette.primary.main;
440
+ tokens.primary = primaryMain;
441
+ const rgb = hexToRgb$2(primaryMain);
442
+ rgb && (tokens["primary-rgb"] = rgb);
443
+ }
444
+ // Secondary color
445
+ if (theme.palette.secondary) {
446
+ const secondaryMain = theme.palette.secondary.main;
447
+ tokens.secondary = secondaryMain;
448
+ const rgb = hexToRgb$2(secondaryMain);
449
+ rgb && (tokens["secondary-rgb"] = rgb);
450
+ }
451
+ // Other colors
452
+ const colorKeys = [ "error", "warning", "info", "success" ];
453
+ for (const key of colorKeys) if (theme.palette[key]) {
454
+ const colorMain = theme.palette[key].main;
455
+ tokens[key] = colorMain;
456
+ const rgb = hexToRgb$2(colorMain);
457
+ rgb && (tokens[`${key}-rgb`] = rgb);
458
+ }
459
+ // Background colors
460
+ theme.palette.background && (tokens["body-bg"] = theme.palette.background.default),
461
+ // Text colors
462
+ theme.palette.text && (tokens["body-color"] = theme.palette.text.primary);
488
463
  }
489
- /**
490
- * Log an info message
491
- */ info(message, context) {
492
- this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
493
- this.config.onInfo?.(message, context));
464
+ // Convert typography
465
+ // Convert border radius
466
+ if (theme.typography && (tokens["body-font-family"] = theme.typography.fontFamily,
467
+ tokens["body-font-size"] = `${theme.typography.fontSize}px`, tokens["font-weight-normal"] = `${theme.typography.fontWeightRegular}`,
468
+ tokens["font-weight-bold"] = `${theme.typography.fontWeightBold}`),
469
+ // Convert spacing
470
+ "function" == typeof theme.spacing && (
471
+ // If spacing is a function, call it with some values to get results
472
+ tokens["spacing-1"] = theme.spacing(1), tokens["spacing-2"] = theme.spacing(2),
473
+ tokens["spacing-4"] = theme.spacing(4)),
474
+ // Convert breakpoints
475
+ theme.breakpoints?.values && (tokens["breakpoint-xs"] = `${theme.breakpoints.values.xs}px`,
476
+ tokens["breakpoint-sm"] = `${theme.breakpoints.values.sm}px`, tokens["breakpoint-md"] = `${theme.breakpoints.values.md}px`,
477
+ tokens["breakpoint-lg"] = `${theme.breakpoints.values.lg}px`, tokens["breakpoint-xl"] = `${theme.breakpoints.values.xl}px`),
478
+ // Convert shadows
479
+ theme.shadows && (tokens["box-shadow"] = theme.shadows[2], // Use a moderate shadow
480
+ tokens["box-shadow-sm"] = theme.shadows[1], tokens["box-shadow-lg"] = theme.shadows[3]),
481
+ // Convert transitions
482
+ theme.transitions && (tokens["transition-duration-base"] = `${theme.transitions.duration.standard}ms`),
483
+ // Convert z-index
484
+ theme.zIndex && (tokens["z-modal"] = `${theme.zIndex.modal}`, tokens["z-popover"] = `${theme.zIndex.popover}`,
485
+ tokens["z-tooltip"] = `${theme.zIndex.tooltip}`), theme.borderRadius) {
486
+ const baseRadius = theme.borderRadius.base;
487
+ tokens["border-radius"] = "number" == typeof baseRadius ? `${baseRadius}px` : baseRadius;
494
488
  }
495
- /**
496
- * Log a debug message
497
- */ debug(message, context) {
498
- this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
489
+ // Add advanced feature tokens if available in theme
490
+ if (theme.custom) {
491
+ // Interactive Effects (Phase 2)
492
+ if (theme.custom.interactiveEffects) {
493
+ const ie = theme.custom.interactiveEffects;
494
+ // Vortex effects
495
+ ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
496
+ tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
497
+ tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8)),
498
+ // Chromatic aberration
499
+ ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
500
+ tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
501
+ tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
502
+ tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
503
+ tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
504
+ tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
505
+ tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
506
+ // Mouse interaction
507
+ ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
508
+ tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1)),
509
+ // Animation speed
510
+ ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
511
+ tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
512
+ }
513
+ // Optimization (Phase 3)
514
+ if (theme.custom.optimization) {
515
+ const opt = theme.custom.optimization;
516
+ // Responsive breakpoints
517
+ opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
518
+ tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
519
+ tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
520
+ tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
521
+ opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
522
+ tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
523
+ tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
524
+ // Performance settings
525
+ opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
526
+ tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1)),
527
+ // Auto-scaling settings
528
+ opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
529
+ tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
530
+ tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
531
+ tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
532
+ }
533
+ // Visual Polish (Phase 4)
534
+ if (theme.custom.visualPolish) {
535
+ const vp = theme.custom.visualPolish;
536
+ vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
537
+ tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
538
+ tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
539
+ tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
540
+ vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
541
+ tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
542
+ tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
543
+ tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
544
+ vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
545
+ tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
546
+ tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
547
+ tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
548
+ tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
549
+ tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
550
+ }
499
551
  }
552
+ // Create full tokens object with defaults
553
+ return createTokens(tokens);
500
554
  }
501
555
 
502
556
  /**
503
- * Default logger instance
504
- */ let defaultLogger = null;
505
-
506
- /**
507
- * Get or create default logger
508
- */ function getLogger() {
509
- return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
510
- }
511
-
512
- /**
513
- * Core Theme Functions
514
- *
515
- * Simplified theme system using DesignTokens only.
516
- * Config-first approach: loads from atomix.config.ts when no input is provided.
517
- */
518
- /**
519
- * Create theme CSS from DesignTokens
557
+ * Converts an AtomixConfig to DesignTokens
520
558
  *
521
- * **Config-First Approach**: If no input is provided, loads from `atomix.config.ts`.
559
+ * This function maps the configuration from the user-facing format
560
+ * to the internal DesignTokens format used by the theme system.
522
561
  *
523
- * @param input - DesignTokens (partial) or undefined (loads from config)
524
- * @param options - CSS generation options (prefix is automatically read from config if not provided)
525
- * @returns CSS string with custom properties
526
- * @throws Error if config loading fails when no input is provided
562
+ * @param config - The configuration object to convert
563
+ * @returns DesignTokens object ready for theme generation
527
564
  *
528
565
  * @example
529
566
  * ```typescript
530
- * // Loads from atomix.config.ts
531
- * const css = createTheme();
532
- *
533
- * // Using DesignTokens
534
- * const css = createTheme({
535
- * 'primary': '#7c3aed',
536
- * 'spacing-4': '1rem',
537
- * });
567
+ * import { configToTokens } from '@shohojdhara/atomix/theme';
538
568
  *
539
- * // With custom options
540
- * const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
569
+ * const config = {
570
+ * prefix: 'myapp',
571
+ * theme: { extend: { colors: { primary: { main: '#7AFFD7' } } } }
572
+ * };
573
+ * const tokens = configToTokens(config);
541
574
  * ```
542
- */ function createTheme(input, options) {
543
- // Validate options if provided
544
- if (options?.prefix) {
545
- const prefixPattern = /^[a-z][a-z0-9-]*$/;
546
- if (!prefixPattern.test(options.prefix)) throw new ThemeError(`Invalid CSS variable prefix: "${options.prefix}". Prefix must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens (e.g., "atomix", "my-app").`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
547
- prefix: options.prefix,
548
- pattern: prefixPattern.toString()
549
- });
550
- }
551
- // Validate selector if provided
552
- if (options?.selector && ("string" != typeof options.selector || 0 === options.selector.trim().length)) throw new ThemeError(`Invalid CSS selector: "${options.selector}". Selector must be a non-empty string (e.g., ":root", ".my-theme").`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
553
- selector: options.selector
554
- });
555
- // Determine tokens based on input
556
- let tokens;
557
- if (input) {
558
- // Validate input tokens structure
559
- if ("object" != typeof input || null === input || Array.isArray(input)) throw new ThemeError(`Invalid tokens input. Expected an object with DesignTokens, but received: ${typeof input}.`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
560
- inputType: typeof input
561
- });
562
- // Use DesignTokens directly
563
- tokens = input;
564
- }
565
- // Merge with defaults and generate CSS
566
- else
567
- // Auto-loading config from file system is removed for browser compatibility.
568
- // If no input is provided, we return an empty theme (using defaults only) or user must provide tokens.
569
- // This allows createTheme to be isomorphic.
570
- // Warn in development if no input provided
571
- "production" !== process.env.NODE_ENV && "undefined" != typeof window && console.warn("Atomix: createTheme() called without tokens. Using default tokens only."),
572
- tokens = {};
573
- const allTokens = createTokens(tokens), prefix = options?.prefix ?? "atomix";
574
- // Get prefix from options or use default
575
- return generateCSSVariables$1(allTokens, {
576
- ...options,
577
- prefix: prefix
578
- });
579
- }
580
-
581
- /**
582
- * Theme Composition Utilities
583
- *
584
- * Simplified utilities for composing and merging DesignTokens.
585
- */
586
- // ============================================================================
587
- // Deep Merge Utility
588
- // ============================================================================
589
- /**
590
- * Check if value is an object
591
- */ function isObject$6(item) {
592
- return item && "object" == typeof item && !Array.isArray(item) && "function" != typeof item;
593
- }
594
-
595
- /**
596
- * Deep merge multiple objects
597
- * Later objects override earlier ones
598
- */ function deepMerge(...objects) {
599
- if (0 === objects.length) return {};
600
- if (1 === objects.length) return objects[0];
601
- const [target, ...sources] = objects, result = {
602
- ...target
575
+ */ function configToTokens(config) {
576
+ const prefix = config.prefix || "atomix", theme = config.theme || {};
577
+ // Start with default tokens
578
+ let tokens = {
579
+ ...defaultTokens
603
580
  };
604
- for (const source of sources) if (source) for (const key in source) {
605
- if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
606
- const targetValue = result[key], sourceValue = source[key];
607
- isObject$6(targetValue) && isObject$6(sourceValue) ?
608
- // Recursively merge objects
609
- result[key] = deepMerge(targetValue, sourceValue) :
610
- // Override with source value
611
- result[key] = sourceValue;
581
+ // Apply theme extensions
582
+ // Apply advanced features if available in config
583
+ if (theme.extend &&
584
+ // Apply extensions to tokens
585
+ Object.entries(theme.extend).forEach((([category, values]) => {
586
+ "object" == typeof values && null !== values && Object.entries(values).forEach((([key, value]) => {
587
+ // Map theme categories to token names
588
+ const tokenName = `${category}-${key}`;
589
+ "string" == typeof value || "number" == typeof value ? tokens[tokenName] = String(value) : "object" == typeof value && null !== value &&
590
+ // Handle nested objects like color scales
591
+ Object.entries(value).forEach((([nestedKey, nestedValue]) => {
592
+ "string" != typeof nestedValue && "number" != typeof nestedValue || (tokens[`${tokenName}-${nestedKey}`] = String(nestedValue));
593
+ }));
594
+ }));
595
+ })),
596
+ // Apply theme tokens if provided (completely replacing defaults)
597
+ theme.tokens && (tokens = {
598
+ ...tokens,
599
+ ...theme.tokens
600
+ }), config) {
601
+ // Interactive Effects (Phase 2)
602
+ if (config.interactiveEffects) {
603
+ const ie = config.interactiveEffects;
604
+ // Vortex effects
605
+ ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
606
+ tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
607
+ tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8), tokens["interactive-vortex-curl-noise"] = String(ie.vortex.curlNoise ?? !1),
608
+ tokens["interactive-vortex-velocity-tracking"] = String(ie.vortex.velocityTracking ?? !1)),
609
+ // Chromatic aberration
610
+ ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
611
+ tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
612
+ tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
613
+ tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
614
+ tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
615
+ tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
616
+ tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
617
+ // Mouse interaction
618
+ ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
619
+ tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1),
620
+ tokens["interactive-mouse-pressure-sensitivity"] = String(ie.mouseInteraction.pressureSensitivity ?? !1)),
621
+ // Animation speed
622
+ ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
623
+ tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
624
+ }
625
+ // Optimization (Phase 3)
626
+ if (config.optimization) {
627
+ const opt = config.optimization;
628
+ // Responsive breakpoints
629
+ opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
630
+ tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
631
+ tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
632
+ tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
633
+ opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
634
+ tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
635
+ tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
636
+ // Performance settings
637
+ opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
638
+ tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1),
639
+ tokens["optimization-monitor-dashboard-enabled"] = String(opt.performance.monitorDashboard ?? !1)),
640
+ // Auto-scaling settings
641
+ opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
642
+ tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
643
+ tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
644
+ tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
645
+ }
646
+ // Visual Polish (Phase 4)
647
+ if (config.visualPolish) {
648
+ const vp = config.visualPolish;
649
+ vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
650
+ tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
651
+ tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
652
+ tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
653
+ vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
654
+ tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
655
+ tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
656
+ tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
657
+ vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
658
+ tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
659
+ tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
660
+ tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
661
+ tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
662
+ tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
663
+ }
612
664
  }
613
- return result;
665
+ // Apply prefix to all tokens
666
+ const prefixedTokens = {};
667
+ return Object.entries(tokens).forEach((([key, value]) => {
668
+ // If the token key already starts with the prefix, use as-is
669
+ // Otherwise, add the prefix
670
+ const prefixedKey = key.startsWith(prefix) ? key : `${prefix}-${key}`;
671
+ prefixedTokens[prefixedKey] = value;
672
+ })), prefixedTokens;
614
673
  }
615
674
 
616
675
  /**
617
- * Merge multiple DesignTokens objects into a single DesignTokens object
618
- *
619
- * @param tokens - DesignTokens objects to merge
620
- * @returns Merged DesignTokens object
621
- *
622
- * @example
623
- * ```typescript
624
- * const baseTokens = { 'primary': '#000', 'spacing-4': '1rem' };
625
- * const customTokens = { 'secondary': '#fff', 'spacing-4': '1.5rem' };
626
- * const merged = mergeTheme(baseTokens, customTokens);
627
- * // Returns: { 'primary': '#000', 'secondary': '#fff', 'spacing-4': '1.5rem' }
628
- * ```
629
- */ function mergeTheme(...tokens) {
630
- return deepMerge({}, ...tokens);
676
+ * Convert hex color to RGB
677
+ */ function hexToRgb$2(hex) {
678
+ hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, ((m, r, g, b) => r + r + g + g + b + b));
679
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
680
+ return result && result[1] && result[2] && result[3] ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` : "0, 0, 0";
631
681
  }
632
682
 
633
683
  /**
634
- * Extend DesignTokens with additional tokens
684
+ * Converts DesignTokens to CSS variables
635
685
  *
636
- * @param baseTokens - Base DesignTokens to extend
637
- * @param extension - Additional DesignTokens to merge
638
- * @returns Extended DesignTokens object
639
- *
640
- * @example
641
- * ```typescript
642
- * const base = { 'primary': '#000' };
643
- * const extended = extendTheme(base, { 'secondary': '#fff' });
644
- * // Returns: { 'primary': '#000', 'secondary': '#fff' }
645
- * ```
646
- */ function extendTheme(baseTokens, extension) {
647
- return mergeTheme(baseTokens, extension);
648
- }
649
-
650
- /**
651
- * Create a new theme registry
652
- */ function createThemeRegistry() {
653
- return {};
686
+ * @param tokens - The tokens to convert
687
+ * @returns A record of CSS variable names and values
688
+ */ function designTokensToCSSVars(tokens) {
689
+ const cssVars = {};
690
+ return Object.entries(tokens).forEach((([key, value]) => {
691
+ void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
692
+ })), cssVars;
654
693
  }
655
694
 
656
- /**
657
- * Register a theme
658
- * @param registry - Theme registry object
659
- * @param id - Theme identifier
660
- * @param metadata - Theme metadata
661
- */ function registerTheme(registry, id, metadata) {
662
- registry[id] = metadata;
663
- }
695
+ var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {};
664
696
 
665
- /**
666
- * Unregister a theme
667
- * @param registry - Theme registry object
668
- * @param id - Theme identifier
669
- */ function unregisterTheme(registry, id) {
670
- const exists = id in registry;
671
- return delete registry[id], exists;
672
- }
673
-
674
- /**
675
- * Check if a theme is registered
676
- * @param registry - Theme registry object
677
- * @param id - Theme identifier
678
- */ function hasTheme(registry, id) {
679
- return id in registry;
680
- }
681
-
682
- /**
683
- * Get theme metadata
684
- * @param registry - Theme registry object
685
- * @param id - Theme identifier
686
- */ function getTheme(registry, id) {
687
- return registry[id];
688
- }
689
-
690
- /**
691
- * Get all registered theme metadata
692
- * @param registry - Theme registry object
693
- */ function getAllThemes(registry) {
694
- return Object.values(registry);
695
- }
696
-
697
- /**
698
- * Get all registered theme IDs
699
- * @param registry - Theme registry object
700
- */ function getThemeIds(registry) {
701
- return Object.keys(registry);
702
- }
703
-
704
- /**
705
- * Clear all registered themes
706
- * @param registry - Theme registry object
707
- */ function clearThemes(registry) {
708
- Object.keys(registry).forEach((key => delete registry[key]));
709
- }
710
-
711
- /**
712
- * Get the number of registered themes
713
- * @param registry - Theme registry object
714
- */ function getThemeCount(registry) {
715
- return Object.keys(registry).length;
716
- }
717
-
718
- /**
719
- * CSS Injection Utilities
720
- *
721
- * Inject CSS into HTML head via <style> element.
722
- */
723
- /**
724
- * Check if running in browser environment
725
- */ function isBrowser$1() {
726
- return "undefined" != typeof document;
727
- }
728
-
729
- /**
730
- * Inject CSS into HTML head via <style> element
731
- *
732
- * Creates or updates a style element in the document head.
733
- * If an element with the same ID exists, it will be updated.
734
- *
735
- * @param css - CSS string to inject
736
- * @param id - Style element ID (default: 'atomix-theme')
737
- *
738
- * @example
739
- * ```typescript
740
- * const css = ':root { --atomix-color-primary: #7AFFD7; }';
741
- * injectCSS(css);
742
- *
743
- * // With custom ID
744
- * injectCSS(css, 'my-custom-theme');
745
- * ```
746
- */ function injectCSS$1(css, id = "atomix-theme") {
747
- if (!isBrowser$1()) return;
748
- let styleElement = document.getElementById(id);
749
- styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
750
- styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
751
- styleElement.textContent = css;
752
- }
753
-
754
- /**
755
- * Remove injected CSS from DOM
756
- *
757
- * Removes the style element with the given ID from the document head.
758
- *
759
- * @param id - Style element ID to remove (default: 'atomix-theme')
760
- *
761
- * @example
762
- * ```typescript
763
- * removeCSS(); // Removes default 'atomix-theme'
764
- * removeCSS('my-custom-theme'); // Removes custom ID
765
- * ```
766
- */ function removeCSS(id = "atomix-theme") {
767
- if (!isBrowser$1()) return;
768
- const styleElement = document.getElementById(id);
769
- styleElement && styleElement.remove();
770
- }
771
-
772
- /**
773
- * Check if CSS is already injected
774
- *
775
- * @param id - Style element ID to check (default: 'atomix-theme')
776
- * @returns True if style element exists
777
- */ function isCSSInjected(id = "atomix-theme") {
778
- return !!isBrowser$1() && null !== document.getElementById(id);
779
- }
780
-
781
- var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {};
782
-
783
- function getDefaultExportFromCjs(x) {
784
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
697
+ function getDefaultExportFromCjs(x) {
698
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
785
699
  }
786
700
 
787
701
  var fails$9 = function(exec) {
@@ -862,7 +776,7 @@ var match, version, createPropertyDescriptor$2 = function(bitmap, value) {
862
776
  return it;
863
777
  }, IndexedObject$1 = indexedObject, requireObjectCoercible$2 = requireObjectCoercible$3, toIndexedObject$2 = function(it) {
864
778
  return IndexedObject$1(requireObjectCoercible$2(it));
865
- }, isCallable$7 = isCallable$8, isObject$5 = function(it) {
779
+ }, isCallable$7 = isCallable$8, isObject$6 = function(it) {
866
780
  return "object" == typeof it ? null !== it : isCallable$7(it);
867
781
  }, path$3 = {}, path$2 = path$3, globalThis$b = globalThis_1, isCallable$6 = isCallable$8, aFunction = function(variable) {
868
782
  return isCallable$6(variable) ? variable : void 0;
@@ -901,7 +815,7 @@ var environmentV8Version = version, V8_VERSION = environmentV8Version, fails$5 =
901
815
  return "Object";
902
816
  }
903
817
  }(argument) + " is not a function");
904
- }, aCallable$2 = aCallable$3, isNullOrUndefined = isNullOrUndefined$2, call$2 = functionCall, isCallable$3 = isCallable$8, isObject$4 = isObject$5, $TypeError$5 = TypeError, sharedStore = {
818
+ }, aCallable$2 = aCallable$3, isNullOrUndefined = isNullOrUndefined$2, call$2 = functionCall, isCallable$3 = isCallable$8, isObject$5 = isObject$6, $TypeError$5 = TypeError, sharedStore = {
905
819
  exports: {}
906
820
  }, globalThis$7 = globalThis_1, defineProperty = Object.defineProperty, globalThis$6 = globalThis_1, store$1 = sharedStore.exports = globalThis$6["__core-js_shared__"] || function(key, value) {
907
821
  try {
@@ -933,25 +847,25 @@ var key, value, store = sharedStore.exports, requireObjectCoercible$1 = requireO
933
847
  }, wellKnownSymbol$5 = function(name) {
934
848
  return hasOwn$2(WellKnownSymbolsStore, name) || (WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn$2(Symbol$1, name) ? Symbol$1[name] : createWellKnownSymbol("Symbol." + name)),
935
849
  WellKnownSymbolsStore[name];
936
- }, call$1 = functionCall, isObject$3 = isObject$5, isSymbol$1 = isSymbol$2, $TypeError$4 = TypeError, TO_PRIMITIVE = wellKnownSymbol$5("toPrimitive"), toPrimitive = function(input, pref) {
937
- if (!isObject$3(input) || isSymbol$1(input)) return input;
850
+ }, call$1 = functionCall, isObject$4 = isObject$6, isSymbol$1 = isSymbol$2, $TypeError$4 = TypeError, TO_PRIMITIVE = wellKnownSymbol$5("toPrimitive"), toPrimitive = function(input, pref) {
851
+ if (!isObject$4(input) || isSymbol$1(input)) return input;
938
852
  var result, func, exoticToPrim = (func = input[TO_PRIMITIVE], isNullOrUndefined(func) ? void 0 : aCallable$2(func));
939
853
  if (exoticToPrim) {
940
854
  if (void 0 === pref && (pref = "default"), result = call$1(exoticToPrim, input, pref),
941
- !isObject$3(result) || isSymbol$1(result)) return result;
855
+ !isObject$4(result) || isSymbol$1(result)) return result;
942
856
  throw new $TypeError$4("Can't convert object to primitive value");
943
857
  }
944
858
  return void 0 === pref && (pref = "number"), function(input, pref) {
945
859
  var fn, val;
946
- if ("string" === pref && isCallable$3(fn = input.toString) && !isObject$4(val = call$2(fn, input))) return val;
947
- if (isCallable$3(fn = input.valueOf) && !isObject$4(val = call$2(fn, input))) return val;
948
- if ("string" !== pref && isCallable$3(fn = input.toString) && !isObject$4(val = call$2(fn, input))) return val;
860
+ if ("string" === pref && isCallable$3(fn = input.toString) && !isObject$5(val = call$2(fn, input))) return val;
861
+ if (isCallable$3(fn = input.valueOf) && !isObject$5(val = call$2(fn, input))) return val;
862
+ if ("string" !== pref && isCallable$3(fn = input.toString) && !isObject$5(val = call$2(fn, input))) return val;
949
863
  throw new $TypeError$5("Can't convert object to primitive value");
950
864
  }(input, pref);
951
865
  }, isSymbol = isSymbol$2, toPropertyKey$2 = function(argument) {
952
866
  var key = toPrimitive(argument, "string");
953
867
  return isSymbol(key) ? key : key + "";
954
- }, isObject$2 = isObject$5, document$1 = globalThis_1.document, EXISTS = isObject$2(document$1) && isObject$2(document$1.createElement), ie8DomDefine = !descriptors && !fails$9((function() {
868
+ }, isObject$3 = isObject$6, document$1 = globalThis_1.document, EXISTS = isObject$3(document$1) && isObject$3(document$1.createElement), ie8DomDefine = !descriptors && !fails$9((function() {
955
869
  // eslint-disable-next-line es/no-object-defineproperty -- required for testing
956
870
  return 7 !== Object.defineProperty((it = "div", EXISTS ? document$1.createElement(it) : {}), "a", {
957
871
  get: function() {
@@ -981,8 +895,8 @@ var fails$3 = fails$9, isCallable$2 = isCallable$8, replacement = /#|\.prototype
981
895
  value: 42,
982
896
  writable: !1
983
897
  }).prototype;
984
- })), isObject$1 = isObject$5, $String$1 = String, $TypeError$3 = TypeError, DESCRIPTORS$1 = descriptors, IE8_DOM_DEFINE = ie8DomDefine, V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug, anObject = function(argument) {
985
- if (isObject$1(argument)) return argument;
898
+ })), isObject$2 = isObject$6, $String$1 = String, $TypeError$3 = TypeError, DESCRIPTORS$1 = descriptors, IE8_DOM_DEFINE = ie8DomDefine, V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug, anObject = function(argument) {
899
+ if (isObject$2(argument)) return argument;
986
900
  throw new $TypeError$3($String$1(argument) + " is not an object");
987
901
  }, toPropertyKey = toPropertyKey$2, $TypeError$2 = TypeError, $defineProperty = Object.defineProperty, $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
988
902
 
@@ -1099,7 +1013,7 @@ var globalThis$2 = globalThis_1, path = path$3, getBuiltInPrototypeMethod$3 = fu
1099
1013
  if (pureMethod) return pureMethod;
1100
1014
  var NativeConstructor = globalThis$2[CONSTRUCTOR], NativePrototype = NativeConstructor && NativeConstructor.prototype;
1101
1015
  return NativePrototype && NativePrototype[METHOD];
1102
- }, includes$4 = getBuiltInPrototypeMethod$3("Array", "includes"), isObject = isObject$5, classof$3 = classofRaw$2, MATCH$1 = wellKnownSymbol$5("match"), $TypeError$1 = TypeError, test = {};
1016
+ }, includes$4 = getBuiltInPrototypeMethod$3("Array", "includes"), isObject$1 = isObject$6, classof$3 = classofRaw$2, MATCH$1 = wellKnownSymbol$5("match"), $TypeError$1 = TypeError, test = {};
1103
1017
 
1104
1018
  test[wellKnownSymbol$5("toStringTag")] = "z";
1105
1019
 
@@ -1115,7 +1029,7 @@ var TO_STRING_TAG_SUPPORT = "[object z]" === String(test), isCallable = isCallab
1115
1029
  }, $String = String, MATCH = wellKnownSymbol$5("match"), $$1 = _export, notARegExp = function(it) {
1116
1030
  if (function(it) {
1117
1031
  var isRegExp;
1118
- return isObject(it) && (void 0 !== (isRegExp = it[MATCH$1]) ? !!isRegExp : "RegExp" === classof$3(it));
1032
+ return isObject$1(it) && (void 0 !== (isRegExp = it[MATCH$1]) ? !!isRegExp : "RegExp" === classof$3(it));
1119
1033
  }(it)) throw new $TypeError$1("The method doesn't accept regular expressions");
1120
1034
  return it;
1121
1035
  }, requireObjectCoercible = requireObjectCoercible$3, toString = function(argument) {
@@ -1153,18 +1067,595 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1153
1067
  }));
1154
1068
 
1155
1069
  /**
1156
- * Theme System Constants
1157
- *
1158
- * Centralized constants for the theme system to avoid magic numbers and strings.
1159
- */
1160
- /**
1161
- * Default storage key for theme persistence
1162
- */ "undefined" != typeof process && process.env;
1070
+ * Load theme from config file (synchronous, Node.js only)
1071
+ * @param configPath - Path to config file (default: atomix.config.ts)
1072
+ * @returns DesignTokens from theme configuration
1073
+ * @throws Error if config loading is not available in browser environment
1074
+ */
1075
+ /**
1076
+ * Helper function to load config from a specific path
1077
+ */
1078
+ function loadConfigAtPath(path, required, defaultConfig) {
1079
+ try {
1080
+ // Use dynamic import for ESM compatibility
1081
+ const configModule = require(path), config = configModule.default || configModule;
1082
+ // Validate it's an AtomixConfig
1083
+ if (config && "object" == typeof config) return config;
1084
+ throw new Error("Invalid config format");
1085
+ } catch (error) {
1086
+ if (required) throw new Error(`Failed to load config from ${path}: ${error.message}`);
1087
+ // Return default config if not required
1088
+ return defaultConfig;
1089
+ }
1090
+ }
1091
+
1092
+ /**
1093
+ * Core Theme Functions
1094
+ *
1095
+ * Unified theme system that handles both DesignTokens and Theme objects.
1096
+ * Config-first approach: loads from atomix.config.ts when no input is provided.
1097
+ * Config-first approach: loads advanced features from config when available.
1098
+ */
1099
+ /**
1100
+ * Create theme CSS from tokens or Theme object
1101
+ *
1102
+ * **Config-First Approach**: If no input is provided, loads from `atomix.config.ts`.
1103
+ * Config file is required for automatic loading.
1104
+ *
1105
+ * @param input - DesignTokens (partial), Theme object, or undefined (loads from config)
1106
+ * @param options - CSS generation options (prefix is automatically read from config if not provided)
1107
+ * @returns CSS string with custom properties
1108
+ * @throws Error if config loading fails when no input is provided
1109
+ *
1110
+ * @example
1111
+ * ```typescript
1112
+ * // Loads from atomix.config.ts (config file required)
1113
+ * const css = createTheme();
1114
+ *
1115
+ * // Using DesignTokens
1116
+ * const css = createTheme({
1117
+ * 'primary': '#7c3aed',
1118
+ * 'spacing-4': '1rem',
1119
+ * });
1120
+ *
1121
+ * // Using Theme object
1122
+ * const theme = createThemeObject({ palette: { primary: { main: '#7c3aed' } } });
1123
+ * const css = createTheme(theme);
1124
+ *
1125
+ * // With custom options
1126
+ * const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
1127
+ * ```
1128
+ */ function createTheme(input, options) {
1129
+ let tokens, configPrefix;
1130
+ // If no input provided, load from config (required)
1131
+ if (input)
1132
+ // Convert Theme object to DesignTokens
1133
+ tokens = !0 === input.__isJSTheme || input.palette && input.typography ? themeToDesignTokens(input) : input; else {
1134
+ const configTokens = function() {
1135
+ // Check if we're in a browser environment
1136
+ if ("undefined" != typeof window) throw new Error("loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.");
1137
+ // Use dynamic import to load the config loader
1138
+ // This allows bundlers to handle external dependencies properly
1139
+ let loadAtomixConfig;
1140
+ try {
1141
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1142
+ const {loadAtomixConfig: loader} = require("../../config/loader");
1143
+ loadAtomixConfig = loader;
1144
+ } catch (error) {
1145
+ throw new Error("Config loader module not available");
1146
+ }
1147
+ const config = loadAtomixConfig({
1148
+ configPath: undefined,
1149
+ required: !0
1150
+ });
1151
+ if (!config?.theme) return createTokens({});
1152
+ if ((obj = config.theme) && "object" == typeof obj && (obj.palette || obj.typography || obj.spacing || obj.breakpoints || obj.colors)) return themeToDesignTokens(config.theme);
1153
+ // Handle the config.theme object which has extend/tokens/themes properties
1154
+ // Extract the actual tokens from the theme configuration
1155
+ // Helper type guard function
1156
+ var obj;
1157
+ /**
1158
+ * Load Atomix configuration from project root
1159
+ *
1160
+ * Attempts to load atomix.config.ts, atomix.config.js, or atomix.config.json from the current working directory.
1161
+ * Falls back to default config if file doesn't exist.
1162
+ *
1163
+ * @param options - Loader options
1164
+ * @returns Loaded configuration or default
1165
+ *
1166
+ * @example
1167
+ * ```typescript
1168
+ * import { loadAtomixConfig } from '@shohojdhara/atomix/config';
1169
+ * import { createTheme } from '@shohojdhara/atomix/theme';
1170
+ *
1171
+ * const config = loadAtomixConfig();
1172
+ * const theme = createTheme(config.theme?.tokens || {});
1173
+ * ```
1174
+ */ const themeConfig = config.theme;
1175
+ let tokensToApply = {};
1176
+ return themeConfig.tokens ?
1177
+ // If tokens is provided, use it as the base
1178
+ tokensToApply = themeConfig.tokens : themeConfig.extend && (
1179
+ // If only extend is provided, use it as overrides
1180
+ tokensToApply = themeConfig.extend),
1181
+ // Apply advanced feature configurations as tokens
1182
+ config.interactiveEffects && (
1183
+ // Vortex effects
1184
+ config.interactiveEffects.vortex && (tokensToApply = {
1185
+ ...tokensToApply,
1186
+ "interactive-vortex-enabled": String(config.interactiveEffects.vortex.enabled ?? !1),
1187
+ "interactive-vortex-strength": String(config.interactiveEffects.vortex.strength ?? .5),
1188
+ "interactive-vortex-radius": String(config.interactiveEffects.vortex.radius ?? 100),
1189
+ "interactive-vortex-decay": String(config.interactiveEffects.vortex.decay ?? .8)
1190
+ }),
1191
+ // Chromatic aberration
1192
+ config.interactiveEffects.chromaticAberration && (tokensToApply = {
1193
+ ...tokensToApply,
1194
+ "interactive-chromatic-enabled": String(config.interactiveEffects.chromaticAberration.enabled ?? !1),
1195
+ "interactive-chromatic-mode": config.interactiveEffects.chromaticAberration.mode ?? "lateral",
1196
+ "interactive-chromatic-red-shift": String(config.interactiveEffects.chromaticAberration.redShift ?? .02),
1197
+ "interactive-chromatic-green-shift": String(config.interactiveEffects.chromaticAberration.greenShift ?? 0),
1198
+ "interactive-chromatic-blue-shift": String(config.interactiveEffects.chromaticAberration.blueShift ?? -.02),
1199
+ "interactive-chromatic-edge-only": String(config.interactiveEffects.chromaticAberration.edgeOnly ?? !1),
1200
+ "interactive-chromatic-edge-threshold": String(config.interactiveEffects.chromaticAberration.edgeThreshold ?? .5)
1201
+ }),
1202
+ // Mouse interaction
1203
+ config.interactiveEffects.mouseInteraction && (tokensToApply = {
1204
+ ...tokensToApply,
1205
+ "interactive-mouse-sensitivity": String(config.interactiveEffects.mouseInteraction.sensitivity ?? 1),
1206
+ "interactive-mouse-trail-effect": String(config.interactiveEffects.mouseInteraction.trailEffect ?? !1)
1207
+ }),
1208
+ // Animation speed
1209
+ config.interactiveEffects.animationSpeed && (tokensToApply = {
1210
+ ...tokensToApply,
1211
+ "interactive-animation-speed-base": String(config.interactiveEffects.animationSpeed.base ?? 1),
1212
+ "interactive-animation-speed-multiplier": String(config.interactiveEffects.animationSpeed.timeMultiplier ?? 1)
1213
+ })),
1214
+ // Apply optimization configurations as tokens
1215
+ config.optimization && (
1216
+ // Responsive breakpoints
1217
+ config.optimization.responsive && (config.optimization.responsive.breakpoints && (tokensToApply = {
1218
+ ...tokensToApply,
1219
+ "optimization-breakpoint-mobile": config.optimization.responsive.breakpoints.mobile ?? "0px",
1220
+ "optimization-breakpoint-tablet": config.optimization.responsive.breakpoints.tablet ?? "768px",
1221
+ "optimization-breakpoint-desktop": config.optimization.responsive.breakpoints.desktop ?? "1024px",
1222
+ "optimization-breakpoint-wide": config.optimization.responsive.breakpoints.wide ?? "1440px"
1223
+ }), config.optimization.responsive.deviceScaling && (tokensToApply = {
1224
+ ...tokensToApply,
1225
+ "optimization-device-scaling-mobile": String(config.optimization.responsive.deviceScaling.mobile ?? .5),
1226
+ "optimization-device-scaling-tablet": String(config.optimization.responsive.deviceScaling.tablet ?? .75),
1227
+ "optimization-device-scaling-desktop": String(config.optimization.responsive.deviceScaling.desktop ?? 1)
1228
+ })),
1229
+ // Performance settings
1230
+ config.optimization.performance && (tokensToApply = {
1231
+ ...tokensToApply,
1232
+ "optimization-performance-fps-target": String(config.optimization.performance.fpsTarget ?? 60),
1233
+ "optimization-auto-scaling-enabled": String(config.optimization.performance.autoScaling ?? !1)
1234
+ }),
1235
+ // Auto-scaling settings
1236
+ config.optimization.autoScaling && (tokensToApply = {
1237
+ ...tokensToApply,
1238
+ "optimization-auto-scaling-enabled": String(config.optimization.autoScaling.enabled ?? !1),
1239
+ "optimization-auto-scaling-low-end": String(config.optimization.autoScaling.qualityThresholds?.lowEnd ?? .5),
1240
+ "optimization-auto-scaling-mid-range": String(config.optimization.autoScaling.qualityThresholds?.midRange ?? .75),
1241
+ "optimization-auto-scaling-high-end": String(config.optimization.autoScaling.qualityThresholds?.highEnd ?? 1)
1242
+ })),
1243
+ // Apply visual polish configurations as tokens
1244
+ config.visualPolish && (config.visualPolish.borders && (tokensToApply = {
1245
+ ...tokensToApply,
1246
+ "visual-polish-border-iridescent-glow": String(config.visualPolish.borders.iridescentGlow ?? !1),
1247
+ "visual-polish-border-shimmer-effect": String(config.visualPolish.borders.shimmerEffect ?? !1),
1248
+ "visual-polish-border-beveled-edges": String(config.visualPolish.borders.beveledEdges ?? !1),
1249
+ "visual-polish-border-pulsing-glow": String(config.visualPolish.borders.pulsingGlow ?? !1)
1250
+ }), config.visualPolish.contentAwareBlur && (tokensToApply = {
1251
+ ...tokensToApply,
1252
+ "visual-polish-content-aware-blur-enabled": String(config.visualPolish.contentAwareBlur.enabled ?? !1),
1253
+ "visual-polish-content-aware-depth-detection": String(config.visualPolish.contentAwareBlur.depthDetection ?? !1),
1254
+ "visual-polish-content-aware-edge-preservation": String(config.visualPolish.contentAwareBlur.edgePreservation ?? !1),
1255
+ "visual-polish-content-aware-variable-radius": String(config.visualPolish.contentAwareBlur.variableRadius ?? !1)
1256
+ }), config.visualPolish.holographicEffects && (tokensToApply = {
1257
+ ...tokensToApply,
1258
+ "visual-polish-holographic-enabled": String(config.visualPolish.holographicEffects.enabled ?? !1),
1259
+ "visual-polish-holographic-rainbow-diffraction": String(config.visualPolish.holographicEffects.rainbowDiffraction ?? !1),
1260
+ "visual-polish-holographic-scanline-animation": String(config.visualPolish.holographicEffects.scanlineAnimation ?? !1),
1261
+ "visual-polish-holographic-grid-overlay": String(config.visualPolish.holographicEffects.gridOverlay ?? !1),
1262
+ "visual-polish-holographic-data-stream": String(config.visualPolish.holographicEffects.dataStream ?? !1),
1263
+ "visual-polish-holographic-pulse-rings": String(config.visualPolish.holographicEffects.pulseRings ?? !1)
1264
+ })), createTokens(tokensToApply);
1265
+ }();
1266
+ // Get prefix from config
1267
+ try {
1268
+ // Use the imported function directly instead of require to avoid bundling issues
1269
+ const config = function(options = {}) {
1270
+ const {configPath: configPath, required: required = !1} = options, defaultConfig = {
1271
+ prefix: "atomix",
1272
+ theme: {
1273
+ extend: {}
1274
+ }
1275
+ };
1276
+ // Default config
1277
+ // In browser environments, config loading is not supported
1278
+ if ("undefined" != typeof window) {
1279
+ if (required) throw new Error('Config loading requires Node.js file system access.\n\nSolutions:\n1. Provide tokens explicitly to createTheme():\n const css = createTheme({ "--brand-primary": "#6366f1" });\n\n2. Use SSR framework (Next.js, Remix, Astro)\n\n3. Load config on server and pass to client\n\nSee examples/config-examples/browser-only.config.ts');
1280
+ return defaultConfig;
1281
+ }
1282
+ // If a specific config path is provided, try to load it directly
1283
+ if (configPath) return loadConfigAtPath(configPath, required, defaultConfig);
1284
+ // Otherwise, try standard locations in order of preference
1285
+ const possiblePaths = [ "atomix.config.ts", "atomix.config.js", "atomix.config.json" ];
1286
+ for (const path of possiblePaths) {
1287
+ const config = loadConfigAtPath(path, !1, defaultConfig);
1288
+ // If we found a valid config, return it
1289
+ if (JSON.stringify(config) !== JSON.stringify(defaultConfig)) return config;
1290
+ }
1291
+ // If no config file was found or all contained only defaults, return default config
1292
+ if (required) throw new Error('No Atomix configuration file found in project root.\n\nExpected one of:\n - atomix.config.ts (recommended)\n - atomix.config.js\n - atomix.config.json\n\nQuick Fix:\n1. Create a config file in your project root:\n touch atomix.config.ts\n\n2. Add basic configuration:\n import { defineConfig } from "@shohojdhara/atomix/config";\n export default defineConfig({ prefix: "myapp" });\n\n3. Or copy an example:\n cp node_modules/@shohojdhara/atomix/examples/config-examples/standard.config.ts ./atomix.config.ts');
1293
+ return defaultConfig;
1294
+ }({
1295
+ configPath: "atomix.config.ts",
1296
+ required: !0
1297
+ });
1298
+ configPrefix = config?.prefix;
1299
+ } catch (error) {
1300
+ // Prefix loading failed, but tokens were loaded, so continue
1301
+ }
1302
+ tokens = configTokens;
1303
+ }
1304
+ // Merge with defaults and generate CSS
1305
+ const allTokens = createTokens(tokens), prefix = options?.prefix ?? configPrefix ?? "atomix";
1306
+ // Get prefix from options, config, or use default
1307
+ return generateCSSVariables$1(allTokens, {
1308
+ ...options,
1309
+ prefix: prefix
1310
+ });
1311
+ }
1312
+
1313
+ /**
1314
+ * Theme Composition Utilities
1315
+ *
1316
+ * Simplified utilities for composing and merging DesignTokens.
1317
+ */
1318
+ // ============================================================================
1319
+ // Deep Merge Utility
1320
+ // ============================================================================
1321
+ /**
1322
+ * Check if value is an object
1323
+ */ function isObject(item) {
1324
+ return item && "object" == typeof item && !Array.isArray(item) && "function" != typeof item;
1325
+ }
1326
+
1327
+ /**
1328
+ * Deep merge multiple objects
1329
+ * Later objects override earlier ones
1330
+ */ function deepMerge(...objects) {
1331
+ if (0 === objects.length) return {};
1332
+ if (1 === objects.length) return objects[0];
1333
+ const [target, ...sources] = objects, result = {
1334
+ ...target
1335
+ };
1336
+ for (const source of sources) if (source) for (const key in source) {
1337
+ if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
1338
+ const targetValue = result[key], sourceValue = source[key];
1339
+ isObject(targetValue) && isObject(sourceValue) ?
1340
+ // Recursively merge objects
1341
+ result[key] = deepMerge(targetValue, sourceValue) :
1342
+ // Override with source value
1343
+ result[key] = sourceValue;
1344
+ }
1345
+ return result;
1346
+ }
1347
+
1348
+ /**
1349
+ * Merge multiple DesignTokens objects into a single DesignTokens object
1350
+ *
1351
+ * @param tokens - DesignTokens objects to merge
1352
+ * @returns Merged DesignTokens object
1353
+ *
1354
+ * @example
1355
+ * ```typescript
1356
+ * const baseTokens = { 'primary': '#000', 'spacing-4': '1rem' };
1357
+ * const customTokens = { 'secondary': '#fff', 'spacing-4': '1.5rem' };
1358
+ * const merged = mergeTheme(baseTokens, customTokens);
1359
+ * // Returns: { 'primary': '#000', 'secondary': '#fff', 'spacing-4': '1.5rem' }
1360
+ * ```
1361
+ */ function mergeTheme(...tokens) {
1362
+ return deepMerge({}, ...tokens);
1363
+ }
1364
+
1365
+ /**
1366
+ * Extend DesignTokens with additional tokens
1367
+ *
1368
+ * @param baseTokens - Base DesignTokens to extend
1369
+ * @param extension - Additional DesignTokens to merge
1370
+ * @returns Extended DesignTokens object
1371
+ *
1372
+ * @example
1373
+ * ```typescript
1374
+ * const base = { 'primary': '#000' };
1375
+ * const extended = extendTheme(base, { 'secondary': '#fff' });
1376
+ * // Returns: { 'primary': '#000', 'secondary': '#fff' }
1377
+ * ```
1378
+ */ function extendTheme(baseTokens, extension) {
1379
+ return mergeTheme(baseTokens, extension);
1380
+ }
1381
+
1382
+ /**
1383
+ * Create a new theme registry
1384
+ */ function createThemeRegistry() {
1385
+ return {};
1386
+ }
1387
+
1388
+ /**
1389
+ * Register a theme
1390
+ * @param registry - Theme registry object
1391
+ * @param id - Theme identifier
1392
+ * @param metadata - Theme metadata
1393
+ */ function registerTheme(registry, id, metadata) {
1394
+ registry[id] = metadata;
1395
+ }
1396
+
1397
+ /**
1398
+ * Unregister a theme
1399
+ * @param registry - Theme registry object
1400
+ * @param id - Theme identifier
1401
+ */ function unregisterTheme(registry, id) {
1402
+ const exists = id in registry;
1403
+ return delete registry[id], exists;
1404
+ }
1405
+
1406
+ /**
1407
+ * Check if a theme is registered
1408
+ * @param registry - Theme registry object
1409
+ * @param id - Theme identifier
1410
+ */ function hasTheme(registry, id) {
1411
+ return id in registry;
1412
+ }
1413
+
1414
+ /**
1415
+ * Get theme metadata
1416
+ * @param registry - Theme registry object
1417
+ * @param id - Theme identifier
1418
+ */ function getTheme(registry, id) {
1419
+ return registry[id];
1420
+ }
1421
+
1422
+ /**
1423
+ * Get all registered theme metadata
1424
+ * @param registry - Theme registry object
1425
+ */ function getAllThemes(registry) {
1426
+ return Object.values(registry);
1427
+ }
1428
+
1429
+ /**
1430
+ * Get all registered theme IDs
1431
+ * @param registry - Theme registry object
1432
+ */ function getThemeIds(registry) {
1433
+ return Object.keys(registry);
1434
+ }
1435
+
1436
+ /**
1437
+ * Clear all registered themes
1438
+ * @param registry - Theme registry object
1439
+ */ function clearThemes(registry) {
1440
+ Object.keys(registry).forEach((key => delete registry[key]));
1441
+ }
1442
+
1443
+ /**
1444
+ * Get the number of registered themes
1445
+ * @param registry - Theme registry object
1446
+ */ function getThemeCount(registry) {
1447
+ return Object.keys(registry).length;
1448
+ }
1449
+
1450
+ /**
1451
+ * CSS Injection Utilities
1452
+ *
1453
+ * Inject CSS into HTML head via <style> element.
1454
+ */
1455
+ /**
1456
+ * Check if running in browser environment
1457
+ */ function isBrowser$1() {
1458
+ return "undefined" != typeof document;
1459
+ }
1460
+
1461
+ /**
1462
+ * Inject CSS into HTML head via <style> element
1463
+ *
1464
+ * Creates or updates a style element in the document head.
1465
+ * If an element with the same ID exists, it will be updated.
1466
+ *
1467
+ * @param css - CSS string to inject
1468
+ * @param id - Style element ID (default: 'atomix-theme')
1469
+ *
1470
+ * @example
1471
+ * ```typescript
1472
+ * const css = ':root { --atomix-color-primary: #7AFFD7; }';
1473
+ * injectCSS(css);
1474
+ *
1475
+ * // With custom ID
1476
+ * injectCSS(css, 'my-custom-theme');
1477
+ * ```
1478
+ */ function injectCSS$1(css, id = "atomix-theme") {
1479
+ if (!isBrowser$1()) return;
1480
+ let styleElement = document.getElementById(id);
1481
+ styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
1482
+ styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
1483
+ styleElement.textContent = css;
1484
+ }
1485
+
1486
+ /**
1487
+ * Remove injected CSS from DOM
1488
+ *
1489
+ * Removes the style element with the given ID from the document head.
1490
+ *
1491
+ * @param id - Style element ID to remove (default: 'atomix-theme')
1492
+ *
1493
+ * @example
1494
+ * ```typescript
1495
+ * removeCSS(); // Removes default 'atomix-theme'
1496
+ * removeCSS('my-custom-theme'); // Removes custom ID
1497
+ * ```
1498
+ */ function removeCSS(id = "atomix-theme") {
1499
+ if (!isBrowser$1()) return;
1500
+ const styleElement = document.getElementById(id);
1501
+ styleElement && styleElement.remove();
1502
+ }
1503
+
1504
+ /**
1505
+ * Check if CSS is already injected
1506
+ *
1507
+ * @param id - Style element ID to check (default: 'atomix-theme')
1508
+ * @returns True if style element exists
1509
+ */ function isCSSInjected(id = "atomix-theme") {
1510
+ return !!isBrowser$1() && null !== document.getElementById(id);
1511
+ }
1512
+
1513
+ /**
1514
+ * Theme System Constants
1515
+ *
1516
+ * Centralized constants for the theme system to avoid magic numbers and strings.
1517
+ */
1518
+ /**
1519
+ * Default storage key for theme persistence
1520
+ */
1521
+ /**
1522
+ * Theme System Error Handling
1523
+ *
1524
+ * Centralized error handling for the Atomix theme system.
1525
+ * Provides custom error classes and logging utilities.
1526
+ */
1527
+ /**
1528
+ * Theme error codes
1529
+ */
1530
+ var ThemeErrorCode, LogLevel;
1531
+
1532
+ "undefined" != typeof process && process.env, function(ThemeErrorCode) {
1533
+ /** Theme not found in registry */
1534
+ ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
1535
+ /** Theme failed to load */
1536
+ ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
1537
+ /** Theme validation failed */
1538
+ ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
1539
+ /** Configuration loading failed */
1540
+ ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
1541
+ /** Configuration validation failed */
1542
+ ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
1543
+ /** Circular dependency detected */
1544
+ ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
1545
+ /** Missing dependency */
1546
+ ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
1547
+ /** Storage operation failed */
1548
+ ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
1549
+ /** Invalid theme name */
1550
+ ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
1551
+ /** CSS injection failed */
1552
+ ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
1553
+ /** Invalid color format */
1554
+ ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
1555
+ /** Missing required token */
1556
+ ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
1557
+ /** Accessibility contrast violation */
1558
+ ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
1559
+ /** Invalid token type */
1560
+ ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
1561
+ /** Unknown error */
1562
+ ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
1563
+ }(ThemeErrorCode || (ThemeErrorCode = {}));
1564
+
1565
+ /**
1566
+ * Custom error class for theme-related errors
1567
+ */
1568
+ class ThemeError extends Error {
1569
+ constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
1570
+ super(message), this.name = "ThemeError", this.code = code, this.context = context,
1571
+ this.timestamp = Date.now(),
1572
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
1573
+ Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
1574
+ }
1575
+ /**
1576
+ * Convert error to JSON for logging
1577
+ */ toJSON() {
1578
+ return {
1579
+ name: this.name,
1580
+ message: this.message,
1581
+ code: this.code,
1582
+ context: this.context,
1583
+ timestamp: this.timestamp,
1584
+ stack: this.stack
1585
+ };
1586
+ }
1587
+ }
1588
+
1589
+ /**
1590
+ * Log level
1591
+ */ !function(LogLevel) {
1592
+ LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
1593
+ LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
1594
+ }(LogLevel || (LogLevel = {}));
1595
+
1596
+ /**
1597
+ * Theme Logger
1598
+ *
1599
+ * Centralized logging for the theme system.
1600
+ * Replaces console statements with structured logging.
1601
+ */
1602
+ class ThemeLogger {
1603
+ constructor(config = {}) {
1604
+ this.config = {
1605
+ level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
1606
+ enableConsole: config.enableConsole ?? !0,
1607
+ onError: config.onError,
1608
+ onWarn: config.onWarn,
1609
+ onInfo: config.onInfo,
1610
+ onDebug: config.onDebug
1611
+ };
1612
+ }
1613
+ /**
1614
+ * Log an error
1615
+ */ error(message, error, context) {
1616
+ if (this.config.level < LogLevel.ERROR) return;
1617
+ const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
1618
+ this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
1619
+ error: errorObj,
1620
+ context: {
1621
+ ...context,
1622
+ ...themeError.context
1623
+ },
1624
+ code: themeError.code
1625
+ }), this.config.onError?.(themeError, context);
1626
+ }
1627
+ /**
1628
+ * Log a warning
1629
+ */ warn(message, context) {
1630
+ this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
1631
+ this.config.onWarn?.(message, context));
1632
+ }
1633
+ /**
1634
+ * Log an info message
1635
+ */ info(message, context) {
1636
+ this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
1637
+ this.config.onInfo?.(message, context));
1638
+ }
1639
+ /**
1640
+ * Log a debug message
1641
+ */ debug(message, context) {
1642
+ this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
1643
+ }
1644
+ }
1645
+
1646
+ /**
1647
+ * Default logger instance
1648
+ */ let defaultLogger = null;
1649
+
1650
+ /**
1651
+ * Get or create default logger
1652
+ */ function getLogger() {
1653
+ return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
1654
+ }
1163
1655
 
1164
1656
  /**
1165
1657
  * Check if code is running in a browser environment
1166
- */
1167
- const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
1658
+ */ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
1168
1659
  , buildThemePath = (themeName, basePath = "/themes", useMinified = !1, cdnPath = null) => {
1169
1660
  // Validate theme name to prevent path injection
1170
1661
  if (!isValidThemeName(themeName)) throw new ThemeError(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`, ThemeErrorCode.INVALID_THEME_NAME, {
@@ -1248,107 +1739,481 @@ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof do
1248
1739
 
1249
1740
  /**
1250
1741
  * Check if code is running on the server (SSR)
1251
- */
1742
+ */ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
1743
+ return function(that, callbackfn, argumentsLength, memo) {
1744
+ var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
1745
+ if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
1746
+ var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
1747
+ if (argumentsLength < 2) for (;;) {
1748
+ if (index in self) {
1749
+ memo = self[index], index += i;
1750
+ break;
1751
+ }
1752
+ if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
1753
+ }
1754
+ for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
1755
+ return memo;
1756
+ };
1757
+ }, arrayReduce = {
1758
+ // `Array.prototype.reduce` method
1759
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
1760
+ left: createMethod(!1),
1761
+ // `Array.prototype.reduceRight` method
1762
+ // https://tc39.es/ecma262/#sec-array.prototype.reduceright
1763
+ right: createMethod(!0)
1764
+ }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
1765
+ return userAgent.slice(0, string.length) === string;
1766
+ }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
1767
+
1768
+ // `Array.prototype.reduce` method
1769
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
1770
+ _export({
1771
+ target: "Array",
1772
+ proto: !0,
1773
+ forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
1774
+ var method = [][METHOD_NAME];
1775
+ return !!method && fails((function() {
1776
+ // eslint-disable-next-line no-useless-call -- required for testing
1777
+ method.call(null, argument || function() {
1778
+ return 1;
1779
+ }, 1);
1780
+ }));
1781
+ }("reduce")
1782
+ }, {
1783
+ reduce: function(callbackfn /* , initialValue */) {
1784
+ var length = arguments.length;
1785
+ return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
1786
+ }
1787
+ });
1788
+
1789
+ var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype;
1790
+
1791
+ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
1792
+ var own = it.reduce;
1793
+ return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
1794
+ }));
1795
+
1796
+ // ============================================================================
1797
+ // Theme Mode Switching
1798
+ // ============================================================================
1252
1799
  /**
1253
- * Theme Utilities
1800
+ * Switch between light and dark themes
1254
1801
  *
1255
- * Helper utilities for working with themes, including color manipulation,
1256
- * spacing helpers, and theme value accessors.
1257
- */
1802
+ * Automatically toggles a class on the root element and persists the choice.
1803
+ *
1804
+ * @param mode - Theme mode ('light', 'dark', or 'system')
1805
+ * @param options - Configuration options
1806
+ *
1807
+ * @example
1808
+ * ```typescript
1809
+ * import { switchTheme } from '@shohojdhara/atomix/theme/utils';
1810
+ *
1811
+ * // Switch to dark mode
1812
+ * switchTheme('dark');
1813
+ *
1814
+ * // Toggle between light/dark
1815
+ * const current = getCurrentTheme();
1816
+ * switchTheme(current === 'dark' ? 'light' : 'dark');
1817
+ * ```
1818
+ */ function switchTheme(mode, options = {}) {
1819
+ const {selector: selector = ":root", storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, resolvedMode = "system" === mode ? getSystemTheme() : mode, root = ":root" === selector ? document.documentElement : document.querySelector(selector);
1820
+ // Determine actual mode (resolve 'system')
1821
+ if (root) {
1822
+ // Add transition class if enabled
1823
+ if (enableTransition) {
1824
+ const htmlRoot = root;
1825
+ htmlRoot.style.transition = `all ${transitionDuration}ms ease-in-out`,
1826
+ // Remove transition after it completes
1827
+ setTimeout((() => {
1828
+ htmlRoot.style.transition = "";
1829
+ }), transitionDuration);
1830
+ }
1831
+ // Apply theme class
1832
+ root.classList.remove("atomix-theme-light", "atomix-theme-dark"), root.classList.add(`atomix-theme-${resolvedMode}`),
1833
+ // Set data attribute for CSS selectors
1834
+ root.setAttribute("data-theme", resolvedMode),
1835
+ // Persist choice
1836
+ persistTheme(resolvedMode, {
1837
+ storageKey: storageKey
1838
+ }),
1839
+ // Dispatch custom event for listeners
1840
+ window.dispatchEvent(new CustomEvent("atomix-theme-change", {
1841
+ detail: {
1842
+ mode: resolvedMode
1843
+ }
1844
+ }));
1845
+ }
1846
+ }
1847
+
1848
+ /**
1849
+ * Toggle between light and dark themes
1850
+ *
1851
+ * @param options - Configuration options
1852
+ * @returns The new theme mode
1853
+ *
1854
+ * @example
1855
+ * ```typescript
1856
+ * const newMode = toggleTheme();
1857
+ * console.log('Switched to:', newMode);
1858
+ * ```
1859
+ */ function toggleTheme(options = {}) {
1860
+ const next = "dark" === getCurrentTheme(options.storageKey) ? "light" : "dark";
1861
+ return switchTheme(next, options), next;
1862
+ }
1863
+
1864
+ /**
1865
+ * Get current theme mode
1866
+ *
1867
+ * @param storageKey - Storage key (default: 'atomix-theme')
1868
+ * @returns Current theme mode or 'light' if not set
1869
+ */ function getCurrentTheme(storageKey = "atomix-theme") {
1870
+ return "undefined" == typeof window ? "light" : localStorage.getItem(storageKey) || "light";
1871
+ }
1872
+
1873
+ /**
1874
+ * Get system theme preference
1875
+ *
1876
+ * @returns 'dark' if system prefers dark mode, 'light' otherwise
1877
+ */ function getSystemTheme() {
1878
+ return "undefined" == typeof window ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1879
+ }
1880
+
1881
+ /**
1882
+ * Initialize theme based on saved preference or system preference
1883
+ *
1884
+ * Call this once at app startup.
1885
+ *
1886
+ * @param options - Configuration options
1887
+ * @returns The initialized theme mode
1888
+ *
1889
+ * @example
1890
+ * ```typescript
1891
+ * // In your app entry point
1892
+ * import { initializeTheme } from '@shohojdhara/atomix/theme/utils';
1893
+ *
1894
+ * const theme = initializeTheme();
1895
+ * console.log('Theme initialized:', theme);
1896
+ * ```
1897
+ */ function initializeTheme(options = {}) {
1898
+ const saved = getCurrentTheme(options.storageKey);
1899
+ // If no saved preference, use system preference
1900
+ if (!saved || "system" === saved) {
1901
+ const system = getSystemTheme();
1902
+ return switchTheme(system, options), system;
1903
+ }
1904
+ // Use saved preference
1905
+ return switchTheme(saved, options), saved;
1906
+ }
1907
+
1908
+ /**
1909
+ * Listen for system theme changes
1910
+ *
1911
+ * @param callback - Function to call when system theme changes
1912
+ * @returns Cleanup function to stop listening
1913
+ *
1914
+ * @example
1915
+ * ```typescript
1916
+ * const cleanup = listenToSystemTheme((mode) => {
1917
+ * console.log('System theme changed to:', mode);
1918
+ * switchTheme(mode);
1919
+ * });
1920
+ *
1921
+ * // Later, when component unmounts
1922
+ * cleanup();
1923
+ * ```
1924
+ */ function listenToSystemTheme(callback) {
1925
+ if ("undefined" == typeof window) return () => {};
1926
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"), handler = e => {
1927
+ callback(e.matches ? "dark" : "light");
1928
+ };
1929
+ // Modern browsers
1930
+ return mediaQuery.addEventListener ? (mediaQuery.addEventListener("change", handler),
1931
+ () => mediaQuery.removeEventListener("change", handler)) : (
1932
+ // Fallback for older browsers
1933
+ mediaQuery.addListener(handler), () => mediaQuery.removeListener(handler));
1934
+ }
1935
+
1258
1936
  // ============================================================================
1259
- // Color Manipulation Utilities
1937
+ // Theme Persistence
1260
1938
  // ============================================================================
1261
1939
  /**
1262
- * Convert hex color to RGB object
1263
- */
1264
- function hexToRgb$1(hex) {
1265
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1266
- return result ? {
1267
- r: parseInt(result[1], 16),
1268
- g: parseInt(result[2], 16),
1269
- b: parseInt(result[3], 16)
1270
- } : null;
1940
+ * Save theme preference to storage
1941
+ *
1942
+ * @param mode - Theme mode to save
1943
+ * @param options - Persistence options
1944
+ */ function persistTheme(mode, options = {}) {
1945
+ if ("undefined" == typeof window) return;
1946
+ const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
1947
+ ("localStorage" === storageType ? localStorage : sessionStorage).setItem(storageKey, mode);
1948
+ }
1949
+
1950
+ /**
1951
+ * Clear saved theme preference
1952
+ *
1953
+ * @param options - Persistence options
1954
+ */ function clearThemePreference(options = {}) {
1955
+ if ("undefined" == typeof window) return;
1956
+ const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
1957
+ ("localStorage" === storageType ? localStorage : sessionStorage).removeItem(storageKey);
1958
+ }
1959
+
1960
+ // ============================================================================
1961
+ // Theme Tokens Manipulation
1962
+ // ============================================================================
1963
+ /**
1964
+ * Merge multiple token sets
1965
+ *
1966
+ * Deep merges token objects, with later tokens overriding earlier ones.
1967
+ *
1968
+ * @param tokens - Token objects to merge
1969
+ * @returns Merged tokens
1970
+ *
1971
+ * @example
1972
+ * ```typescript
1973
+ * const merged = mergeTokens(
1974
+ * baseTokens,
1975
+ * { colors: { primary: { main: '#custom' } } }
1976
+ * );
1977
+ * ```
1978
+ */ function mergeTokens(...tokens) {
1979
+ return _reduceInstanceProperty(tokens).call(tokens, ((acc, current) => deepMerge(acc, current)), {});
1980
+ }
1981
+
1982
+ /**
1983
+ * Override specific tokens
1984
+ *
1985
+ * Creates a new token object with specific overrides.
1986
+ *
1987
+ * @param base - Base tokens
1988
+ * @param overrides - Tokens to override
1989
+ * @returns New tokens with overrides applied
1990
+ *
1991
+ * @example
1992
+ * ```typescript
1993
+ * const customized = overrideTokens(defaultTokens, {
1994
+ * colors: {
1995
+ * primary: { main: '#ff0000' }
1996
+ * }
1997
+ * });
1998
+ * ```
1999
+ */ function overrideTokens(base, overrides) {
2000
+ return deepMerge({
2001
+ ...base
2002
+ }, overrides);
2003
+ }
2004
+
2005
+ /**
2006
+ * Pick specific token categories
2007
+ *
2008
+ * Extracts only the specified categories from tokens.
2009
+ *
2010
+ * @param tokens - Source tokens
2011
+ * @param categories - Categories to pick
2012
+ * @returns Tokens with only selected categories
2013
+ *
2014
+ * @example
2015
+ * ```typescript
2016
+ * const colorTokens = pickTokens(allTokens, ['colors']);
2017
+ * ```
2018
+ */ function pickTokens(tokens, categories) {
2019
+ const result = {};
2020
+ return categories.forEach((category => {
2021
+ tokens[category] && (result[category] = tokens[category]);
2022
+ })), result;
2023
+ }
2024
+
2025
+ /**
2026
+ * Omit specific token categories
2027
+ *
2028
+ * Removes specified categories from tokens.
2029
+ *
2030
+ * @param tokens - Source tokens
2031
+ * @param categories - Categories to omit
2032
+ * @returns Tokens without omitted categories
2033
+ *
2034
+ * @example
2035
+ * ```typescript
2036
+ * const withoutColors = omitTokens(allTokens, ['colors']);
2037
+ * ```
2038
+ */ function omitTokens(tokens, categories) {
2039
+ const result = {
2040
+ ...tokens
2041
+ };
2042
+ return categories.forEach((category => {
2043
+ delete result[category];
2044
+ })), result;
2045
+ }
2046
+
2047
+ // ============================================================================
2048
+ // Color Utilities
2049
+ // ============================================================================
2050
+ /**
2051
+ * Convert hex color to RGB
2052
+ *
2053
+ * @param hex - Hex color (with or without #)
2054
+ * @returns RGB object { r, g, b }
2055
+ */ function hexToRgb$1(hex) {
2056
+ // Validate
2057
+ if (
2058
+ // Remove # if present
2059
+ // Handle shorthand hex
2060
+ 3 === (hex = hex.replace(/^#/, "")).length && (hex = hex.split("").map((c => c + c)).join("")),
2061
+ 6 !== hex.length) return null;
2062
+ const num = parseInt(hex, 16);
2063
+ return {
2064
+ r: num >> 16 & 255,
2065
+ g: num >> 8 & 255,
2066
+ b: 255 & num
2067
+ };
1271
2068
  }
1272
2069
 
1273
2070
  /**
1274
- * Convert RGB to hex color
2071
+ * Convert RGB to hex
2072
+ *
2073
+ * @param r - Red (0-255)
2074
+ * @param g - Green (0-255)
2075
+ * @param b - Blue (0-255)
2076
+ * @returns Hex color with #
1275
2077
  */ function rgbToHex(r, g, b) {
1276
- const toHex = val => Math.round(Math.max(0, Math.min(255, val))).toString(16).padStart(2, "0");
1277
- return `#${toHex(r ?? 0)}${toHex(g ?? 0)}${toHex(b ?? 0)}`;
2078
+ return "#" + [ r, g, b ].map((x => {
2079
+ const hex = x.toString(16);
2080
+ return 1 === hex.length ? "0" + hex : hex;
2081
+ })).join("");
1278
2082
  }
1279
2083
 
1280
2084
  /**
1281
- * Calculate relative luminance of a color
1282
- * Used for determining contrast ratios
1283
- */ function getLuminance(color) {
1284
- const rgb = hexToRgb$1(color);
2085
+ * Calculate luminance of a color
2086
+ *
2087
+ * Used for determining contrast ratios.
2088
+ *
2089
+ * @param hex - Hex color
2090
+ * @returns Luminance value (0-1)
2091
+ */ function getLuminance(hex) {
2092
+ const rgb = hexToRgb$1(hex);
1285
2093
  if (!rgb) return 0;
1286
- const {r: r, g: g, b: b} = rgb, [rs, gs, bs] = [ r ?? 0, g ?? 0, b ?? 0 ].map((c => {
1287
- const val = c / 255;
1288
- return val <= .03928 ? val / 12.92 : Math.pow((val + .055) / 1.055, 2.4);
1289
- }));
1290
- return .2126 * (rs ?? 0) + .7152 * (gs ?? 0) + .0722 * (bs ?? 0);
2094
+ const [r, g, b] = [ rgb.r, rgb.g, rgb.b ].map((v => (v /= 255) <= .03928 ? v / 12.92 : Math.pow((v + .055) / 1.055, 2.4)));
2095
+ return .2126 * (r ?? 0) + .7152 * (g ?? 0) + .0722 * (b ?? 0);
1291
2096
  }
1292
2097
 
1293
2098
  /**
1294
2099
  * Calculate contrast ratio between two colors
1295
- */ function getContrastRatio(foreground, background) {
1296
- const lumA = getLuminance(foreground), lumB = getLuminance(background);
1297
- return (Math.max(lumA, lumB) + .05) / (Math.min(lumA, lumB) + .05);
2100
+ *
2101
+ * @param hex1 - First hex color
2102
+ * @param hex2 - Second hex color
2103
+ * @returns Contrast ratio (1-21)
2104
+ */ function getContrastRatio(hex1, hex2) {
2105
+ const lum1 = getLuminance(hex1), lum2 = getLuminance(hex2);
2106
+ return (Math.max(lum1, lum2) + .05) / (Math.min(lum1, lum2) + .05);
2107
+ }
2108
+
2109
+ /**
2110
+ * Check if text color passes WCAG AA standard
2111
+ *
2112
+ * @param textColor - Text color hex
2113
+ * @param backgroundColor - Background color hex
2114
+ * @param size - Font size ('small' or 'large')
2115
+ * @returns true if passes WCAG AA
2116
+ */ function isAccessible(textColor, backgroundColor, size = "small") {
2117
+ return getContrastRatio(textColor, backgroundColor) >= ("large" === size ? 3 : 4.5);
1298
2118
  }
1299
2119
 
1300
2120
  /**
1301
- * Get appropriate contrast text color (black or white) for a background color
1302
- */ function getContrastText(background, threshold = 3) {
1303
- const contrastWithWhite = getContrastRatio("#FFFFFF", background), contrastWithBlack = getContrastRatio("#000000", background);
1304
- return contrastWithWhite >= threshold ? "#FFFFFF" : contrastWithBlack >= threshold ? "#000000" : contrastWithWhite > contrastWithBlack ? "#FFFFFF" : "#000000";
2121
+ * Get appropriate text color (black or white) for a background
2122
+ *
2123
+ * @param backgroundColor - Background hex color
2124
+ * @param threshold - Contrast threshold (default: 3)
2125
+ * @returns '#000000' or '#FFFFFF'
2126
+ */ function getContrastText(backgroundColor, threshold = 3) {
2127
+ return getContrastRatio(backgroundColor, "#FFFFFF") >= threshold ? "#FFFFFF" : "#000000";
1305
2128
  }
1306
2129
 
1307
2130
  /**
1308
- * Lighten a color by a given amount
2131
+ * Lighten a color
1309
2132
  *
1310
- * @param color - Hex color string
1311
- * @param amount - Amount to lighten (0-1), default 0.2
2133
+ * @param hex - Base hex color
2134
+ * @param amount - Amount to lighten (0-1)
1312
2135
  * @returns Lightened hex color
1313
- */ function lighten(color, amount = .2) {
1314
- const rgb = hexToRgb$1(color);
1315
- if (!rgb) return color;
1316
- const {r: r, g: g, b: b} = rgb, lightenValue = val => Math.min(255, Math.round(val + (255 - val) * amount));
1317
- return rgbToHex(lightenValue(r), lightenValue(g), lightenValue(b));
2136
+ */ function lighten(hex, amount = 0) {
2137
+ const rgb = hexToRgb$1(hex);
2138
+ if (!rgb) return hex;
2139
+ // Use amount directly as factor (0-1)
2140
+ const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r + (255 - rgb.r) * factor), g = Math.round(rgb.g + (255 - rgb.g) * factor), b = Math.round(rgb.b + (255 - rgb.b) * factor);
2141
+ return rgbToHex(Math.min(255, r), Math.min(255, g), Math.min(255, b));
1318
2142
  }
1319
2143
 
1320
2144
  /**
1321
- * Darken a color by a given amount
2145
+ * Darken a color
1322
2146
  *
1323
- * @param color - Hex color string
1324
- * @param amount - Amount to darken (0-1), default 0.2
2147
+ * @param hex - Base hex color
2148
+ * @param amount - Amount to darken (0-1)
1325
2149
  * @returns Darkened hex color
1326
- */ function darken(color, amount = .2) {
1327
- const rgb = hexToRgb$1(color);
1328
- if (!rgb) return color;
1329
- const {r: r, g: g, b: b} = rgb, darkenValue = val => Math.max(0, Math.round(val * (1 - amount)));
1330
- return rgbToHex(darkenValue(r), darkenValue(g), darkenValue(b));
2150
+ */ function darken(hex, amount = 0) {
2151
+ const rgb = hexToRgb$1(hex);
2152
+ if (!rgb) return hex;
2153
+ // Use amount directly as factor (0-1)
2154
+ const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r * (1 - factor)), g = Math.round(rgb.g * (1 - factor)), b = Math.round(rgb.b * (1 - factor));
2155
+ return rgbToHex(Math.max(0, r), Math.max(0, g), Math.max(0, b));
1331
2156
  }
1332
2157
 
1333
2158
  /**
1334
- * Add alpha (opacity) to a color
2159
+ * Add alpha to a color
1335
2160
  *
1336
- * @param color - Hex color string
2161
+ * @param hex - Hex color
1337
2162
  * @param opacity - Opacity value (0-1)
1338
2163
  * @returns RGBA color string
1339
- */ function alpha(color, opacity) {
1340
- const rgb = hexToRgb$1(color);
1341
- if (!rgb) return color;
1342
- const {r: r, g: g, b: b} = rgb;
1343
- return `rgba(${r}, ${g}, ${b}, ${Math.max(0, Math.min(1, opacity))})`;
2164
+ */ function alpha(hex, opacity) {
2165
+ const rgb = hexToRgb$1(hex);
2166
+ if (!rgb) return hex;
2167
+ const validOpacity = Math.max(0, Math.min(1, opacity));
2168
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${validOpacity})`;
1344
2169
  }
1345
2170
 
1346
2171
  /**
1347
2172
  * Emphasize a color (lighten if dark, darken if light)
1348
2173
  *
1349
- * @param color - Hex color string
1350
- * @param coefficient - Amount to emphasize (0-1), default 0.15
2174
+ * @param hex - Hex color
2175
+ * @param amount - Amount to emphasize (0-1)
1351
2176
  * @returns Emphasized hex color
2177
+ */ function emphasize(hex, amount = .15) {
2178
+ return getLuminance(hex) > .5 ? darken(hex, amount) : lighten(hex, amount);
2179
+ }
2180
+
2181
+ /**
2182
+ * Create a spacing utility
2183
+ *
2184
+ * @param spacingInput - Spacing configuration
2185
+ * @returns Spacing function
2186
+ */ function createSpacing(spacingInput = 4) {
2187
+ return (...values) => 0 === values.length ? "0px" : "function" == typeof spacingInput ? spacingInput(...values) : values.map((value => {
2188
+ if ("number" == typeof spacingInput) return value * spacingInput + "px";
2189
+ if (Array.isArray(spacingInput)) {
2190
+ const scaled = spacingInput[value];
2191
+ return "number" == typeof scaled ? `${scaled}px` : `${value}px`;
2192
+ }
2193
+ return `${value}px`;
2194
+ })).join(" ");
2195
+ }
2196
+
2197
+ /**
2198
+ * CSS Variable Generator
2199
+ *
2200
+ * Generates CSS custom properties from theme objects and injects them into the DOM.
2201
+ *
2202
+ * **Token Naming Alignment:**
2203
+ * This generator produces CSS variables that match the SCSS token naming pattern exactly:
2204
+ * - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
2205
+ * - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
2206
+ * - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
2207
+ * - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
2208
+ *
2209
+ * All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
2210
+ * This ensures compatibility between SCSS themes and JavaScript themes.
2211
+ *
2212
+ * @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
2213
+ */
2214
+ /**
2215
+ * Convert a nested object to flat CSS variable declarations
2216
+ * Uses iterative approach for better performance with large objects
1352
2217
  */
1353
2218
  /**
1354
2219
  * Generate a color scale from a base color (1-10 steps)
@@ -1416,18 +2281,7 @@ function generateCSSVariables(theme, options = {}) {
1416
2281
  color.dark && (vars[`${prefix}-${key}-hover`] = color.dark),
1417
2282
  // Generate semantic color variants (matches SCSS patterns)
1418
2283
  // Text emphasis: emphasized version of the color for text (--atomix-primary-text-emphasis)
1419
- vars[`${prefix}-${key}-text-emphasis`] = function(color, coefficient = .15) {
1420
- return getLuminance(color) > .5 ? darken(color, coefficient) : lighten(color, coefficient);
1421
- }
1422
- // ============================================================================
1423
- // Spacing Utilities
1424
- // ============================================================================
1425
- /**
1426
- * Create a spacing function from various input types
1427
- *
1428
- * @param spacingInput - Spacing configuration (number, array, or function), default 4
1429
- * @returns Spacing function
1430
- */ (color.main, .15),
2284
+ vars[`${prefix}-${key}-text-emphasis`] = emphasize(color.main, .15),
1431
2285
  // Background subtle: very light version for backgrounds (--atomix-primary-bg-subtle)
1432
2286
  vars[`${prefix}-${key}-bg-subtle`] = alpha(color.main, .1),
1433
2287
  // Border subtle: light version for borders (--atomix-primary-border-subtle)
@@ -1757,44 +2611,22 @@ function generateCSSVariables(theme, options = {}) {
1757
2611
  // 2X large border radius (maps to spacing-4 = 16px)
1758
2612
  vars[`${prefix}-border-radius-xxl`] = formatValue(borderRadius.xxl, "1rem"),
1759
2613
  // 3X large border radius (maps to spacing-6 = 24px)
1760
- vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius["3xl"], "1.5rem"),
1761
- // 4X large border radius (maps to spacing-8 = 32px)
1762
- vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius["4xl"], "2rem"),
1763
- // Pill shape (fully rounded, maps to spacing-200 = 800px)
1764
- vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, "50rem"),
1765
- vars;
1766
- }
1767
- /**
1768
- * Generate focus ring CSS variables
1769
- */ (theme.borderRadius, prefix)), Object.assign(variables, function(palette, prefix) {
1770
- const vars = {};
1771
- // Focus ring properties
1772
- return vars[`${prefix}-focus-ring-width`] = "3px", vars[`${prefix}-focus-ring-offset`] = "2px",
1773
- vars[`${prefix}-focus-ring-opacity`] = "0.25", vars;
1774
- }(theme.palette, prefix)), theme.custom && Object.keys(theme.custom).length > 0) {
1775
- const customVars =
1776
- /**
1777
- * CSS Variable Generator
1778
- *
1779
- * Generates CSS custom properties from theme objects and injects them into the DOM.
1780
- *
1781
- * **Token Naming Alignment:**
1782
- * This generator produces CSS variables that match the SCSS token naming pattern exactly:
1783
- * - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
1784
- * - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
1785
- * - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
1786
- * - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
1787
- *
1788
- * All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
1789
- * This ensures compatibility between SCSS themes and JavaScript themes.
1790
- *
1791
- * @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
1792
- */
1793
- /**
1794
- * Convert a nested object to flat CSS variable declarations
1795
- * Uses iterative approach for better performance with large objects
1796
- */
1797
- function(obj, prefix = "", result = {}) {
2614
+ vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius["3xl"], "1.5rem"),
2615
+ // 4X large border radius (maps to spacing-8 = 32px)
2616
+ vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius["4xl"], "2rem"),
2617
+ // Pill shape (fully rounded, maps to spacing-200 = 800px)
2618
+ vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, "50rem"),
2619
+ vars;
2620
+ }
2621
+ /**
2622
+ * Generate focus ring CSS variables
2623
+ */ (theme.borderRadius, prefix)), Object.assign(variables, function(palette, prefix) {
2624
+ const vars = {};
2625
+ // Focus ring properties
2626
+ return vars[`${prefix}-focus-ring-width`] = "3px", vars[`${prefix}-focus-ring-offset`] = "2px",
2627
+ vars[`${prefix}-focus-ring-opacity`] = "0.25", vars;
2628
+ }(theme.palette, prefix)), theme.custom && Object.keys(theme.custom).length > 0) {
2629
+ const customVars = function(obj, prefix = "", result = {}) {
1798
2630
  // Use iterative approach with stack to avoid deep recursion
1799
2631
  const stack = [ {
1800
2632
  obj: obj,
@@ -2248,19 +3080,29 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
2248
3080
  // Check storage first
2249
3081
  if (enablePersistence && storageAdapter.isAvailable()) {
2250
3082
  const stored = storageAdapter.getItem(storageKey);
2251
- if (stored) return stored;
3083
+ if (stored) {
3084
+ // If it looks like a JSON object, parse it
3085
+ if (stored.trim().startsWith("{")) try {
3086
+ return JSON.parse(stored);
3087
+ } catch (e) {
3088
+ return logger.error("Failed to parse stored theme tokens", e), stored;
3089
+ }
3090
+ return stored;
3091
+ }
2252
3092
  }
2253
3093
  // If defaultTheme is provided, use it
2254
3094
  return null != defaultTheme ? defaultTheme : "default";
2255
3095
  // Default fallback
2256
3096
  }), [ defaultTheme, enablePersistence, storageKey, storageAdapter ]), [currentTheme, setCurrentTheme] = useState((() => "string" == typeof initialDefaultTheme ? initialDefaultTheme : "tokens-theme")), [activeTokens, setActiveTokens] = useState((() => {
2257
- // If defaultTheme is DesignTokens, validate and store them
2258
- if (defaultTheme && "string" != typeof defaultTheme) {
3097
+ // 1. Check if initialDefaultTheme (from storage) is an object
3098
+ if (initialDefaultTheme && "string" != typeof initialDefaultTheme) {
3099
+ const {tokens: tokens, validation: validation} = validateAndMergeTokens(initialDefaultTheme);
3100
+ if (validation.valid) return tokens;
3101
+ }
3102
+ // 2. Check if defaultTheme prop is an object
3103
+ if (defaultTheme && "string" != typeof defaultTheme) {
2259
3104
  const {tokens: tokens, validation: validation} = validateAndMergeTokens(defaultTheme);
2260
- return validation.valid ? tokens : (logger.warn("Invalid default theme tokens, using defaults", {
2261
- errors: validation.errors,
2262
- warnings: validation.warnings
2263
- }), createTokens({}));
3105
+ if (validation.valid) return tokens;
2264
3106
  }
2265
3107
  return null;
2266
3108
  })), [isLoading, setIsLoading] = useState(!1), [error, setError] = useState(null), loadedThemesRef = useRef(new Set), themePromisesRef = useRef({}), abortControllerRef = useRef(null);
@@ -2272,10 +3114,14 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
2272
3114
  useEffect((() => {
2273
3115
  isServer() || applyThemeAttributes(String(currentTheme), dataAttribute);
2274
3116
  }), [ currentTheme, dataAttribute ]),
2275
- // Handle theme persistence
3117
+ // Handle persistence
2276
3118
  useEffect((() => {
2277
- enablePersistence && storageAdapter.isAvailable() && storageAdapter.setItem(storageKey, String(currentTheme));
2278
- }), [ currentTheme, storageKey, enablePersistence, storageAdapter ]),
3119
+ enablePersistence && storageAdapter.isAvailable() && ("tokens-theme" === currentTheme ?
3120
+ // Only persist if we have actual tokens to store
3121
+ activeTokens && storageAdapter.setItem(storageKey, JSON.stringify(activeTokens)) :
3122
+ // Persist named theme string
3123
+ storageAdapter.setItem(storageKey, String(currentTheme)));
3124
+ }), [ currentTheme, activeTokens, enablePersistence, storageKey, storageAdapter ]),
2279
3125
  // Cleanup: Remove completed promises and abort controllers on unmount
2280
3126
  useEffect((() => () => {
2281
3127
  // Cancel any in-flight theme loads
@@ -2414,20 +3260,21 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
2414
3260
  setIsLoading(!1);
2415
3261
  }
2416
3262
  }
2417
- }), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), themeManager = useMemo((() => ({})), []), availableThemes = useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
3263
+ }), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), updateTheme = useCallback((async (sectionOrTokens, values) => setTheme("string" == typeof sectionOrTokens && values ? values : sectionOrTokens)), [ setTheme ]), themeManager = useMemo((() => ({})), []), availableThemes = useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
2418
3264
  ...metadata,
2419
3265
  name: name
2420
3266
  })))), [ themes ]), contextValue = useMemo((() => ({
2421
3267
  theme: currentTheme,
2422
3268
  activeTokens: activeTokens,
2423
3269
  setTheme: setTheme,
3270
+ updateTheme: updateTheme,
2424
3271
  availableThemes: availableThemes,
2425
3272
  isLoading: isLoading,
2426
3273
  error: error,
2427
3274
  isThemeLoaded: isThemeLoaded,
2428
3275
  preloadTheme: preloadTheme,
2429
3276
  themeManager: themeManager
2430
- })), [ currentTheme, activeTokens, setTheme, availableThemes,
3277
+ })), [ currentTheme, activeTokens, setTheme, updateTheme, availableThemes,
2431
3278
  // Use memoized value
2432
3279
  isLoading, error, isThemeLoaded, preloadTheme, themeManager ]);
2433
3280
  // Check if theme is loaded
@@ -2481,6 +3328,7 @@ function useTheme() {
2481
3328
  theme: context.theme,
2482
3329
  activeTokens: context.activeTokens,
2483
3330
  setTheme: context.setTheme,
3331
+ updateTheme: context.updateTheme,
2484
3332
  availableThemes: context.availableThemes,
2485
3333
  isLoading: context.isLoading,
2486
3334
  error: context.error,
@@ -2716,6 +3564,389 @@ function useThemeTokens() {
2716
3564
  }
2717
3565
  }
2718
3566
 
3567
+ /**
3568
+ * useThemeSwitcher Hook
3569
+ *
3570
+ * React hook for managing theme switching with persistence and system preference detection.
3571
+ * Provides an easy-to-use API for dark/light mode toggling.
3572
+ *
3573
+ * @example
3574
+ * ```tsx
3575
+ * import { useThemeSwitcher } from '@shohojdhara/atomix/theme';
3576
+ *
3577
+ * function ThemeToggle() {
3578
+ * const { mode, toggle, setMode, isDark } = useThemeSwitcher();
3579
+ *
3580
+ * return (
3581
+ * <button onClick={toggle}>
3582
+ * {isDark ? '☀️ Light' : '🌙 Dark'}
3583
+ * </button>
3584
+ * );
3585
+ * }
3586
+ * ```
3587
+ */
3588
+ /**
3589
+ * Hook for managing theme switching
3590
+ *
3591
+ * @param options - Configuration options
3592
+ * @returns Theme switcher controls
3593
+ */ function useThemeSwitcher(options = {}) {
3594
+ const {initialMode: initialMode = "system", syncWithSystem: syncWithSystem = !1, storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, [mode, setModeState] = useState((() => {
3595
+ if ("undefined" == typeof window) return initialMode;
3596
+ // Check for saved preference first
3597
+ const saved = getCurrentTheme(storageKey);
3598
+ return saved && "system" !== saved ? saved : "system" === initialMode ? getSystemTheme() : initialMode;
3599
+ // Fall back to initial mode or system
3600
+ }));
3601
+ // State for current mode
3602
+ // Initialize theme on mount
3603
+ return useEffect((() => {
3604
+ "undefined" != typeof window && (
3605
+ // Initialize with proper theme application
3606
+ initializeTheme({
3607
+ storageKey: storageKey,
3608
+ enableTransition: enableTransition,
3609
+ transitionDuration: transitionDuration
3610
+ }),
3611
+ // Update state to match initialized theme
3612
+ setModeState(getCurrentTheme(storageKey)));
3613
+ }), [ storageKey, enableTransition, transitionDuration ]),
3614
+ // Listen for system theme changes if enabled
3615
+ useEffect((() => {
3616
+ if (syncWithSystem) return listenToSystemTheme((newMode => {
3617
+ setModeState(newMode), switchTheme(newMode, {
3618
+ storageKey: storageKey,
3619
+ enableTransition: enableTransition,
3620
+ transitionDuration: transitionDuration
3621
+ });
3622
+ }));
3623
+ }), [ syncWithSystem, storageKey, enableTransition, transitionDuration ]), {
3624
+ mode: mode,
3625
+ isDark: "dark" === mode,
3626
+ isLight: "light" === mode,
3627
+ toggle: useCallback((() => {
3628
+ const newMode = toggleTheme({
3629
+ storageKey: storageKey,
3630
+ enableTransition: enableTransition,
3631
+ transitionDuration: transitionDuration
3632
+ });
3633
+ return setModeState(newMode), newMode;
3634
+ }), [ storageKey, enableTransition, transitionDuration ]),
3635
+ setMode: useCallback((newMode => {
3636
+ switchTheme(newMode, {
3637
+ storageKey: storageKey,
3638
+ enableTransition: enableTransition,
3639
+ transitionDuration: transitionDuration
3640
+ }), setModeState(newMode);
3641
+ }), [ storageKey, enableTransition, transitionDuration ]),
3642
+ resetToSystem: useCallback((() => {
3643
+ const systemMode = getSystemTheme();
3644
+ switchTheme(systemMode, {
3645
+ storageKey: storageKey,
3646
+ enableTransition: enableTransition,
3647
+ transitionDuration: transitionDuration
3648
+ }), setModeState(systemMode);
3649
+ }), [ storageKey, enableTransition, transitionDuration ]),
3650
+ clearPreference: useCallback((() => {
3651
+ "undefined" != typeof window && localStorage.removeItem(storageKey);
3652
+ }), [ storageKey ])
3653
+ };
3654
+ }
3655
+
3656
+ /**
3657
+ * ThemeToggle component with multiple variants
3658
+ */ const ThemeToggle = ({className: className = "", showLabel: showLabel = !1, lightLabel: lightLabel = "Light", darkLabel: darkLabel = "Dark", iconSize: iconSize = 20, variant: variant = "icon", render: render, ariaLabel: ariaLabel = "Toggle theme", ...hookOptions}) => {
3659
+ const {mode: mode, isDark: isDark, toggle: toggle} = useThemeSwitcher(hookOptions);
3660
+ // Custom render
3661
+ return render ? jsx(Fragment, {
3662
+ children: render({
3663
+ isDark: isDark,
3664
+ toggle: toggle,
3665
+ mode: mode
3666
+ })
3667
+ }) :
3668
+ // Icon-only variant (default)
3669
+ "icon" === variant ? jsx("button", {
3670
+ onClick: toggle,
3671
+ className: `theme-toggle theme-toggle-icon ${className}`,
3672
+ "aria-label": ariaLabel,
3673
+ title: isDark ? darkLabel : lightLabel,
3674
+ style: {
3675
+ background: "none",
3676
+ border: "none",
3677
+ cursor: "pointer",
3678
+ padding: "8px",
3679
+ borderRadius: "50%",
3680
+ display: "flex",
3681
+ alignItems: "center",
3682
+ justifyContent: "center",
3683
+ transition: "all 0.3s ease-in-out"
3684
+ },
3685
+ children: isDark ? jsxs("svg", {
3686
+ width: iconSize,
3687
+ height: iconSize,
3688
+ viewBox: "0 0 24 24",
3689
+ fill: "none",
3690
+ stroke: "currentColor",
3691
+ strokeWidth: "2",
3692
+ strokeLinecap: "round",
3693
+ strokeLinejoin: "round",
3694
+ children: [ jsx("circle", {
3695
+ cx: "12",
3696
+ cy: "12",
3697
+ r: "5"
3698
+ }), jsx("line", {
3699
+ x1: "12",
3700
+ y1: "1",
3701
+ x2: "12",
3702
+ y2: "3"
3703
+ }), jsx("line", {
3704
+ x1: "12",
3705
+ y1: "21",
3706
+ x2: "12",
3707
+ y2: "23"
3708
+ }), jsx("line", {
3709
+ x1: "4.22",
3710
+ y1: "4.22",
3711
+ x2: "5.64",
3712
+ y2: "5.64"
3713
+ }), jsx("line", {
3714
+ x1: "18.36",
3715
+ y1: "18.36",
3716
+ x2: "19.78",
3717
+ y2: "19.78"
3718
+ }), jsx("line", {
3719
+ x1: "1",
3720
+ y1: "12",
3721
+ x2: "3",
3722
+ y2: "12"
3723
+ }), jsx("line", {
3724
+ x1: "21",
3725
+ y1: "12",
3726
+ x2: "23",
3727
+ y2: "12"
3728
+ }), jsx("line", {
3729
+ x1: "4.22",
3730
+ y1: "19.78",
3731
+ x2: "5.64",
3732
+ y2: "18.36"
3733
+ }), jsx("line", {
3734
+ x1: "18.36",
3735
+ y1: "5.64",
3736
+ x2: "19.78",
3737
+ y2: "4.22"
3738
+ }) ]
3739
+ }) : jsx("svg", {
3740
+ width: iconSize,
3741
+ height: iconSize,
3742
+ viewBox: "0 0 24 24",
3743
+ fill: "none",
3744
+ stroke: "currentColor",
3745
+ strokeWidth: "2",
3746
+ strokeLinecap: "round",
3747
+ strokeLinejoin: "round",
3748
+ children: jsx("path", {
3749
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
3750
+ })
3751
+ })
3752
+ }) :
3753
+ // Button variant with text
3754
+ "button" === variant ? jsxs("button", {
3755
+ onClick: toggle,
3756
+ className: `theme-toggle theme-toggle-button ${className}`,
3757
+ "aria-label": ariaLabel,
3758
+ style: {
3759
+ display: "flex",
3760
+ alignItems: "center",
3761
+ gap: "8px",
3762
+ padding: "8px 16px",
3763
+ borderRadius: "8px",
3764
+ border: "1px solid currentColor",
3765
+ background: "transparent",
3766
+ color: "inherit",
3767
+ cursor: "pointer",
3768
+ fontSize: "14px",
3769
+ fontWeight: "500",
3770
+ transition: "all 0.3s ease-in-out"
3771
+ },
3772
+ children: [ isDark ? jsxs("svg", {
3773
+ width: iconSize,
3774
+ height: iconSize,
3775
+ viewBox: "0 0 24 24",
3776
+ fill: "none",
3777
+ stroke: "currentColor",
3778
+ strokeWidth: "2",
3779
+ strokeLinecap: "round",
3780
+ strokeLinejoin: "round",
3781
+ children: [ jsx("circle", {
3782
+ cx: "12",
3783
+ cy: "12",
3784
+ r: "5"
3785
+ }), jsx("line", {
3786
+ x1: "12",
3787
+ y1: "1",
3788
+ x2: "12",
3789
+ y2: "3"
3790
+ }), jsx("line", {
3791
+ x1: "12",
3792
+ y1: "21",
3793
+ x2: "12",
3794
+ y2: "23"
3795
+ }), jsx("line", {
3796
+ x1: "4.22",
3797
+ y1: "4.22",
3798
+ x2: "5.64",
3799
+ y2: "5.64"
3800
+ }), jsx("line", {
3801
+ x1: "18.36",
3802
+ y1: "18.36",
3803
+ x2: "19.78",
3804
+ y2: "19.78"
3805
+ }), jsx("line", {
3806
+ x1: "1",
3807
+ y1: "12",
3808
+ x2: "3",
3809
+ y2: "12"
3810
+ }), jsx("line", {
3811
+ x1: "21",
3812
+ y1: "12",
3813
+ x2: "23",
3814
+ y2: "12"
3815
+ }), jsx("line", {
3816
+ x1: "4.22",
3817
+ y1: "19.78",
3818
+ x2: "5.64",
3819
+ y2: "18.36"
3820
+ }), jsx("line", {
3821
+ x1: "18.36",
3822
+ y1: "5.64",
3823
+ x2: "19.78",
3824
+ y2: "4.22"
3825
+ }) ]
3826
+ }) : jsx("svg", {
3827
+ width: iconSize,
3828
+ height: iconSize,
3829
+ viewBox: "0 0 24 24",
3830
+ fill: "none",
3831
+ stroke: "currentColor",
3832
+ strokeWidth: "2",
3833
+ strokeLinecap: "round",
3834
+ strokeLinejoin: "round",
3835
+ children: jsx("path", {
3836
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
3837
+ })
3838
+ }), showLabel && jsx("span", {
3839
+ children: isDark ? darkLabel : lightLabel
3840
+ }) ]
3841
+ }) :
3842
+ // Switch/toggle variant
3843
+ "switch" === variant ? jsx("div", {
3844
+ className: `theme-toggle theme-toggle-switch ${className}`,
3845
+ role: "button",
3846
+ tabIndex: 0,
3847
+ onClick: toggle,
3848
+ onKeyDown: e => "Enter" === e.key && toggle(),
3849
+ "aria-label": ariaLabel,
3850
+ style: {
3851
+ position: "relative",
3852
+ width: "56px",
3853
+ height: "28px",
3854
+ borderRadius: "14px",
3855
+ background: isDark ? "#4b5563" : "#d1d5db",
3856
+ cursor: "pointer",
3857
+ transition: "background 0.3s ease-in-out",
3858
+ display: "flex",
3859
+ alignItems: "center",
3860
+ padding: "2px"
3861
+ },
3862
+ children: jsx("div", {
3863
+ style: {
3864
+ position: "absolute",
3865
+ left: isDark ? "auto" : "2px",
3866
+ right: isDark ? "2px" : "auto",
3867
+ width: "24px",
3868
+ height: "24px",
3869
+ borderRadius: "50%",
3870
+ background: "white",
3871
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
3872
+ transition: "all 0.3s ease-in-out",
3873
+ display: "flex",
3874
+ alignItems: "center",
3875
+ justifyContent: "center"
3876
+ },
3877
+ children: isDark ? jsx("svg", {
3878
+ width: "14",
3879
+ height: "14",
3880
+ viewBox: "0 0 24 24",
3881
+ fill: "none",
3882
+ stroke: "#4b5563",
3883
+ strokeWidth: "2",
3884
+ strokeLinecap: "round",
3885
+ strokeLinejoin: "round",
3886
+ children: jsx("path", {
3887
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
3888
+ })
3889
+ }) : jsxs("svg", {
3890
+ width: "14",
3891
+ height: "14",
3892
+ viewBox: "0 0 24 24",
3893
+ fill: "none",
3894
+ stroke: "#f59e0b",
3895
+ strokeWidth: "2",
3896
+ strokeLinecap: "round",
3897
+ strokeLinejoin: "round",
3898
+ children: [ jsx("circle", {
3899
+ cx: "12",
3900
+ cy: "12",
3901
+ r: "5"
3902
+ }), jsx("line", {
3903
+ x1: "12",
3904
+ y1: "1",
3905
+ x2: "12",
3906
+ y2: "3"
3907
+ }), jsx("line", {
3908
+ x1: "12",
3909
+ y1: "21",
3910
+ x2: "12",
3911
+ y2: "23"
3912
+ }), jsx("line", {
3913
+ x1: "4.22",
3914
+ y1: "4.22",
3915
+ x2: "5.64",
3916
+ y2: "5.64"
3917
+ }), jsx("line", {
3918
+ x1: "18.36",
3919
+ y1: "18.36",
3920
+ x2: "19.78",
3921
+ y2: "19.78"
3922
+ }), jsx("line", {
3923
+ x1: "1",
3924
+ y1: "12",
3925
+ x2: "3",
3926
+ y2: "12"
3927
+ }), jsx("line", {
3928
+ x1: "21",
3929
+ y1: "12",
3930
+ x2: "23",
3931
+ y2: "12"
3932
+ }), jsx("line", {
3933
+ x1: "4.22",
3934
+ y1: "19.78",
3935
+ x2: "5.64",
3936
+ y2: "18.36"
3937
+ }), jsx("line", {
3938
+ x1: "18.36",
3939
+ y1: "5.64",
3940
+ x2: "19.78",
3941
+ y2: "4.22"
3942
+ }) ]
3943
+ })
3944
+ })
3945
+ }) : null;
3946
+ };
3947
+
3948
+ ThemeToggle.displayName = "ThemeToggle";
3949
+
2719
3950
  /**
2720
3951
  * Theme Applicator
2721
3952
  *
@@ -2728,7 +3959,8 @@ function useThemeTokens() {
2728
3959
  * Theme applicator class for runtime theme application
2729
3960
  *
2730
3961
  * Uses the unified theme system for efficient CSS variable generation and injection.
2731
- */ class ThemeApplicator {
3962
+ */
3963
+ class ThemeApplicator {
2732
3964
  constructor(root = document.documentElement) {
2733
3965
  this.styleId = "atomix-theme-applicator", this.root = root;
2734
3966
  }
@@ -4268,65 +5500,7 @@ class ThemeValidator {
4268
5500
  children: "\n .atomix-theme-comparator {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n background: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 24px;\n }\n\n .comparator-header {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 24px;\n align-items: center;\n margin-bottom: 24px;\n padding-bottom: 24px;\n border-bottom: 2px solid #e0e0e0;\n }\n\n .theme-column {\n text-align: center;\n }\n\n .theme-column h3 {\n margin: 0;\n font-size: 20px;\n color: #333;\n }\n\n .version {\n display: inline-block;\n margin-top: 4px;\n padding: 2px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n }\n\n .vs-divider {\n font-weight: bold;\n font-size: 24px;\n color: #666;\n }\n\n .filter-controls {\n display: flex;\n gap: 16px;\n align-items: center;\n margin-bottom: 24px;\n padding: 16px;\n background: #f5f5f5;\n border-radius: 8px;\n flex-wrap: wrap;\n }\n\n .filter-group {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .filter-group label {\n font-size: 14px;\n font-weight: 500;\n color: #666;\n }\n\n .search-input {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 14px;\n width: 250px;\n }\n\n .search-input:focus {\n outline: none;\n border-color: #2196f3;\n }\n\n .clear-search {\n background: #f44336;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .clear-search:hover {\n background: #d32f2f;\n }\n\n .filter-select {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n }\n\n .clear-filters {\n padding: 8px 16px;\n background: #666;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n }\n\n .clear-filters:hover {\n background: #555;\n }\n\n .search-highlight {\n background: #fff59d;\n padding: 2px 4px;\n border-radius: 2px;\n }\n\n .comparator-stats {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .stat {\n text-align: center;\n padding: 16px;\n background: #f5f5f5;\n border-radius: 8px;\n }\n\n .stat-added {\n border-left: 4px solid #4caf50;\n }\n\n .stat-removed {\n border-left: 4px solid #f44336;\n }\n\n .stat-changed {\n border-left: 4px solid #ff9800;\n }\n\n .stat-filtered {\n border-left: 4px solid #2196f3;\n }\n\n .stat-value {\n display: block;\n font-size: 32px;\n font-weight: bold;\n color: #2196f3;\n }\n\n .stat-label {\n display: block;\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n }\n\n .no-differences {\n text-align: center;\n padding: 48px;\n font-size: 18px;\n color: #4caf50;\n background: #e8f5e9;\n border-radius: 8px;\n }\n\n .differences-list h4 {\n margin: 0 0 16px 0;\n font-size: 18px;\n color: #333;\n }\n\n .difference-item {\n margin-bottom: 16px;\n border: 2px solid;\n border-radius: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .difference-added {\n border-color: #4caf50;\n }\n\n .difference-removed {\n border-color: #f44336;\n }\n\n .difference-changed {\n border-color: #ff9800;\n }\n\n .difference-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n background: rgba(0, 0, 0, 0.05);\n }\n\n .difference-type {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: bold;\n color: white;\n text-transform: uppercase;\n }\n\n .difference-path {\n font-family: 'Monaco', 'Menlo', monospace;\n font-size: 13px;\n color: #1976d2;\n flex: 1;\n }\n\n .difference-category {\n font-size: 11px;\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 4px;\n color: #666;\n text-transform: uppercase;\n }\n\n .difference-values {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 16px;\n padding: 16px;\n align-items: start;\n }\n\n .value-column {\n min-width: 0;\n }\n\n .value-column.value-empty {\n opacity: 0.5;\n }\n\n .value-label {\n font-size: 12px;\n font-weight: bold;\n color: #666;\n margin-bottom: 8px;\n }\n\n .value-content {\n margin: 0;\n padding: 12px;\n background: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n font-family: 'Monaco', 'Menlo', monospace;\n font-size: 12px;\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-all;\n }\n\n .value-empty {\n color: #999;\n font-style: italic;\n }\n\n .value-divider {\n font-size: 20px;\n color: #666;\n align-self: center;\n }\n "
4269
5501
  }) ]
4270
5502
  });
4271
- };
4272
-
4273
- /**
4274
- * Theme Comparator Component
4275
- *
4276
- * Compares two themes and highlights differences
4277
- */ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
4278
- return function(that, callbackfn, argumentsLength, memo) {
4279
- var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
4280
- if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
4281
- var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
4282
- if (argumentsLength < 2) for (;;) {
4283
- if (index in self) {
4284
- memo = self[index], index += i;
4285
- break;
4286
- }
4287
- if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
4288
- }
4289
- for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
4290
- return memo;
4291
- };
4292
- }, arrayReduce = {
4293
- // `Array.prototype.reduce` method
4294
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
4295
- left: createMethod(!1),
4296
- // `Array.prototype.reduceRight` method
4297
- // https://tc39.es/ecma262/#sec-array.prototype.reduceright
4298
- right: createMethod(!0)
4299
- }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
4300
- return userAgent.slice(0, string.length) === string;
4301
- }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
4302
-
4303
- // `Array.prototype.reduce` method
4304
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
4305
- _export({
4306
- target: "Array",
4307
- proto: !0,
4308
- forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
4309
- var method = [][METHOD_NAME];
4310
- return !!method && fails((function() {
4311
- // eslint-disable-next-line no-useless-call -- required for testing
4312
- method.call(null, argument || function() {
4313
- return 1;
4314
- }, 1);
4315
- }));
4316
- }("reduce")
4317
- }, {
4318
- reduce: function(callbackfn /* , initialValue */) {
4319
- var length = arguments.length;
4320
- return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
4321
- }
4322
- });
4323
-
4324
- var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype;
4325
-
4326
- const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
4327
- var own = it.reduce;
4328
- return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
4329
- })), DEFAULT_PALETTE = {
5503
+ }, DEFAULT_PALETTE = {
4330
5504
  primary: {
4331
5505
  main: "#7c3aed",
4332
5506
  // Primary-6
@@ -4511,9 +5685,11 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
4511
5685
  pill: "50rem"
4512
5686
  };
4513
5687
 
4514
- // ============================================================================
4515
- // Default Theme Values
4516
- // ============================================================================
5688
+ /**
5689
+ * Theme Comparator Component
5690
+ *
5691
+ * Compares two themes and highlights differences
5692
+ */
4517
5693
  // ============================================================================
4518
5694
  // Helper Functions
4519
5695
  // ============================================================================
@@ -4523,13 +5699,13 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
4523
5699
  function createPaletteColor(color) {
4524
5700
  return "string" == typeof color ? {
4525
5701
  main: color,
4526
- light: lighten(color),
4527
- dark: darken(color),
5702
+ light: lighten(color, .15),
5703
+ dark: darken(color, .15),
4528
5704
  contrastText: getContrastText(color)
4529
5705
  } : {
4530
5706
  main: color.main || "#000000",
4531
- light: color.light || lighten(color.main || "#000000"),
4532
- dark: color.dark || darken(color.main || "#000000"),
5707
+ light: color.light || lighten(color.main || "#000000", .15),
5708
+ dark: color.dark || darken(color.main || "#000000", .15),
4533
5709
  contrastText: color.contrastText || getContrastText(color.main || "#000000")
4534
5710
  };
4535
5711
  }
@@ -4564,23 +5740,19 @@ function createThemeObject(...options) {
4564
5740
  },
4565
5741
  background: {
4566
5742
  default: mergedOptions.palette?.background?.default || DEFAULT_PALETTE.background.default,
5743
+ paper: mergedOptions.palette?.background?.paper || DEFAULT_PALETTE.background.paper,
4567
5744
  subtle: mergedOptions.palette?.background?.subtle || DEFAULT_PALETTE.background.subtle
4568
5745
  },
4569
5746
  text: {
4570
5747
  primary: mergedOptions.palette?.text?.primary || DEFAULT_PALETTE.text.primary,
4571
5748
  secondary: mergedOptions.palette?.text?.secondary || DEFAULT_PALETTE.text.secondary,
4572
5749
  disabled: mergedOptions.palette?.text?.disabled || DEFAULT_PALETTE.text.disabled
4573
- }
5750
+ },
5751
+ // Spread other palette properties
5752
+ ...mergedOptions.palette
4574
5753
  }, typography = deepMerge({
4575
5754
  ...DEFAULT_TYPOGRAPHY
4576
- }, mergedOptions.typography || {}), spacing = function(spacingInput = 4) {
4577
- // If it's already a function, return it
4578
- return "function" == typeof spacingInput ? spacingInput :
4579
- // If it's a number, create a function that multiplies by that number
4580
- "number" == typeof spacingInput ? (...values) => 0 === values.length ? "0px" : values.map((value => value * spacingInput + "px")).join(" ") :
4581
- // If it's an array, use it as a scale
4582
- Array.isArray(spacingInput) ? (...values) => 0 === values.length ? "0px" : values.map((value => `${spacingInput[value] || value}px`)).join(" ") : (...values) => 0 === values.length ? "0px" : values.map((value => 4 * value + "px")).join(" ");
4583
- }(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
5755
+ }, mergedOptions.typography || {}), spacing = createSpacing(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
4584
5756
  const values = {
4585
5757
  xs: 0,
4586
5758
  sm: 576,
@@ -5403,24 +6575,6 @@ const ThemeLiveEditor = ({initialTheme: initialTheme, onChange: onChange, classN
5403
6575
  /**
5404
6576
  * Design Tokens Customizer Component
5405
6577
  */
5406
- /**
5407
- * Theme Adapter
5408
- *
5409
- * Converts between Theme objects and DesignTokens.
5410
- */
5411
- /**
5412
- * Convert DesignTokens to Theme-compatible CSS variables
5413
- *
5414
- * @param tokens - DesignTokens object
5415
- * @returns CSS variables object compatible with Theme.cssVars
5416
- */
5417
- function designTokensToCSSVars(tokens) {
5418
- const cssVars = {};
5419
- return Object.entries(tokens).forEach((([key, value]) => {
5420
- void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
5421
- })), cssVars;
5422
- }
5423
-
5424
6578
  /**
5425
6579
  * Map SCSS tokens to CSS custom properties
5426
6580
  *
@@ -5428,7 +6582,8 @@ function designTokensToCSSVars(tokens) {
5428
6582
  * const tokens = { '$primary-color': '#7AFFD7', '$spacing-md': '16px' }
5429
6583
  * const vars = mapSCSSTokensToCSSVars(tokens)
5430
6584
  * // Returns: { '--primary-color': '#7AFFD7', '--spacing-md': '16px' }
5431
- */ function mapSCSSTokensToCSSVars(tokens, options = {}) {
6585
+ */
6586
+ function mapSCSSTokensToCSSVars(tokens, options = {}) {
5432
6587
  const vars = {}, {prefix: prefix = "atomix", separator: separator = "-"} = options;
5433
6588
  return Object.entries(tokens).forEach((([key, value]) => {
5434
6589
  // Remove $ prefix from SCSS variables
@@ -5541,6 +6696,335 @@ function designTokensToCSSVars(tokens) {
5541
6696
  // Check if keys look like DesignTokens (kebab-case, no nesting)
5542
6697
  }
5543
6698
 
6699
+ /**
6700
+ * Performance monitor class
6701
+ */ class PerformanceMonitor {
6702
+ /**
6703
+ * Create a new performance monitor
6704
+ *
6705
+ * @param config Configuration options
6706
+ */
6707
+ constructor(config) {
6708
+ this.frameCount = 0, this.lastSampleTime = 0, this.lastFpsUpdate = 0, this.frameTimes = [],
6709
+ this.animationFrameId = null, this.isActive = !1, this.startTime = 0, this.config = {
6710
+ fpsTarget: config?.fpsTarget ?? 60,
6711
+ sampleInterval: config?.sampleInterval ?? 500,
6712
+ onUpdate: config?.onUpdate ?? (() => {}),
6713
+ onDegraded: config?.onDegraded ?? (() => {}),
6714
+ enableMemoryMonitoring: config?.enableMemoryMonitoring ?? ("undefined" != typeof window && window.performance && window.performance.memory)
6715
+ };
6716
+ }
6717
+ /**
6718
+ * Start monitoring performance
6719
+ */ start() {
6720
+ this.isActive || (this.isActive = !0, this.frameCount = 0, this.lastSampleTime = performance.now(),
6721
+ this.lastFpsUpdate = this.lastSampleTime, this.frameTimes = [], this.startTime = this.lastSampleTime,
6722
+ this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this)));
6723
+ }
6724
+ /**
6725
+ * Stop monitoring performance
6726
+ */ stop() {
6727
+ this.animationFrameId && (cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null),
6728
+ this.isActive = !1;
6729
+ }
6730
+ /**
6731
+ * Get current performance metrics
6732
+ */ getMetrics() {
6733
+ var _context;
6734
+ const now = performance.now(), elapsed = now - this.lastFpsUpdate, fps = elapsed > 0 ? Math.round(this.frameCount / elapsed * 1e3) : 0, avgFrameTime = this.frameTimes.length > 0 ? _reduceInstanceProperty(_context = this.frameTimes).call(_context, ((a, b) => a + b), 0) / this.frameTimes.length : 0, peakFrameTime = this.frameTimes.length > 0 ? Math.max(...this.frameTimes) : 0;
6735
+ // Get memory stats if available
6736
+ let memory;
6737
+ if (this.config.enableMemoryMonitoring) {
6738
+ const perf = window.performance;
6739
+ perf && perf.memory && (memory = {
6740
+ usedJSHeapSize: perf.memory.usedJSHeapSize,
6741
+ totalJSHeapSize: perf.memory.totalJSHeapSize,
6742
+ jsHeapSizeLimit: perf.memory.jsHeapSizeLimit
6743
+ });
6744
+ }
6745
+ return {
6746
+ fps: fps,
6747
+ frameTime: avgFrameTime,
6748
+ peakFrameTime: peakFrameTime,
6749
+ memory: memory,
6750
+ timestamp: now,
6751
+ isDegraded: fps < .7 * this.config.fpsTarget
6752
+ };
6753
+ }
6754
+ /**
6755
+ * Get the current FPS
6756
+ */ getFps() {
6757
+ return this.getMetrics().fps;
6758
+ }
6759
+ /**
6760
+ * Check if performance is degraded
6761
+ */ isPerformanceDegraded() {
6762
+ return this.getMetrics().isDegraded;
6763
+ }
6764
+ /**
6765
+ * Private method called on each animation frame
6766
+ */ onFrame(timestamp) {
6767
+ if (!this.isActive) return;
6768
+ // Calculate frame time
6769
+ const frameTime = timestamp - this.lastSampleTime;
6770
+ // Check if we need to update metrics
6771
+ if (this.frameTimes.push(frameTime),
6772
+ // Keep only the last 60 frame times for averaging
6773
+ this.frameTimes.length > 60 && this.frameTimes.shift(), this.frameCount++, this.lastSampleTime = timestamp,
6774
+ timestamp - this.lastFpsUpdate >= this.config.sampleInterval) {
6775
+ const metrics = this.getMetrics();
6776
+ // Call update callback
6777
+ this.config.onUpdate(metrics),
6778
+ // Check for degradation
6779
+ metrics.isDegraded && this.config.onDegraded(metrics),
6780
+ // Reset counters
6781
+ this.frameCount = 0, this.lastFpsUpdate = timestamp;
6782
+ }
6783
+ this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this));
6784
+ }
6785
+ /**
6786
+ * Run a performance test for a specific function
6787
+ *
6788
+ * @param fn Function to test
6789
+ * @param iterations Number of iterations (default: 100)
6790
+ * @returns Average execution time in ms
6791
+ */ async testFunctionPerformance(fn, iterations = 100) {
6792
+ const times = [];
6793
+ for (let i = 0; i < iterations; i++) {
6794
+ const start = performance.now();
6795
+ fn();
6796
+ const end = performance.now();
6797
+ times.push(end - start);
6798
+ }
6799
+ return _reduceInstanceProperty(times).call(times, ((a, b) => a + b), 0) / times.length;
6800
+ }
6801
+ }
6802
+
6803
+ /**
6804
+ * Create a performance monitor instance
6805
+ *
6806
+ * @param config Configuration options
6807
+ * @returns PerformanceMonitor instance
6808
+ *
6809
+ * @example
6810
+ * ```typescript
6811
+ * import { createPerformanceMonitor } from '@shohojdhara/atomix/theme';
6812
+ *
6813
+ * const monitor = createPerformanceMonitor({
6814
+ * fpsTarget: 60,
6815
+ * onUpdate: (metrics) => console.log('FPS:', metrics.fps),
6816
+ * onDegraded: (metrics) => console.warn('Performance degraded!', metrics),
6817
+ * });
6818
+ *
6819
+ * monitor.start();
6820
+ *
6821
+ * // Later...
6822
+ * monitor.stop();
6823
+ * ```
6824
+ */ function createPerformanceMonitor(config) {
6825
+ return new PerformanceMonitor(config);
6826
+ }
6827
+
6828
+ /**
6829
+ * Hook for React components to monitor performance
6830
+ *
6831
+ * @param config Configuration options
6832
+ * @returns Performance metrics and monitor controls
6833
+ *
6834
+ * @example
6835
+ * ```typescript
6836
+ * import { usePerformanceMonitor } from '@shohojdhara/atomix/theme';
6837
+ *
6838
+ * function MyComponent() {
6839
+ * const { metrics, start, stop } = usePerformanceMonitor({ fpsTarget: 60 });
6840
+ *
6841
+ * useEffect(() => {
6842
+ * start();
6843
+ * return () => stop();
6844
+ * }, []);
6845
+ *
6846
+ * return <div>FPS: {metrics.fps}</div>;
6847
+ * }
6848
+ * ```
6849
+ */ function usePerformanceMonitor(config) {
6850
+ const [monitor] = React.useState((() => createPerformanceMonitor(config))), [metrics, setMetrics] = React.useState((() => "undefined" != typeof window ? monitor.getMetrics() : {
6851
+ fps: 0,
6852
+ frameTime: 0,
6853
+ peakFrameTime: 0,
6854
+ timestamp: 0,
6855
+ isDegraded: !1
6856
+ })), start = React.useCallback((() => {
6857
+ "undefined" != typeof window && monitor.start();
6858
+ }), [ monitor ]), stop = React.useCallback((() => {
6859
+ "undefined" != typeof window && monitor.stop();
6860
+ }), [ monitor ]);
6861
+ return React.useEffect((() => {
6862
+ if ("undefined" == typeof window) return;
6863
+ // Update metrics when monitor callbacks fire
6864
+ const originalOnUpdate = config?.onUpdate;
6865
+ return monitor.config.onUpdate = newMetrics => {
6866
+ setMetrics(newMetrics), originalOnUpdate?.(newMetrics);
6867
+ }, () => {
6868
+ monitor.stop();
6869
+ };
6870
+ }), [ monitor, config?.onUpdate ]), {
6871
+ metrics: metrics,
6872
+ start: start,
6873
+ stop: stop
6874
+ };
6875
+ }
6876
+
6877
+ /**
6878
+ * Responsive Utility for Atomix Theme System
6879
+ *
6880
+ * Provides responsive breakpoint detection and device-aware parameter scaling
6881
+ * based on configuration from the advanced optimization features.
6882
+ */
6883
+ /**
6884
+ * Responsive utility class
6885
+ */ class ResponsiveUtil {
6886
+ constructor(config) {
6887
+ this.currentDevice = "desktop", // Default
6888
+ this.resizeHandler = null, this.observer = null, this.config = config, this.currentDevice = this.getCurrentDeviceType(),
6889
+ // Set up resize listener
6890
+ this.setupResizeListener();
6891
+ }
6892
+ /**
6893
+ * Get the current device type based on viewport width
6894
+ */ getCurrentDeviceType() {
6895
+ if ("undefined" == typeof window) return "desktop";
6896
+ // SSR fallback
6897
+ const width = window.innerWidth;
6898
+ // Parse breakpoint values to numbers
6899
+ this.parsePxValue(this.config.breakpoints.mobile);
6900
+ const tabletWidth = this.parsePxValue(this.config.breakpoints.tablet), desktopWidth = this.parsePxValue(this.config.breakpoints.desktop), wideWidth = this.parsePxValue(this.config.breakpoints.wide);
6901
+ return width < tabletWidth ? "mobile" : width < desktopWidth ? "tablet" : width < wideWidth ? "desktop" : "wide";
6902
+ }
6903
+ /**
6904
+ * Get the scaling factor for the current device
6905
+ */ getCurrentScalingFactor() {
6906
+ // 'wide' devices use the same scaling as 'desktop'
6907
+ const scalingKey = "wide" === this.currentDevice ? "desktop" : this.currentDevice;
6908
+ return this.config.deviceScaling[scalingKey] || 1;
6909
+ }
6910
+ /**
6911
+ * Scale a value based on the current device's scaling factor
6912
+ */ scaleValue(value) {
6913
+ return value * this.getCurrentScalingFactor();
6914
+ }
6915
+ /**
6916
+ * Check if the current device matches a specific type
6917
+ */ isDevice(device) {
6918
+ return this.currentDevice === device;
6919
+ }
6920
+ /**
6921
+ * Check if the current device is mobile or smaller
6922
+ */ isMobileOrSmaller() {
6923
+ return "mobile" === this.currentDevice;
6924
+ }
6925
+ /**
6926
+ * Check if the current device is tablet or smaller
6927
+ */ isTabletOrSmaller() {
6928
+ return "mobile" === this.currentDevice || "tablet" === this.currentDevice;
6929
+ }
6930
+ /**
6931
+ * Check if the current device is desktop or larger
6932
+ */ isDesktopOrLarger() {
6933
+ return "desktop" === this.currentDevice || "wide" === this.currentDevice;
6934
+ }
6935
+ /**
6936
+ * Update the responsive configuration
6937
+ */ updateConfig(config) {
6938
+ this.config = config, this.currentDevice = this.getCurrentDeviceType();
6939
+ }
6940
+ /**
6941
+ * Destroy the responsive utility and clean up listeners
6942
+ */ destroy() {
6943
+ this.resizeHandler && (window.removeEventListener("resize", this.resizeHandler),
6944
+ this.resizeHandler = null), this.observer && (this.observer.disconnect(), this.observer = null);
6945
+ }
6946
+ /**
6947
+ * Parse a CSS value to pixels
6948
+ */ parsePxValue(value) {
6949
+ return value.endsWith("px") ? parseFloat(value.slice(0, -2)) :
6950
+ // For other units, we'll use a rough conversion assuming 16px base
6951
+ value.endsWith("rem") ? 16 * parseFloat(value.slice(0, -3)) : value.endsWith("em") ? 16 * parseFloat(value.slice(0, -2)) : parseFloat(value) || 0;
6952
+ }
6953
+ /**
6954
+ * Set up the resize listener
6955
+ */ setupResizeListener() {
6956
+ if ("undefined" == typeof window) return;
6957
+ // Throttled resize handler
6958
+ let resizeTimeout = null;
6959
+ const handleResize = () => {
6960
+ resizeTimeout && window.clearTimeout(resizeTimeout), resizeTimeout = window.setTimeout((() => {
6961
+ const newDeviceType = this.getCurrentDeviceType();
6962
+ newDeviceType !== this.currentDevice && (this.currentDevice = newDeviceType);
6963
+ }), 150);
6964
+ } // Throttle to 150ms
6965
+ ;
6966
+ this.resizeHandler = handleResize, window.addEventListener("resize", handleResize),
6967
+ // Also observe the document body for size changes
6968
+ "undefined" != typeof ResizeObserver && (this.observer = new ResizeObserver(handleResize),
6969
+ this.observer.observe(document.body));
6970
+ }
6971
+ }
6972
+
6973
+ /**
6974
+ * Create a responsive utility instance
6975
+ *
6976
+ * @param config Responsive configuration
6977
+ * @returns ResponsiveUtil instance
6978
+ */ function createResponsiveUtil(config) {
6979
+ return new ResponsiveUtil(config);
6980
+ }
6981
+
6982
+ /**
6983
+ * Hook for React components to use responsive features
6984
+ *
6985
+ * @param config Responsive configuration
6986
+ * @returns Current device type and utility functions
6987
+ */ function useResponsive(config) {
6988
+ const [util] = React.useState((() => createResponsiveUtil(config))), [deviceType, setDeviceType] = React.useState((() => "undefined" != typeof window ? util.getCurrentDeviceType() : "desktop"));
6989
+ return React.useEffect((() => {
6990
+ if ("undefined" == typeof window) return;
6991
+ const handleResize = () => {
6992
+ const newDeviceType = util.getCurrentDeviceType();
6993
+ newDeviceType !== deviceType && setDeviceType(newDeviceType);
6994
+ };
6995
+ // Update device type on mount
6996
+ return setDeviceType(util.getCurrentDeviceType()),
6997
+ // Listen for resize events
6998
+ window.addEventListener("resize", handleResize), () => {
6999
+ window.removeEventListener("resize", handleResize), util.destroy();
7000
+ };
7001
+ }), [ util, deviceType ]), "undefined" == typeof window ? {
7002
+ deviceType: "desktop",
7003
+ isMobile: !1,
7004
+ isTablet: !1,
7005
+ isDesktop: !0,
7006
+ isWide: !1,
7007
+ scaleValue: value => value,
7008
+ getCurrentDeviceType: () => "desktop",
7009
+ getCurrentScalingFactor: () => 1,
7010
+ isMobileOrSmaller: () => !1,
7011
+ isTabletOrSmaller: () => !0,
7012
+ isDesktopOrLarger: () => !0
7013
+ } : {
7014
+ deviceType: deviceType,
7015
+ isMobile: "mobile" === deviceType,
7016
+ isTablet: "tablet" === deviceType,
7017
+ isDesktop: "desktop" === deviceType,
7018
+ isWide: "wide" === deviceType,
7019
+ scaleValue: value => util.scaleValue(value),
7020
+ getCurrentDeviceType: () => util.getCurrentDeviceType(),
7021
+ getCurrentScalingFactor: () => util.getCurrentScalingFactor(),
7022
+ isMobileOrSmaller: () => util.isMobileOrSmaller(),
7023
+ isTabletOrSmaller: () => util.isTabletOrSmaller(),
7024
+ isDesktopOrLarger: () => util.isDesktopOrLarger()
7025
+ };
7026
+ }
7027
+
5544
7028
  /**
5545
7029
  * RTL (Right-to-Left) Support Utilities
5546
7030
  *
@@ -5726,6 +7210,32 @@ class RTLManager {
5726
7210
  }
5727
7211
  }
5728
7212
 
7213
+ /**
7214
+ * Create RTL manager instance
7215
+ */ function createRTLManager(config) {
7216
+ return new RTLManager(config);
7217
+ }
7218
+
7219
+ /**
7220
+ * Check if locale is RTL
7221
+ */ function isRTLLocale(locale) {
7222
+ return RTL_LOCALES.has(locale.toLowerCase());
7223
+ }
7224
+
7225
+ /**
7226
+ * Get direction from locale
7227
+ */ function getDirectionFromLocale(locale) {
7228
+ return isRTLLocale(locale) ? "rtl" : "ltr";
7229
+ }
7230
+
7231
+ /**
7232
+ * RTL-aware CSS helper
7233
+ *
7234
+ * Returns appropriate CSS based on direction
7235
+ */ function rtlCSS(ltrCSS, rtlCSS, direction = "ltr") {
7236
+ return "rtl" === direction ? rtlCSS : ltrCSS;
7237
+ }
7238
+
5729
7239
  /**
5730
7240
  * Theme System Exports
5731
7241
  *
@@ -5748,8 +7258,6 @@ class RTLManager {
5748
7258
  // ============================================================================
5749
7259
  // Core Theme Functions
5750
7260
  // ============================================================================
5751
- // Create theme CSS from DesignTokens
5752
- // File saving utilities removed to prevent bundling Node.js modules in browser
5753
7261
  /**
5754
7262
  * Inject theme CSS into DOM
5755
7263
  */ function injectTheme(css, id = "atomix-theme") {
@@ -5762,5 +7270,5 @@ class RTLManager {
5762
7270
  removeCSS(id);
5763
7271
  }
5764
7272
 
5765
- export { DesignTokensCustomizer, RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeValidator, applyCSSVariables, applyComponentTheme, applyTheme, camelToKebab, clearThemes, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, deepMerge, defaultTokens, designTokensToCSSVars, extendTheme, extractComponentName, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, getAllThemes, getCSSVariable, getComponentThemeValue, getTheme, getThemeApplicator, getThemeCount, getThemeIds, hasTheme, injectCSS$1 as injectCSS, injectTheme, isCSSInjected, isDesignTokens, isValidCSSVariableName, mapSCSSTokensToCSSVars, mergeCSSVars, mergeTheme, normalizeThemeTokens, registerTheme, removeCSS, removeCSSVariables, removeTheme, themePropertyToCSSVar, unregisterTheme, useComponentTheme, useHistory, useTheme, useThemeTokens };
7273
+ export { DesignTokensCustomizer, RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeToggle, ThemeValidator, alpha, applyCSSVariables, applyComponentTheme, applyTheme, camelToKebab, clearThemePreference, clearThemes, configToTokens, createPerformanceMonitor, createRTLManager, createResponsiveUtil, createSpacing, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, darken, deepMerge, defaultTokens, designTokensToCSSVars, emphasize, extendTheme, extractComponentName, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, getAllThemes, getCSSVariable, getComponentThemeValue, getContrastRatio, getContrastText, getCurrentTheme, getDirectionFromLocale, getLuminance, getSystemTheme, getTheme, getThemeApplicator, getThemeCount, getThemeIds, hasTheme, hexToRgb$1 as hexToRgb, initializeTheme, injectCSS$1 as injectCSS, injectTheme, isAccessible, isCSSInjected, isDesignTokens, isRTLLocale, isValidCSSVariableName, lighten, listenToSystemTheme, mapSCSSTokensToCSSVars, mergeCSSVars, mergeTheme, mergeTokens, normalizeThemeTokens, omitTokens, overrideTokens, persistTheme, pickTokens, registerTheme, removeCSS, removeCSSVariables, removeTheme, rgbToHex, rtlCSS, switchTheme, themePropertyToCSSVar, toggleTheme, unregisterTheme, useComponentTheme, useHistory, usePerformanceMonitor, useResponsive, useTheme, useThemeSwitcher, useThemeTokens };
5766
7274
  //# sourceMappingURL=theme.js.map