@shohojdhara/atomix 0.5.0 → 0.5.1

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 (76) hide show
  1. package/dist/atomix.css +95 -69
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +1 -1
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +1 -0
  6. package/dist/charts.js +231 -332
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +1 -0
  9. package/dist/core.js +232 -333
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +1 -0
  12. package/dist/forms.js +231 -332
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +11 -2
  15. package/dist/heavy.js +233 -334
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +13 -2
  18. package/dist/index.esm.js +228 -327
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +227 -326
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +11 -1
  25. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  26. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +62 -90
  27. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  28. package/src/components/AtomixGlass/glass-utils.ts +50 -0
  29. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  31. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  32. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  33. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  34. package/src/components/AtomixGlass/stories/types.ts +3 -3
  35. package/src/lib/composables/useAtomixGlass.ts +108 -71
  36. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  37. package/src/lib/constants/components.ts +1 -0
  38. package/src/lib/types/components.ts +1 -0
  39. package/src/lib/utils/displacement-generator.ts +1 -1
  40. package/src/styles/06-components/_components.atomix-glass.scss +158 -97
  41. package/scripts/cli/__tests__/README.md +0 -81
  42. package/scripts/cli/__tests__/basic.test.js +0 -116
  43. package/scripts/cli/__tests__/clean.test.js +0 -278
  44. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  45. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  46. package/scripts/cli/__tests__/generator.test.js +0 -613
  47. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  48. package/scripts/cli/__tests__/integration.test.js +0 -938
  49. package/scripts/cli/__tests__/migrate.test.js +0 -74
  50. package/scripts/cli/__tests__/security.test.js +0 -206
  51. package/scripts/cli/__tests__/test-setup.js +0 -135
  52. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  53. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  54. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  55. package/scripts/cli/__tests__/utils.test.js +0 -165
  56. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +0 -216
  57. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  58. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  59. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  60. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  61. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  62. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  63. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  64. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  65. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  66. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  67. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  68. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  69. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  70. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  71. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  72. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  73. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  74. package/src/components/TypedButton/TypedButton.tsx +0 -39
  75. package/src/components/TypedButton/index.ts +0 -2
  76. package/src/lib/composables/useTypedButton.ts +0 -66
package/dist/core.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
 
3
- import React, { useState, useRef, useEffect, memo, forwardRef, useMemo, useCallback, useId, Children, isValidElement, cloneElement } from "react";
3
+ import React, { useState, useRef, useEffect, memo, forwardRef, useId, useMemo, useCallback, Children, isValidElement, cloneElement } from "react";
4
4
 
5
5
  import * as PhosphorIcons from "@phosphor-icons/react";
6
6
 
@@ -524,6 +524,7 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
524
524
  FILTER_OVERLAY_CLASS: "c-atomix-glass__filter-overlay",
525
525
  FILTER_SHADOW_CLASS: "c-atomix-glass__filter-shadow",
526
526
  CONTENT_CLASS: "c-atomix-glass__content",
527
+ BORDER_BACKDROP_CLASS: "c-atomix-glass__border-backdrop",
527
528
  BORDER_1_CLASS: "c-atomix-glass__border-1",
528
529
  BORDER_2_CLASS: "c-atomix-glass__border-2",
529
530
  HOVER_1_CLASS: "c-atomix-glass__hover-1",
@@ -869,7 +870,7 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
869
870
  default:
870
871
  return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
871
872
  }
872
- }, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
873
+ }, sharedShaderCache = new Map, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
873
874
  style: {
874
875
  position: "absolute",
875
876
  width: "100%",
@@ -1013,24 +1014,17 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1013
1014
  */ GlassFilterComponent.displayName = "GlassFilter";
1014
1015
 
1015
1016
  // Memoize component to prevent unnecessary re-renders
1016
- const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount));
1017
-
1018
- // Module-level counter for deterministic ID generation
1019
- let idCounter = 0;
1020
-
1021
- // Module-level shared shader cache with LRU eviction
1022
- const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
1017
+ const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount)), AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
1023
1018
  x: 0,
1024
1019
  y: 0
1025
1020
  }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
1026
1021
  width: 0,
1027
1022
  height: 0
1028
- }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1,
1023
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1,
1029
1024
  // Phase 1: Animation System props
1030
1025
  shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
1031
- // Generate a stable, deterministic ID for SSR compatibility
1032
- // Use a module-level counter that's consistent across server and client
1033
- const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
1026
+ // React 18 useId — stable, unique, and SSR-safe (no module-level counter)
1027
+ const rawId = useId(), filterId = useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
1034
1028
  // Lazy load shader utilities only when shader mode is needed
1035
1029
  useEffect((() => {
1036
1030
  "shader" === mode ?
@@ -1053,9 +1047,7 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1053
1047
  // Create cache key from size and variant
1054
1048
  const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
1055
1049
  const entry = sharedShaderCache.get(key);
1056
- return entry ? (
1057
- // Update access timestamp for LRU
1058
- entry.timestamp = Date.now(), entry.url) : null;
1050
+ return entry ? (entry.timestamp = Date.now(), entry.url) : null;
1059
1051
  })(cacheKey);
1060
1052
  // Check shared cache first
1061
1053
  if (cachedUrl) return void setShaderMapUrl(cachedUrl);
@@ -1066,29 +1058,24 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1066
1058
  if (shaderUtilsRef.current) try {
1067
1059
  const {ShaderDisplacementGenerator: ShaderDisplacementGenerator, fragmentShaders: fragmentShaders} = shaderUtilsRef.current;
1068
1060
  shaderGeneratorRef.current?.destroy();
1069
- const selectedShader = fragmentShaders[shaderVariant] || fragmentShaders.liquidGlass;
1061
+ const selectedShader = fragmentShaders[shaderVariant] ?? fragmentShaders.liquidGlass;
1070
1062
  shaderGeneratorRef.current = new ShaderDisplacementGenerator({
1071
1063
  width: glassSize.width,
1072
1064
  height: glassSize.height,
1073
1065
  fragment: selectedShader
1074
1066
  }), shaderUpdateTimeoutRef.current = setTimeout((() => {
1075
- const url = shaderGeneratorRef.current?.updateShader() || "";
1067
+ const url = shaderGeneratorRef.current?.updateShader() ?? "";
1076
1068
  url && ((key, url) => {
1077
- // Evict oldest entries if at capacity
1078
1069
  if (sharedShaderCache.size >= 15) {
1079
1070
  const entries = Array.from(sharedShaderCache.entries());
1080
- // Sort by timestamp (oldest first)
1081
- entries.sort(((a, b) => a[1].timestamp - b[1].timestamp));
1082
- // Remove oldest entry
1083
- const oldestEntry = entries[0];
1084
- oldestEntry && sharedShaderCache.delete(oldestEntry[0]);
1071
+ entries.sort(((a, b) => a[1].timestamp - b[1].timestamp));
1072
+ const oldest = entries[0];
1073
+ oldest && sharedShaderCache.delete(oldest[0]);
1085
1074
  }
1086
1075
  sharedShaderCache.set(key, {
1087
1076
  url: url,
1088
1077
  timestamp: Date.now()
1089
- }),
1090
- // Development mode: log cache size
1091
- "undefined" != typeof process && "production" === process.env?.NODE_ENV || sharedShaderCache.size;
1078
+ }), "undefined" != typeof process && "production" === process.env?.NODE_ENV || sharedShaderCache.size;
1092
1079
  })(cacheKey, url), setShaderMapUrl(url);
1093
1080
  }), 100);
1094
1081
  } catch (error) {
@@ -1157,7 +1144,6 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1157
1144
  console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
1158
1145
  }
1159
1146
  }), [ ref, glassSize ]);
1160
- // Pre-calculate static multipliers outside useMemo
1161
1147
  const liquidBlur = useMemo((() => {
1162
1148
  const defaultBlur = {
1163
1149
  baseBlur: blurAmount,
@@ -1226,7 +1212,6 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1226
1212
  }), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
1227
1213
  return jsx("div", {
1228
1214
  ref: el => {
1229
- // Apply force no-transition
1230
1215
  // Handle forwarded ref
1231
1216
  "function" == typeof ref ? ref(el) : ref && (ref.current = el);
1232
1217
  },
@@ -1268,6 +1253,7 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1268
1253
  });
1269
1254
  }));
1270
1255
 
1256
+ // ─── Blur multiplier constants (module-level, never change at runtime) ────────
1271
1257
  AtomixGlassContainer.displayName = "AtomixGlassContainer";
1272
1258
 
1273
1259
  // Singleton instance
@@ -1468,8 +1454,6 @@ class {
1468
1454
  backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
1469
1455
  // Container variables
1470
1456
  const style = containerElement.style;
1471
- style.setProperty("--atomix-glass-container-width", isFixedOrSticky ? `${glassSize.width}` : "100%"),
1472
- style.setProperty("--atomix-glass-container-height", isFixedOrSticky ? `${glassSize.height}` : "100%"),
1473
1457
  style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
1474
1458
  style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
1475
1459
  // Shadows
@@ -1930,47 +1914,55 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1930
1914
  return "undefined" == typeof process || process.env, finalConfig;
1931
1915
  }
1932
1916
  return "undefined" == typeof process || process.env, baseConfig;
1933
- }), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null), handleGlobalMousePosition = useCallback((globalPos => {
1934
- if (externalGlobalMousePosition && externalMouseOffset) return;
1935
- if (effectiveWithoutEffects) return;
1936
- const container = mouseContainer?.current || glassRef.current;
1937
- if (!container) return;
1938
- // Use cached rect if available, otherwise get new one
1939
- let rect = cachedRectRef.current;
1940
- if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
1941
- cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
1942
- const center = calculateElementCenter(rect);
1943
- // Write raw target — the lerp loop will smoothly pursue it
1944
- targetMouseOffsetRef.current = {
1945
- x: (globalPos.x - center.x) / rect.width * 100,
1946
- y: (globalPos.y - center.y) / rect.height * 100
1947
- }, targetGlobalMousePositionRef.current = globalPos;
1948
- }), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects ]), startLerpLoop = useCallback((() => {
1917
+ }), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null), stopLerpLoop = useCallback((() => {
1918
+ lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
1919
+ lerpRafRef.current = null);
1920
+ }), []), startLerpLoop = useCallback((() => {
1949
1921
  if (lerpActiveRef.current) return;
1950
1922
  lerpActiveRef.current = !0;
1951
1923
  const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
1952
1924
  if (!lerpActiveRef.current) return;
1953
- // Add ref validity check to prevent memory leaks
1954
- if (!glassRef.current || !wrapperRef?.current) return void (lerpActiveRef.current = !1);
1925
+ if (!glassRef.current) return void (lerpActiveRef.current = !1);
1955
1926
  const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
1956
1927
  // If we're close enough, snap and park
1957
- if (Math.abs(dx) < .05 && Math.abs(dy) < .05) internalMouseOffsetRef.current = {
1928
+ if (Math.abs(dx) < .01 && Math.abs(dy) < .01) return internalMouseOffsetRef.current = {
1958
1929
  ...tgt
1959
1930
  }, internalGlobalMousePositionRef.current = {
1960
1931
  ...targetGlobalMousePositionRef.current
1961
- }; else {
1962
- internalMouseOffsetRef.current = {
1963
- x: lerp$1(cur.x, tgt.x, LERP_T),
1964
- y: lerp$1(cur.y, tgt.y, LERP_T)
1965
- };
1966
- const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
1967
- internalGlobalMousePositionRef.current = {
1968
- x: lerp$1(curG.x, tgtG.x, LERP_T),
1969
- y: lerp$1(curG.y, tgtG.y, LERP_T)
1970
- };
1971
- }
1972
- // Imperative style update with the smoothed values
1973
- updateAtomixGlassStyles(wrapperRef.current, glassRef.current, {
1932
+ },
1933
+ // Final update and stop
1934
+ updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1935
+ mouseOffset: internalMouseOffsetRef.current,
1936
+ globalMousePosition: internalGlobalMousePositionRef.current,
1937
+ glassSize: glassSize,
1938
+ isHovered: isHovered,
1939
+ isActive: isActive,
1940
+ isOverLight: overLightConfig.isOverLight,
1941
+ baseOverLightConfig: overLightConfig,
1942
+ effectiveBorderRadius: effectiveBorderRadius,
1943
+ effectiveWithoutEffects: effectiveWithoutEffects,
1944
+ effectiveReducedMotion: effectiveReducedMotion,
1945
+ elasticity: elasticity,
1946
+ directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1947
+ onClick: onClick,
1948
+ withLiquidBlur: withLiquidBlur,
1949
+ blurAmount: blurAmount,
1950
+ saturation: saturation,
1951
+ padding: padding,
1952
+ isFixedOrSticky: isFixedOrSticky
1953
+ }), void stopLerpLoop();
1954
+ // Smooth step
1955
+ internalMouseOffsetRef.current = {
1956
+ x: lerp$1(cur.x, tgt.x, LERP_T),
1957
+ y: lerp$1(cur.y, tgt.y, LERP_T)
1958
+ };
1959
+ const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
1960
+ internalGlobalMousePositionRef.current = {
1961
+ x: lerp$1(curG.x, tgtG.x, LERP_T),
1962
+ y: lerp$1(curG.y, tgtG.y, LERP_T)
1963
+ },
1964
+ // Imperative style update
1965
+ updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1974
1966
  mouseOffset: internalMouseOffsetRef.current,
1975
1967
  globalMousePosition: internalGlobalMousePositionRef.current,
1976
1968
  glassSize: glassSize,
@@ -1993,10 +1985,24 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1993
1985
  };
1994
1986
  // 0.08 – lower = more viscous
1995
1987
  lerpRafRef.current = requestAnimationFrame(tick);
1996
- }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky ]), stopLerpLoop = useCallback((() => {
1997
- lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
1998
- lerpRafRef.current = null);
1999
- }), []);
1988
+ }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
1989
+ if (externalGlobalMousePosition && externalMouseOffset) return;
1990
+ if (effectiveWithoutEffects) return;
1991
+ const container = mouseContainer?.current || glassRef.current;
1992
+ if (!container) return;
1993
+ // Use cached rect if available, otherwise get new one
1994
+ let rect = cachedRectRef.current;
1995
+ if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
1996
+ cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
1997
+ const center = calculateElementCenter(rect);
1998
+ // Write raw target — the lerp loop will smoothly pursue it
1999
+ targetMouseOffsetRef.current = {
2000
+ x: (globalPos.x - center.x) / rect.width * 100,
2001
+ y: (globalPos.y - center.y) / rect.height * 100
2002
+ }, targetGlobalMousePositionRef.current = globalPos,
2003
+ // Ensure the lerp loop is running to smoothly chase the new target
2004
+ lerpActiveRef.current || startLerpLoop();
2005
+ }), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects, startLerpLoop ]);
2000
2006
  /**
2001
2007
  * Validate and clamp a numeric config value
2002
2008
  */
@@ -2005,7 +2011,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2005
2011
  if (externalGlobalMousePosition && externalMouseOffset) return;
2006
2012
  if (effectiveWithoutEffects) return;
2007
2013
  const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
2008
- // Start the lerp loop — it will smoothly chase the target
2014
+ // Initial start
2009
2015
  startLerpLoop();
2010
2016
  const container = mouseContainer?.current || glassRef.current;
2011
2017
  let resizeObserver = null;
@@ -2018,7 +2024,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2018
2024
  unsubscribe(), stopLerpLoop(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
2019
2025
  updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
2020
2026
  };
2021
- }), [ handleGlobalMousePosition, startLerpLoop, stopLerpLoop, mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects ]),
2027
+ }), [ externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects, handleGlobalMousePosition, startLerpLoop, stopLerpLoop, mouseContainer, glassRef ]),
2022
2028
  // Also call updateStyles on other state changes (hover, active, etc)
2023
2029
  useEffect((() => {
2024
2030
  updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
@@ -2120,184 +2126,147 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2120
2126
  *
2121
2127
  * @returns Performance tier classification
2122
2128
  */
2129
+ /** Map an FPS value to a semantic color token string. */
2130
+ const getQualityColor = quality => {
2131
+ switch (quality) {
2132
+ case "high":
2133
+ return "var(--atomix-color-success, #4ade80)";
2134
+
2135
+ case "medium":
2136
+ return "var(--atomix-color-warning, #fbbf24)";
2137
+
2138
+ case "low":
2139
+ return "var(--atomix-color-danger, #ef4444)";
2140
+
2141
+ default:
2142
+ return "#9ca3af";
2143
+ }
2144
+ }, getFpsLabel = fps => fps >= 58 ? "Optimal" : fps >= 45 ? "Warning" : "Critical";
2145
+
2146
+ /** Map a quality level string to a semantic color token string. */
2147
+ // Inject keyframes once
2148
+ if ("undefined" != typeof document) {
2149
+ const styleId = "perf-dashboard-keyframes";
2150
+ if (!document.getElementById(styleId)) {
2151
+ const styleEl = document.createElement("style");
2152
+ styleEl.id = styleId, styleEl.textContent = "\n@keyframes perf-dashboard-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n",
2153
+ document.head.appendChild(styleEl);
2154
+ }
2155
+ }
2156
+
2123
2157
  /**
2124
- * PerformanceDashboard - Real-time performance monitoring overlay
2158
+ * PerformanceDashboard - Real-time performance monitoring overlay.
2125
2159
  *
2126
- * Displays:
2127
- * - Current FPS with color coding
2128
- * - Frame time statistics
2129
- * - Quality level indicator
2130
- * - GPU memory usage (if available)
2131
- * - Auto-scaling status
2132
- */
2133
- const PerformanceDashboard = ({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
2134
- // Get color for FPS display
2135
- const getFpsColor = fps => fps >= 58 ? "#4ade80" : // Green - good
2136
- fps >= 45 ? "#fbbf24" : "#ef4444" // Red - critical
2137
- , dashboardStyle = useMemo((() => ({
2138
- position: "fixed",
2139
- top: "16px",
2140
- right: "16px",
2141
- padding: "12px 16px",
2142
- backgroundColor: "rgba(17, 24, 39, 0.95)",
2143
- borderRadius: "8px",
2144
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
2145
- fontFamily: "monospace",
2146
- fontSize: "12px",
2147
- color: "#fff",
2148
- zIndex: 9999,
2149
- minWidth: "200px",
2150
- backdropFilter: "blur(8px)",
2151
- border: "1px solid rgba(255, 255, 255, 0.1)",
2152
- transition: "opacity 0.3s ease",
2153
- opacity: isVisible ? 1 : 0,
2154
- pointerEvents: isVisible ? "auto" : "none"
2155
- })), [ isVisible ]), headerStyle = useMemo((() => ({
2156
- display: "flex",
2157
- justifyContent: "space-between",
2158
- alignItems: "center",
2159
- marginBottom: "8px",
2160
- paddingBottom: "8px",
2161
- borderBottom: "1px solid rgba(255, 255, 255, 0.1)"
2162
- })), []), titleStyle = useMemo((() => ({
2163
- fontWeight: "bold",
2164
- fontSize: "13px",
2165
- color: "#fff"
2166
- })), []), closeButtonStyle = useMemo((() => ({
2167
- background: "transparent",
2168
- border: "none",
2169
- color: "#9ca3af",
2170
- cursor: "pointer",
2171
- fontSize: "16px",
2172
- padding: "0",
2173
- lineHeight: "1"
2174
- })), []), metricRowStyle = useMemo((() => ({
2175
- display: "flex",
2176
- justifyContent: "space-between",
2177
- alignItems: "center",
2178
- marginBottom: "6px"
2179
- })), []), labelStyle = useMemo((() => ({
2180
- color: "#9ca3af",
2181
- marginRight: "12px"
2182
- })), []), valueStyle = useMemo((() => ({
2183
- fontWeight: "bold"
2184
- })), []);
2185
- // Get quality level badge color
2186
- return isVisible ? jsxs("div", {
2187
- style: dashboardStyle,
2160
+ * Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
2161
+ * Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
2162
+ */ const PerformanceDashboard = memo((({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
2163
+ if (!isVisible) return null;
2164
+ const fpsColor = (fps = metrics.fps) >= 58 ? "var(--atomix-color-success, #4ade80)" : fps >= 45 ? "var(--atomix-color-warning, #fbbf24)" : "var(--atomix-color-danger, #ef4444)";
2165
+ var fps;
2166
+ const isCritical = metrics.fps < 45;
2167
+ return jsxs("div", {
2168
+ className: "c-perf-dashboard u-position-fixed u-top-4 u-end-4 u-p-3 u-px-4 u-text-xs u-font-mono u-text-white u-rounded-md u-border u-border-white-alpha-10 u-shadow-lg",
2169
+ style: {
2170
+ zIndex: 9999,
2171
+ minWidth: "12.5rem",
2172
+ // 200px
2173
+ backgroundColor: "rgba(17, 24, 39, 0.95)",
2174
+ backdropFilter: "blur(8px)",
2175
+ transition: "opacity 0.3s ease"
2176
+ },
2188
2177
  children: [ jsxs("div", {
2189
- style: headerStyle,
2178
+ className: "u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10",
2190
2179
  children: [ jsx("span", {
2191
- style: titleStyle,
2180
+ className: "u-text-sm u-font-bold u-text-white",
2192
2181
  children: "Performance Monitor"
2193
2182
  }), onClose && jsx("button", {
2194
- style: closeButtonStyle,
2183
+ className: "u-bg-transparent u-border-none u-p-0 u-line-height-1 u-text-base u-text-gray-400 u-cursor-pointer hover:u-text-white",
2195
2184
  onClick: onClose,
2196
2185
  "aria-label": "Close performance dashboard",
2186
+ style: {
2187
+ transition: "color 0.2s ease"
2188
+ },
2197
2189
  children: "×"
2198
2190
  }) ]
2199
2191
  }), jsxs("div", {
2200
- style: metricRowStyle,
2192
+ className: "u-flex u-items-center u-justify-between u-mb-1-5",
2201
2193
  children: [ jsx("span", {
2202
- style: labelStyle,
2194
+ className: "u-text-gray-400 u-me-3",
2203
2195
  children: "FPS"
2204
2196
  }), jsx("span", {
2197
+ className: "u-font-bold",
2205
2198
  style: {
2206
- ...valueStyle,
2207
- color: getFpsColor(metrics.fps)
2199
+ color: fpsColor
2208
2200
  },
2209
2201
  children: Math.round(metrics.fps)
2210
2202
  }) ]
2211
2203
  }), jsxs("div", {
2212
- style: metricRowStyle,
2204
+ className: "u-flex u-items-center u-justify-between u-mb-1-5",
2213
2205
  children: [ jsx("span", {
2214
- style: labelStyle,
2206
+ className: "u-text-gray-400 u-me-3",
2215
2207
  children: "Frame Time"
2216
2208
  }), jsxs("span", {
2217
- style: valueStyle,
2209
+ className: "u-font-bold",
2218
2210
  children: [ metrics.frameTime.toFixed(2), "ms" ]
2219
2211
  }) ]
2220
2212
  }), jsxs("div", {
2221
- style: metricRowStyle,
2213
+ className: "u-flex u-items-center u-justify-between u-mb-1-5",
2222
2214
  children: [ jsx("span", {
2223
- style: labelStyle,
2215
+ className: "u-text-gray-400 u-me-3",
2224
2216
  children: "Quality"
2225
2217
  }), jsx("span", {
2218
+ className: "u-font-bold u-text-uppercase",
2226
2219
  style: {
2227
- ...valueStyle,
2228
- color: (quality => {
2229
- switch (quality) {
2230
- case "high":
2231
- return "#4ade80";
2232
-
2233
- case "medium":
2234
- return "#fbbf24";
2235
-
2236
- case "low":
2237
- return "#ef4444";
2238
-
2239
- default:
2240
- return "#9ca3af";
2241
- }
2242
- })(metrics.qualityLevel),
2243
- textTransform: "uppercase",
2244
- fontSize: "11px"
2220
+ fontSize: "0.6875rem",
2221
+ // 11px
2222
+ color: getQualityColor(metrics.qualityLevel)
2245
2223
  },
2246
2224
  children: metrics.qualityLevel
2247
2225
  }) ]
2248
2226
  }), metrics.gpuMemory && jsxs("div", {
2249
- style: metricRowStyle,
2227
+ className: "u-flex u-items-center u-justify-between u-mb-1-5",
2250
2228
  children: [ jsx("span", {
2251
- style: labelStyle,
2229
+ className: "u-text-gray-400 u-me-3",
2252
2230
  children: "GPU Memory"
2253
2231
  }), jsxs("span", {
2254
- style: valueStyle,
2232
+ className: "u-font-bold",
2255
2233
  children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
2256
2234
  }) ]
2257
2235
  }), metrics.isAutoScaling && jsx("div", {
2236
+ className: "u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center",
2258
2237
  style: {
2259
- marginTop: "8px",
2260
- paddingTop: "8px",
2261
- borderTop: "1px solid rgba(255, 255, 255, 0.1)",
2262
- fontSize: "10px",
2263
- color: "#6b7280",
2264
- textAlign: "center"
2238
+ fontSize: "0.625rem",
2239
+ // 10px
2240
+ color: "#6b7280"
2265
2241
  },
2266
2242
  children: "Auto-scaling active"
2267
2243
  }), jsxs("div", {
2268
- style: {
2269
- marginTop: "8px",
2270
- paddingTop: "8px",
2271
- borderTop: "1px solid rgba(255, 255, 255, 0.1)",
2272
- display: "flex",
2273
- alignItems: "center",
2274
- gap: "6px"
2275
- },
2244
+ className: "u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10",
2276
2245
  children: [ jsx("div", {
2246
+ className: "u-rounded-full",
2277
2247
  style: {
2278
- width: "8px",
2279
- height: "8px",
2280
- borderRadius: "50%",
2281
- backgroundColor: getFpsColor(metrics.fps),
2282
- animation: metrics.fps < 45 ? "pulse 1s infinite" : "none"
2248
+ width: "0.5rem",
2249
+ height: "0.5rem",
2250
+ flexShrink: 0,
2251
+ backgroundColor: fpsColor,
2252
+ ...isCritical && {
2253
+ animation: "perf-dashboard-pulse 1s infinite"
2254
+ }
2283
2255
  }
2284
2256
  }), jsx("span", {
2257
+ className: "u-text-xs",
2285
2258
  style: {
2286
- fontSize: "10px",
2287
- color: metrics.fps >= 58 ? "#4ade80" : metrics.fps >= 45 ? "#fbbf24" : "#ef4444"
2259
+ fontSize: "0.625rem",
2260
+ // 10px
2261
+ color: fpsColor
2288
2262
  },
2289
- children: metrics.fps >= 58 ? "Optimal" : metrics.fps >= 45 ? "Warning" : "Critical"
2263
+ children: getFpsLabel(metrics.fps)
2290
2264
  }) ]
2291
2265
  }) ]
2292
- }) : null;
2293
- };
2266
+ });
2267
+ }));
2294
2268
 
2295
- // Add pulse animation for critical FPS
2296
- if ("undefined" != typeof document) {
2297
- const styleSheet = document.createElement("style");
2298
- styleSheet.textContent = "\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n ",
2299
- document.head.appendChild(styleSheet);
2300
- }
2269
+ PerformanceDashboard.displayName = "PerformanceDashboard";
2301
2270
 
2302
2271
  /**
2303
2272
  * Mobile optimization presets
@@ -2308,7 +2277,8 @@ if ("undefined" != typeof document) {
2308
2277
  /**
2309
2278
  * Performance preset - Maximum FPS, reduced quality
2310
2279
  * Best for low-end devices or when battery saving is priority
2311
- */ const PERFORMANCE_PRESET = {
2280
+ */
2281
+ const PERFORMANCE_PRESET = {
2312
2282
  distortionOctaves: 2,
2313
2283
  // Minimal FBM layers
2314
2284
  displacementScale: 50,
@@ -2417,95 +2387,21 @@ if ("undefined" != typeof document) {
2417
2387
  saturation: 70
2418
2388
  }
2419
2389
  }
2420
- };
2421
-
2422
- /**
2423
- * Balanced preset - Good quality with reasonable performance
2424
- * Default preset for most mobile devices
2425
- */
2426
- /**
2427
- * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
2428
- *
2429
- * Features:
2430
- * - Hardware-accelerated glass effects with SVG filters
2431
- * - Mouse-responsive liquid distortion
2432
- * - Dynamic border-radius extraction from children CSS properties
2433
- * - Automatic light/dark theme detection via overLight prop
2434
- * - Accessibility and performance optimizations
2435
- * - Multiple displacement modes (standard, polar, prominent, shader)
2436
- * - Design token integration for consistent theming
2437
- * - Focus ring support for keyboard navigation
2438
- * - Responsive breakpoints for mobile optimization
2439
- * - Enhanced ARIA attributes for screen readers
2440
- * - Time-based animation system with FBM distortion
2441
- * - Device preset optimization for performance/quality balance
2442
- *
2443
- * Design System Compliance:
2444
- * - Uses design tokens for opacity, spacing, and colors
2445
- * - Follows BEM methodology for class naming
2446
- * - Implements focus-ring mixin for accessibility
2447
- * - Supports reduced motion and high contrast preferences
2448
- *
2449
- * @example
2450
- * // Basic usage with dynamic border-radius extraction
2451
- * <AtomixGlass>
2452
- * <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
2453
- * </AtomixGlass>
2454
- *
2455
- * @example
2456
- * // Manual border-radius override
2457
- * <AtomixGlass borderRadius={20}>
2458
- * <div>Content with 20px glass radius</div>
2459
- * </AtomixGlass>
2460
- *
2461
- * @example
2462
- * // Interactive glass with click handler
2463
- * <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
2464
- * <div>Clickable content</div>
2465
- * </AtomixGlass>
2466
- *
2467
- * @example
2468
- * // OverLight - Boolean mode (explicit control)
2469
- * <AtomixGlass overLight={true}>
2470
- * <div>Content on light background</div>
2471
- * </AtomixGlass>
2472
- *
2473
- * @example
2474
- * // OverLight - Auto-detection mode
2475
- * <AtomixGlass overLight="auto">
2476
- * <div>Content with auto-detected background</div>
2477
- * </AtomixGlass>
2478
- *
2479
- * @example
2480
- * // OverLight - Object config with custom settings
2481
- * <AtomixGlass
2482
- * overLight={{
2483
- * threshold: 0.8,
2484
- * opacity: 0.6,
2485
- * contrast: 1.8,
2486
- * brightness: 1.0,
2487
- * saturationBoost: 1.5
2488
- * }}
2489
- * >
2490
- * <div>Content with custom overLight config</div>
2491
- * </AtomixGlass>
2492
- *
2493
- * @example
2494
- * // Debug mode for overLight detection
2495
- * <AtomixGlass overLight="auto" debugOverLight={true}>
2496
- * <div>Content with debug logging enabled</div>
2497
- * </AtomixGlass>
2498
- *
2499
- * @example
2500
- * // Performance-optimized for mobile devices
2501
- * <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
2502
- * <div>Mobile-optimized glass effect</div>
2503
- * </AtomixGlass>
2504
- */
2505
- function AtomixGlass({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, ...rest}) {
2506
- const glassRef = useRef(null), contentRef = useRef(null), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, applyTimeBasedDistortion: applyTimeBasedDistortion, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
2390
+ }, AtomixGlassInner = forwardRef((function({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, isFixedOrSticky: propsIsFixedOrSticky, ...rest}, ref) {
2391
+ const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useMemo((() =>
2392
+ // Helper to merge refs
2393
+ function(...refs) {
2394
+ return node => {
2395
+ refs.forEach((ref => {
2396
+ "function" == typeof ref ? ref(node) : null != ref && (ref.current = node);
2397
+ }));
2398
+ };
2399
+ }
2400
+ // Internal implementation with forwardRef
2401
+ (ref, internalWrapperRef)), [ ref ]), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = propsIsFixedOrSticky || "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
2507
2402
  glassRef: glassRef,
2508
2403
  contentRef: contentRef,
2404
+ wrapperRef: internalWrapperRef,
2509
2405
  borderRadius: borderRadius,
2510
2406
  globalMousePosition: externalGlobalMousePosition,
2511
2407
  mouseOffset: externalMouseOffset,
@@ -2534,7 +2430,6 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
2534
2430
  distortionGain: distortionGain,
2535
2431
  distortionQuality: distortionQuality
2536
2432
  });
2537
- // Re-calculate only when devicePreset changes
2538
2433
  // Responsive breakpoint system - automatically adjusts parameters based on viewport
2539
2434
  !
2540
2435
  /**
@@ -2727,11 +2622,10 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
2727
2622
  },
2728
2623
  breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
2729
2624
  enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
2730
- // Enable unless disabled
2731
2625
  debug: !1
2732
2626
  });
2733
2627
  // Performance monitoring - tracks FPS, frame time, memory usage
2734
- const {metrics: performanceMetrics, recommendedQuality: recommendedQuality, isUnderperforming: isUnderperforming, setQualityLevel: setQualityLevel, toggleMonitoring: toggleMonitoring} =
2628
+ const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} =
2735
2629
  /**
2736
2630
  * Performance Monitor Hook
2737
2631
  *
@@ -2895,17 +2789,17 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
2895
2789
  toggleMonitoring: toggleMonitoring
2896
2790
  };
2897
2791
  }({
2898
- enabled: !1,
2899
- // We'll toggle manually based on prop
2792
+ enabled: debugPerformance,
2793
+ // Enable when debugPerformance is true
2900
2794
  debug: !1,
2901
2795
  showOverlay: !1
2902
2796
  });
2903
- // Auto-start performance monitoring if enabled (only in development)
2797
+ // Auto-start performance monitoring when debugPerformance is enabled
2904
2798
  React.useEffect((() => {
2905
- "development" === process.env.NODE_ENV && window?.enablePerformanceMonitoring && toggleMonitoring();
2799
+ debugPerformance && toggleMonitoring();
2906
2800
  // eslint-disable-next-line react-hooks/exhaustive-deps
2907
- }), []);
2908
- // Only run once on mount
2801
+ }), [ debugPerformance ]);
2802
+ // Re-run when debugPerformance changes
2909
2803
  const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
2910
2804
  if (!isFixedOrSticky) return {};
2911
2805
  const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
@@ -2933,34 +2827,30 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
2933
2827
  if (isFixedOrSticky) {
2934
2828
  const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
2935
2829
  return {
2936
- ...visualStyle,
2937
- ...!effectiveWithoutEffects && {
2938
- transform: transformStyle
2939
- }
2830
+ ...visualStyle
2940
2831
  };
2941
2832
  }
2942
2833
  return {
2943
- ...restStyle,
2944
- ...!effectiveWithoutEffects && {
2945
- transform: transformStyle
2946
- }
2834
+ ...restStyle
2947
2835
  };
2948
- }), [ isFixedOrSticky, restStyle, effectiveWithoutEffects, transformStyle ]);
2836
+ }), [ isFixedOrSticky, restStyle ]);
2949
2837
  // Build className with state modifiers
2950
2838
  const componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
2951
2839
  position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
2952
- top: isFixedOrSticky ? 0 : restStyle.top || 0,
2953
- left: isFixedOrSticky ? 0 : restStyle.left || 0
2954
- })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left ]), adjustedSize = useMemo((() => {
2840
+ top: isFixedOrSticky ? restStyle.top ?? 0 : 0,
2841
+ left: isFixedOrSticky ? restStyle.left ?? 0 : 0,
2842
+ right: isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
2843
+ bottom: isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto"
2844
+ })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), adjustedSize = useMemo((() => {
2955
2845
  // Keep a reference to positionStyles to avoid unused-variable lint,
2956
2846
  // but sizing is driven by explicit width/height or measured size.
2957
2847
  positionStyles.position;
2958
- const resolveLength = (value, measured) => void 0 !== value ? "number" == typeof value ? `${value}px` : value : measured > 0 ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
2848
+ const resolveLength = (value, measured) => void 0 !== value && isFixedOrSticky ? "number" == typeof value ? `${value}px` : value : measured > 0 && isFixedOrSticky ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
2959
2849
  return {
2960
2850
  width: resolveLength(effectiveWidth, glassSize.width),
2961
2851
  height: resolveLength(effectiveHeight, glassSize.height)
2962
2852
  };
2963
- }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height ]), gradientValues = useMemo((() => {
2853
+ }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height, isFixedOrSticky ]), gradientValues = useMemo((() => {
2964
2854
  const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
2965
2855
  return {
2966
2856
  borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
@@ -2990,33 +2880,32 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
2990
2880
  absMx: absMx,
2991
2881
  absMy: absMy
2992
2882
  };
2993
- }), [ mouseOffset.x, mouseOffset.y ]), opacityValues = useMemo((() => {
2994
- const overLightOpacity = overLightConfig.opacity;
2995
- return {
2996
- hover1: isHovered || isActive ? .5 : 0,
2997
- hover2: isActive ? .5 : 0,
2998
- hover3: isHovered ? .4 : isActive ? .8 : 0,
2999
- base: isOverLight ? overLightOpacity || .4 : 0,
3000
- over: isOverLight ? 1.1 * (overLightOpacity || .4) : 0
3001
- };
3002
- }), [ isHovered, isActive, isOverLight, overLightConfig.opacity ]), glassVars = useMemo((() => {
3003
- const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, {borderGradientAngle: borderGradientAngle, borderStop1: borderStop1, borderStop2: borderStop2, borderOpacities: borderOpacities, hoverPositions: hoverPositions, basePosition: basePosition, mx: mx, my: my, absMx: absMx, absMy: absMy} = gradientValues, configBorderOpacity = overLightConfig?.borderOpacity ?? 1;
2883
+ }), [ mouseOffset.x, mouseOffset.y ]), clampedOverLightOpacity = Math.max(0, Math.min(1, overLightConfig?.opacity ?? .4)), clampedBorderOpacity = Math.max(0, Math.min(1, overLightConfig?.borderOpacity ?? 1)), opacityValues = useMemo((() => ({
2884
+ hover1: isHovered || isActive ? .5 : 0,
2885
+ hover2: isActive ? .5 : 0,
2886
+ hover3: isHovered ? .4 : isActive ? .8 : 0,
2887
+ base: isOverLight ? clampedOverLightOpacity || .4 : 0,
2888
+ over: isOverLight ? 1.1 * (clampedOverLightOpacity || .4) : 0
2889
+ })), [ isHovered, isActive, isOverLight, clampedOverLightOpacity ]), glassVars = useMemo((() => {
2890
+ const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, {borderGradientAngle: borderGradientAngle, borderStop1: borderStop1, borderStop2: borderStop2, borderOpacities: borderOpacities, hoverPositions: hoverPositions, basePosition: basePosition, mx: mx, my: my, absMx: absMx, absMy: absMy} = gradientValues;
3004
2891
  return {
3005
2892
  ...void 0 !== customZIndex && {
3006
2893
  "--atomix-glass-base-z-index": customZIndex
3007
2894
  },
3008
2895
  "--atomix-glass-radius": `${effectiveBorderRadius}px`,
3009
2896
  "--atomix-glass-transform": transformStyle || "none",
3010
- // Internal decorative layers are positioned relative to the root;
3011
- "--atomix-glass-position": rootLayoutStyle.position,
3012
- "--atomix-glass-top": `${isFixedOrSticky ? rootLayoutStyle.top : 0}px`,
3013
- "--atomix-glass-left": `${isFixedOrSticky ? rootLayoutStyle.left : 0}px`,
2897
+ "--atomix-glass-container-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
2898
+ "--atomix-glass-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
2899
+ "--atomix-glass-top": `${isFixedOrSticky ? restStyle.top ?? 0 : 0}px`,
2900
+ "--atomix-glass-left": `${isFixedOrSticky ? restStyle.left ?? 0 : 0}px`,
2901
+ "--atomix-glass-right": isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
2902
+ "--atomix-glass-bottom": isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto",
3014
2903
  "--atomix-glass-width": adjustedSize.width,
3015
2904
  "--atomix-glass-height": adjustedSize.height,
3016
- "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
2905
+ "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.125rem)",
3017
2906
  "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
3018
- "--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
3019
- "--atomix-glass-border-gradient-2": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
2907
+ "--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
2908
+ "--atomix-glass-border-gradient-2": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
3020
2909
  "--atomix-glass-hover-1-opacity": opacityValues.hover1,
3021
2910
  "--atomix-glass-hover-1-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`,
3022
2911
  "--atomix-glass-hover-2-opacity": opacityValues.hover2,
@@ -3030,13 +2919,14 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
3030
2919
  "--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
3031
2920
  "--atomix-glass-overlay-highlight-bg": `radial-gradient(circle at ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_X}% ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_Y}%, rgba(255, 255, 255, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.WHITE_OPACITY}) 0%, transparent ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.STOP}%)`
3032
2921
  };
3033
- }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, overLightConfig.borderOpacity, customZIndex, rootLayoutStyle, isFixedOrSticky ]), renderBackgroundLayer = layerType => jsx("div", {
2922
+ }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, clampedBorderOpacity, customZIndex, isFixedOrSticky, positionStyles.position, rootLayoutStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), renderBackgroundLayer = layerType => jsx("div", {
3034
2923
  className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, "dark" === layerType ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" ")
3035
2924
  });
3036
2925
  // Calculate position and size styles for internal layers
3037
2926
  // When root is fixed/sticky, internal layers use absolute (relative to root)
3038
2927
  return jsxs("div", {
3039
2928
  ...rest,
2929
+ ref: mergedRef,
3040
2930
  className: componentClassName,
3041
2931
  style: {
3042
2932
  ...glassVars
@@ -3046,17 +2936,14 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
3046
2936
  "aria-label": ariaLabel,
3047
2937
  "aria-describedby": ariaDescribedBy,
3048
2938
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
3049
- "aria-pressed": !(!onClick || !isActive) || !onClick && void 0,
2939
+ "aria-pressed": void 0,
3050
2940
  onKeyDown: onClick ? handleKeyDown : void 0,
3051
2941
  children: [ jsx(AtomixGlassContainer, {
3052
2942
  ref: glassRef,
3053
2943
  contentRef: contentRef,
3054
2944
  className: className,
3055
2945
  style: {
3056
- ...restStyle,
3057
- ...!isFixedOrSticky && {
3058
- position: "relative"
3059
- }
2946
+ ...restStyle
3060
2947
  },
3061
2948
  borderRadius: effectiveBorderRadius,
3062
2949
  displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
@@ -3092,6 +2979,7 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
3092
2979
  effectiveReducedMotion: effectiveReducedMotion,
3093
2980
  shaderVariant: shaderVariant,
3094
2981
  withLiquidBlur: withLiquidBlur,
2982
+ isFixedOrSticky: isFixedOrSticky,
3095
2983
  // Phase 1: Animation System props
3096
2984
  shaderTime: getShaderTime(),
3097
2985
  withTimeAnimation: withTimeAnimation,
@@ -3120,6 +3008,8 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
3120
3008
  }) ]
3121
3009
  }), withBorder && jsxs(Fragment, {
3122
3010
  children: [ jsx("span", {
3011
+ className: ATOMIX_GLASS.BORDER_BACKDROP_CLASS
3012
+ }), jsx("span", {
3123
3013
  className: ATOMIX_GLASS.BORDER_1_CLASS
3124
3014
  }), jsx("span", {
3125
3015
  className: ATOMIX_GLASS.BORDER_2_CLASS
@@ -3130,10 +3020,18 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
3130
3020
  onClose: () => {}
3131
3021
  }) ]
3132
3022
  });
3133
- }
3023
+ }));
3134
3024
 
3135
- // Default icon
3136
- const DefaultIcon = () => jsx("i", {
3025
+ /**
3026
+ * Balanced preset - Good quality with reasonable performance
3027
+ * Default preset for most mobile devices
3028
+ */ AtomixGlassInner.displayName = "AtomixGlass";
3029
+
3030
+ /**
3031
+ * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
3032
+ * Ref is forwarded to the root `<div>` element.
3033
+ */
3034
+ const AtomixGlass = memo(AtomixGlassInner), DefaultIcon = () => jsx("i", {
3137
3035
  className: "c-accordion__icon",
3138
3036
  "aria-hidden": "true",
3139
3037
  children: jsx("svg", {
@@ -3167,6 +3065,7 @@ const DefaultIcon = () => jsx("i", {
3167
3065
  });
3168
3066
  }));
3169
3067
 
3068
+ // Default icon
3170
3069
  AccordionHeader.displayName = "AccordionHeader";
3171
3070
 
3172
3071
  const AccordionBody = forwardRef((({children: children, className: className = "", panelRef: panelRef, contentRef: contentRef, ...props}, ref) => {