@shohojdhara/atomix 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/atomix.css +117 -38
  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/atomix.umd.js +1 -1
  6. package/dist/atomix.umd.js.map +1 -1
  7. package/dist/atomix.umd.min.js +1 -1
  8. package/dist/charts.d.ts +30 -1
  9. package/dist/charts.js +566 -597
  10. package/dist/charts.js.map +1 -1
  11. package/dist/core.d.ts +30 -1
  12. package/dist/core.js +600 -624
  13. package/dist/core.js.map +1 -1
  14. package/dist/forms.d.ts +30 -1
  15. package/dist/forms.js +1122 -1163
  16. package/dist/forms.js.map +1 -1
  17. package/dist/heavy.d.ts +31 -89
  18. package/dist/heavy.js +1015 -1045
  19. package/dist/heavy.js.map +1 -1
  20. package/dist/index.d.ts +378 -104
  21. package/dist/index.esm.js +10959 -10837
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/index.js +10935 -10812
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.min.js +1 -1
  26. package/dist/index.min.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/components/Accordion/Accordion.tsx +2 -5
  29. package/src/components/AtomixGlass/AtomixGlass.test.tsx +14 -16
  30. package/src/components/AtomixGlass/AtomixGlass.tsx +137 -355
  31. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +32 -249
  32. package/src/components/AtomixGlass/GlassFilter.tsx +62 -68
  33. package/src/components/AtomixGlass/README.md +2 -1
  34. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +19 -18
  35. package/src/components/AtomixGlass/glass-border-styles.test.ts +58 -0
  36. package/src/components/AtomixGlass/glass-border-styles.ts +136 -0
  37. package/src/components/AtomixGlass/glass-utils.ts +411 -6
  38. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +158 -537
  39. package/src/components/AtomixGlass/stories/Border.stories.tsx +149 -0
  40. package/src/components/AtomixGlass/stories/Examples.stories.tsx +229 -89
  41. package/src/components/AtomixGlass/stories/Playground.stories.tsx +29 -340
  42. package/src/components/AtomixGlass/stories/argTypes.ts +30 -13
  43. package/src/components/AtomixGlass/stories/premium-presets.ts +206 -0
  44. package/src/components/AtomixGlass/stories/shared-components.tsx +52 -8
  45. package/src/components/Badge/Badge.tsx +4 -4
  46. package/src/components/Button/Button.tsx +2 -6
  47. package/src/components/Callout/Callout.test.tsx +4 -3
  48. package/src/components/Callout/Callout.tsx +2 -5
  49. package/src/components/Dropdown/Dropdown.tsx +3 -7
  50. package/src/components/Form/Checkbox.tsx +2 -8
  51. package/src/components/Form/Input.tsx +2 -9
  52. package/src/components/Form/Radio.tsx +2 -9
  53. package/src/components/Form/Select.tsx +2 -7
  54. package/src/components/Form/Textarea.tsx +2 -9
  55. package/src/components/Messages/Messages.tsx +2 -8
  56. package/src/components/Modal/Modal.tsx +4 -5
  57. package/src/components/Navigation/Nav/Nav.tsx +2 -6
  58. package/src/components/Navigation/Navbar/Navbar.tsx +2 -9
  59. package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -6
  60. package/src/components/Pagination/Pagination.tsx +2 -10
  61. package/src/components/Popover/Popover.tsx +2 -9
  62. package/src/components/Progress/Progress.tsx +2 -7
  63. package/src/components/Rating/Rating.tsx +2 -10
  64. package/src/components/Spinner/Spinner.tsx +2 -7
  65. package/src/components/Steps/Steps.tsx +2 -10
  66. package/src/components/Tabs/Tabs.tsx +2 -9
  67. package/src/components/Toggle/Toggle.tsx +2 -10
  68. package/src/components/Tooltip/Tooltip.tsx +2 -5
  69. package/src/lib/composables/useAtomixGlass.ts +41 -10
  70. package/src/lib/composables/useAtomixGlassStyles.ts +59 -75
  71. package/src/lib/composables/usePerformanceMonitor.ts +5 -0
  72. package/src/lib/constants/components.ts +358 -46
  73. package/src/lib/types/components.ts +33 -1
  74. package/src/styles/01-settings/_settings.atomix-glass.scss +66 -28
  75. package/src/styles/02-tools/_tools.glass.scss +45 -3
  76. package/src/styles/06-components/_components.atomix-glass.scss +114 -77
  77. package/src/components/AtomixGlass/deprecated/AtomixGlass.deprecated.tsx +0 -390
package/dist/core.js CHANGED
@@ -516,6 +516,62 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
516
516
  SIZES: [ "xs", "sm", "md", "lg", "xl", "none" ],
517
517
  DEFAULT: "md"
518
518
  }
519
+ }, GLASS_DEFAULTS_BUTTON = {
520
+ displacementScale: 16,
521
+ saturation: 180,
522
+ elasticity: 0
523
+ }, GLASS_DEFAULTS_BADGE = {
524
+ displacementScale: 14,
525
+ borderRadius: 16,
526
+ elasticity: 0
527
+ }, GLASS_DEFAULTS_ACCORDION = {
528
+ displacementScale: 18,
529
+ elasticity: 0
530
+ }, GLASS_DEFAULTS_CALLOUT = {
531
+ displacementScale: 22,
532
+ borderRadius: 8,
533
+ elasticity: 0
534
+ }, GLASS_DEFAULTS_SPINNER = {
535
+ displacementScale: 12,
536
+ elasticity: 0
537
+ }, GLASS_BORDER_GRADIENT = {
538
+ BASE_ANGLE: 135,
539
+ ANGLE_MULTIPLIER: .5,
540
+ VELOCITY_ANGLE_MULTIPLIER: .5,
541
+ CHROMATIC_OFFSET: 1.5,
542
+ STOP_1: {
543
+ MIN: 10,
544
+ BASE: 33,
545
+ get MULTIPLIER() {
546
+ return .009 * this.BASE;
547
+ }
548
+ },
549
+ STOP_2: {
550
+ MAX: 90,
551
+ BASE: 66,
552
+ get MULTIPLIER() {
553
+ return .006 * this.BASE;
554
+ }
555
+ },
556
+ OPACITY: {
557
+ /** Matches $glass-border-1-opacity (0.08) */
558
+ BASE_1: .08,
559
+ get BASE_2() {
560
+ return 3.33 * this.BASE_1;
561
+ },
562
+ get BASE_3() {
563
+ return 2.66 * this.BASE_1;
564
+ },
565
+ get BASE_4() {
566
+ return 5 * this.BASE_1;
567
+ },
568
+ get MULTIPLIER_LOW() {
569
+ return .066 * this.BASE_1;
570
+ },
571
+ get MULTIPLIER_HIGH() {
572
+ return .1 * this.BASE_1;
573
+ }
574
+ }
519
575
  }, ATOMIX_GLASS = {
520
576
  BASE_CLASS: "c-atomix-glass",
521
577
  CONTAINER_CLASS: "c-atomix-glass__container",
@@ -527,6 +583,22 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
527
583
  BORDER_BACKDROP_CLASS: "c-atomix-glass__border-backdrop",
528
584
  BORDER_1_CLASS: "c-atomix-glass__border-1",
529
585
  BORDER_2_CLASS: "c-atomix-glass__border-2",
586
+ /** Centralized liquid glass rim configuration */
587
+ BORDER: {
588
+ WIDTH_CSS_VAR: "--atomix-glass-border-width",
589
+ DEFAULT_WIDTH: "0.5px",
590
+ GRADIENT_CSS_VARS: {
591
+ GRADIENT_1: "--atomix-glass-border-gradient-1",
592
+ GRADIENT_2: "--atomix-glass-border-gradient-2"
593
+ },
594
+ GRADIENT: GLASS_BORDER_GRADIENT,
595
+ OVER_LIGHT: {
596
+ opacity: .7
597
+ },
598
+ DARK: {
599
+ opacity: .35
600
+ }
601
+ },
530
602
  HOVER_1_CLASS: "c-atomix-glass__hover-1",
531
603
  HOVER_2_CLASS: "c-atomix-glass__hover-2",
532
604
  HOVER_3_CLASS: "c-atomix-glass__hover-3",
@@ -555,25 +627,22 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
555
627
  SHADER: "c-atomix-glass--shader"
556
628
  },
557
629
  DEFAULTS: {
558
- DISPLACEMENT_SCALE: 70,
630
+ /** Subtle refraction — Apple UI chrome avoids heavy liquid distortion */
631
+ DISPLACEMENT_SCALE: 28,
559
632
  get BLUR_AMOUNT() {
560
- return .15 * this.DISPLACEMENT_SCALE;
561
- // Dynamically computed based on displacement
562
- },
563
- get SATURATION() {
564
- return 100 + .5 * this.DISPLACEMENT_SCALE;
565
- // Saturate relative to intensity
566
- },
633
+ // Apple Music sidebar / player bar: ~20–40px frost (see $glass-backdrop-filter)
634
+ return Math.max(20, .72 * this.DISPLACEMENT_SCALE);
635
+ },
636
+ /** Fixed 180% matches Apple's saturate(180%) backdrop recipe */
637
+ SATURATION: 180,
567
638
  get ABERRATION_INTENSITY() {
568
- return .03 * this.DISPLACEMENT_SCALE;
569
- // Scale aberration with displacement
570
- },
571
- ELASTICITY: .15,
639
+ return .02 * this.DISPLACEMENT_SCALE;
640
+ },
641
+ ELASTICITY: .05,
572
642
  get CORNER_RADIUS() {
573
643
  return 16;
574
644
  // Use 16 to match SCSS design system (was 20)
575
645
  },
576
- PADDING: "0",
577
646
  MODE: "standard",
578
647
  OVER_LIGHT: !1,
579
648
  ENABLE_OVER_LIGHT_LAYERS: !0,
@@ -589,19 +658,24 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
589
658
  },
590
659
  CONSTANTS: {
591
660
  ACTIVATION_ZONE: 200,
592
- LERP_FACTOR: .08,
661
+ LERP_FACTOR: .05,
662
+ // Lower = more viscous, liquid-smooth tracking (Apple feel)
593
663
  SMOOTHSTEP_POWER: 2.5,
594
664
  MIN_BLUR: .1,
595
665
  MOUSE_INFLUENCE_DIVISOR: 100,
596
666
  EDGE_FADE_PIXELS: 2,
597
- // Elasticity physics constants
598
- ELASTICITY_TRANSLATION_FACTOR: .1,
667
+ // Elasticity physics constants — Apple-tuned: soft springs, fast settling, minimal stretch
668
+ ELASTICITY_TRANSLATION_FACTOR: .06,
669
+ // Subtler elastic shift (was 0.1)
599
670
  ELASTICITY_DISTANCE_THRESHOLD: 200,
600
671
  ELASTICITY_COMPRESSION_FACTOR: .3,
601
- ELASTICITY_STIFFNESS: .1,
602
- ELASTICITY_DAMPING: .76,
672
+ ELASTICITY_STIFFNESS: .06,
673
+ // Softer springs = gentler motion (was 0.1)
674
+ ELASTICITY_DAMPING: .88,
675
+ // Fast settling, no wobble (was 0.76)
603
676
  ELASTICITY_VELOCITY_FACTOR: .65,
604
- ELASTICITY_STRETCH_RATIO: .45,
677
+ ELASTICITY_STRETCH_RATIO: .25,
678
+ // Less visible surface tension stretch (was 0.45)
605
679
  ELASTICITY_MAGNIFICATION_BASE: 1.02,
606
680
  // Note: This default must match the SCSS variable --atomix-radius-md
607
681
  // @see src/styles/01-settings/_settings.global.scss
@@ -615,55 +689,16 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
615
689
  },
616
690
  // Gradient calculation constants
617
691
  GRADIENT: {
618
- BASE_ANGLE: 135,
619
- // Base angle for border gradients (degrees)
620
- ANGLE_MULTIPLIER: 1.2,
621
- // Multiplier for mouse influence on angle
622
- VELOCITY_ANGLE_MULTIPLIER: 2.5,
623
- // How much velocity affects gradient rotation
624
- CHROMATIC_OFFSET: 1.5,
625
- // Degree offset for chromatic rim layers
626
- BORDER_STOP_1: {
627
- MIN: 10,
628
- // Minimum percentage for border stop 1
629
- BASE: 33,
630
- // Base percentage for border stop 1
631
- get MULTIPLIER() {
632
- return .009 * this.BASE;
633
- }
634
- },
635
- BORDER_STOP_2: {
636
- MAX: 90,
637
- // Maximum percentage for border stop 2
638
- BASE: 66,
639
- // Base percentage for border stop 2
640
- get MULTIPLIER() {
641
- return .006 * this.BASE;
642
- }
643
- },
644
- BORDER_OPACITY: {
645
- BASE_1: .12,
646
- // Base opacity for border gradient 1
647
- get BASE_2() {
648
- return 3.33 * this.BASE_1;
649
- },
650
- // Base opacity for border gradient 2
651
- get BASE_3() {
652
- return 2.66 * this.BASE_1;
653
- },
654
- // Base opacity for border gradient 3
655
- get BASE_4() {
656
- return 5 * this.BASE_1;
657
- },
658
- // Base opacity for border gradient 4
659
- get MULTIPLIER_LOW() {
660
- return .066 * this.BASE_1;
661
- },
662
- // Low multiplier for mouse influence on opacity
663
- get MULTIPLIER_HIGH() {
664
- return .1 * this.BASE_1;
665
- }
666
- },
692
+ BASE_ANGLE: GLASS_BORDER_GRADIENT.BASE_ANGLE,
693
+ ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.ANGLE_MULTIPLIER,
694
+ VELOCITY_ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.VELOCITY_ANGLE_MULTIPLIER,
695
+ CHROMATIC_OFFSET: GLASS_BORDER_GRADIENT.CHROMATIC_OFFSET,
696
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_1 */
697
+ BORDER_STOP_1: GLASS_BORDER_GRADIENT.STOP_1,
698
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_2 */
699
+ BORDER_STOP_2: GLASS_BORDER_GRADIENT.STOP_2,
700
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.OPACITY */
701
+ BORDER_OPACITY: GLASS_BORDER_GRADIENT.OPACITY,
667
702
  CENTER_POSITION: 50,
668
703
  // Center position percentage (50%)
669
704
  HOVER_POSITION: {
@@ -696,8 +731,8 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
696
731
  return 2 * this.BLACK_STOP;
697
732
  },
698
733
  // End percentage for black hover 1
699
- WHITE_START: .5,
700
- // Start opacity for white hover 1
734
+ WHITE_START: .35,
735
+ // Gentler hover flash Apple hover is barely visible
701
736
  get WHITE_STOP() {
702
737
  return this.BLACK_END - 10;
703
738
  }
@@ -715,8 +750,8 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
715
750
  return 2 * this.BLACK_STOP;
716
751
  },
717
752
  // End percentage for black hover 2
718
- WHITE_START: 1,
719
- // Start opacity for white hover 2
753
+ WHITE_START: .7,
754
+ // Gentler hover flash
720
755
  get WHITE_STOP() {
721
756
  return this.BLACK_END;
722
757
  }
@@ -734,8 +769,8 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
734
769
  return 2 * this.BLACK_STOP;
735
770
  },
736
771
  // End percentage for black hover 3
737
- WHITE_START: 1,
738
- // Start opacity for white hover 3
772
+ WHITE_START: .7,
773
+ // Gentler hover flash
739
774
  get WHITE_STOP() {
740
775
  return this.BLACK_END;
741
776
  }
@@ -745,13 +780,13 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
745
780
  BASE_GRADIENT: {
746
781
  ANGLE: 135,
747
782
  // Gradient angle in degrees
748
- BLACK_START_BASE: .15,
783
+ BLACK_START_BASE: .1,
749
784
  // Base start opacity for black
750
785
  get BLACK_START_MULTIPLIER() {
751
786
  return .02 * this.BLACK_START_BASE;
752
787
  },
753
788
  // Multiplier for mouse X influence on start
754
- BLACK_MID_BASE: .1,
789
+ BLACK_MID_BASE: .07,
755
790
  // Base mid opacity for black
756
791
  get BLACK_MID_MULTIPLIER() {
757
792
  return .02 * this.BLACK_MID_BASE;
@@ -772,7 +807,7 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
772
807
  }
773
808
  },
774
809
  OVERLAY_GRADIENT: {
775
- BLACK_START_BASE: .12,
810
+ BLACK_START_BASE: .08,
776
811
  // Base start opacity for black overlay
777
812
  get BLACK_START_MULTIPLIER() {
778
813
  return .025 * this.BLACK_START_BASE;
@@ -796,14 +831,14 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
796
831
  return .416 * this.BLACK_START_BASE;
797
832
  }
798
833
  },
799
- // Overlay highlight constants
834
+ // Overlay highlight constants — Apple places specular at the top-center
800
835
  OVERLAY_HIGHLIGHT: {
801
- POSITION_X: 20,
802
- // X position percentage
803
- POSITION_Y: 20,
804
- // Y position percentage
805
- WHITE_OPACITY: .4,
806
- // White opacity in gradient
836
+ POSITION_X: 50,
837
+ // Centered horizontal — Apple's top-center specular
838
+ POSITION_Y: 5,
839
+ // Very top — catches light like a curved glass surface
840
+ WHITE_OPACITY: .28,
841
+ // Softer specular visible but not glaring
807
842
  get STOP() {
808
843
  return 150 * this.WHITE_OPACITY;
809
844
  },
@@ -824,6 +859,10 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
824
859
  SATURATION: {
825
860
  HIGH_CONTRAST: 200
826
861
  },
862
+ // Container shadows — hairline inner catch + soft floating lift (Apple player bar)
863
+ CONTAINER_SHADOW: {
864
+ LIGHT: "inset 0 0.5px 0 rgba(255, 255, 255, 0.32), inset 0 1px 2px rgba(255, 255, 255, 0.06), 0 8px 32px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.16)"
865
+ },
827
866
  // Phase 1: Animation System Constants
828
867
  ANIMATION: {
829
868
  // Breathing effect timing (in milliseconds)
@@ -863,7 +902,44 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
863
902
  }
864
903
  }
865
904
  }
866
- }, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
905
+ };
906
+
907
+ /**
908
+ * Default theme colors for components
909
+ */
910
+ /**
911
+ * Button-specific constants
912
+ */
913
+ /**
914
+ * Component Utilities
915
+ *
916
+ * Helper functions for component development with the new customization system
917
+ */
918
+ /**
919
+ * Merge multiple class names
920
+ */
921
+ function mergeClassNames(...classes) {
922
+ return classes.filter(Boolean).join(" ");
923
+ }
924
+
925
+ /**
926
+ * Utility to merge multiple React refs into one
927
+ */ function setRef(ref, value) {
928
+ "function" == typeof ref ? ref(value) : ref && (
929
+ // This is safe because we're checking that ref exists first
930
+ ref.current = value);
931
+ }
932
+
933
+ /**
934
+ * Combines two React refs into a single ref function
935
+ * This is used when you need to use and forward a ref at the same time
936
+ */ function useForkRef(refA, refB) {
937
+ return React.useMemo((() => null == refA && null == refB ? null : refValue => {
938
+ setRef(refA, refValue), setRef(refB, refValue);
939
+ }), [ refA, refB ]);
940
+ }
941
+
942
+ const {CONSTANTS: CONSTANTS$3} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
867
943
  x: rect.left + rect.width / 2,
868
944
  y: rect.top + rect.height / 2
869
945
  } : {
@@ -871,37 +947,37 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
871
947
  y: 0
872
948
  }, calculateMouseInfluence = mouseOffset => {
873
949
  if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
874
- // Bounded calculation keeps the glass effect subtle and stable
875
- const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$2.MOUSE_INFLUENCE_DIVISOR;
950
+ // Clamp influence to keep mouse response subtle and stable.
951
+ const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$3.MOUSE_INFLUENCE_DIVISOR;
876
952
  return Math.min(.8, influence);
877
953
  // Tighter cap to prevent blur/filter blow-out
878
- }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$2.MIN_BLUR : Math.max(CONSTANTS$2.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$2.MAX_SIZE && size.height <= CONSTANTS$2.MAX_SIZE, parseBorderRadiusValue = value => {
954
+ }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$3.MIN_BLUR : Math.max(CONSTANTS$3.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$3.MAX_SIZE && size.height <= CONSTANTS$3.MAX_SIZE, parseBorderRadiusValue = value => {
879
955
  if ("number" == typeof value) return Math.max(0, value);
880
- if ("string" != typeof value || !value.trim()) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
956
+ if ("string" != typeof value || !value.trim()) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
881
957
  const trimmedValue = value.trim();
882
958
  // Handle px values
883
959
  if (trimmedValue.endsWith("px")) {
884
960
  const parsed = parseFloat(trimmedValue);
885
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
961
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
886
962
  }
887
963
  // Handle rem values (assume 16px = 1rem)
888
964
  if (trimmedValue.endsWith("rem")) {
889
965
  const parsed = parseFloat(trimmedValue);
890
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
966
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
891
967
  }
892
968
  // Handle em values (assume 16px = 1em for simplicity)
893
969
  if (trimmedValue.endsWith("em")) {
894
970
  const parsed = parseFloat(trimmedValue);
895
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
971
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
896
972
  }
897
973
  // Handle percentage (convert to approximate px value, assuming 200px container)
898
974
  if (trimmedValue.endsWith("%")) {
899
975
  const parsed = parseFloat(trimmedValue);
900
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
976
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
901
977
  }
902
978
  // Handle unitless numbers
903
979
  const numValue = parseFloat(trimmedValue);
904
- return isNaN(numValue) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
980
+ return isNaN(numValue) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
905
981
  }, extractBorderRadiusFromElement = element => {
906
982
  if (!element || !element.props) return null;
907
983
  // Check inline styles first (highest priority)
@@ -917,11 +993,11 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
917
993
  // If element has children, recursively check them
918
994
  if (element.props.children) {
919
995
  const childRadius = extractBorderRadiusFromChildren(element.props.children);
920
- if (childRadius > 0 && childRadius !== CONSTANTS$2.DEFAULT_CORNER_RADIUS) return childRadius;
996
+ if (childRadius > 0 && childRadius !== CONSTANTS$3.DEFAULT_CORNER_RADIUS) return childRadius;
921
997
  }
922
998
  return null;
923
999
  }, extractBorderRadiusFromChildren = children => {
924
- if (!children) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1000
+ if (!children) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
925
1001
  try {
926
1002
  const childArray = React.Children.toArray(children);
927
1003
  for (let i = 0; i < childArray.length; i++) {
@@ -934,7 +1010,7 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
934
1010
  } catch (error) {
935
1011
  // Silently handle errors
936
1012
  }
937
- return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1013
+ return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
938
1014
  }, smoothstep = t => {
939
1015
  const clamped = Math.max(0, Math.min(1, t));
940
1016
  return clamped * clamped * (3 - 2 * clamped);
@@ -944,7 +1020,47 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
944
1020
  value: current + newVelocity,
945
1021
  velocity: newVelocity
946
1022
  };
947
- }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
1023
+ };
1024
+
1025
+ /**
1026
+ * Calculate element center from bounding rect
1027
+ */
1028
+ /**
1029
+ * Normalizes a layout inset for use in CSS custom properties.
1030
+ *
1031
+ * @param value - Raw inset from `style` (number, px string, or `auto`).
1032
+ * @param fallback - Value used when `value` is undefined.
1033
+ */
1034
+ function formatGlassInsetValue(value, fallback = "auto") {
1035
+ return void 0 === value ? "number" == typeof fallback ? `${fallback}px` : String(fallback) : "auto" === value ? "auto" : "number" == typeof value ? `${value}px` : value;
1036
+ }
1037
+
1038
+ /**
1039
+ * Determines whether the glass should use fixed/sticky layout semantics.
1040
+ *
1041
+ * @param explicit - Value of the `isFixedOrSticky` prop.
1042
+ * @param position - `position` from the consumer `style` object.
1043
+ */
1044
+ /** Coerces a value to a finite number, returning `fallback` when invalid. */
1045
+ function toSafeNumber(value, fallback = 0) {
1046
+ return "number" != typeof value || isNaN(value) ? fallback : value;
1047
+ }
1048
+
1049
+ /**
1050
+ * Calculates the target frame rate for shader time-animation loops.
1051
+ *
1052
+ * Balances visual quality against distortion complexity and `animationSpeed`.
1053
+ */
1054
+ /**
1055
+ * Computes per-channel displacement scale for the SVG chromatic-aberration filter.
1056
+ */
1057
+ function getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channelFactor) {
1058
+ return displacementScale * (("shader" === mode ? 1 : -1) - aberrationIntensity * channelFactor);
1059
+ }
1060
+
1061
+ /**
1062
+ * Get displacement map URL based on mode
1063
+ */ const getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
948
1064
  switch (mode) {
949
1065
  case "standard":
950
1066
  return displacementMap;
@@ -961,13 +1077,28 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
961
1077
  default:
962
1078
  return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
963
1079
  }
964
- }, sharedShaderCache = new Map, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
965
- style: {
966
- position: "absolute",
967
- width: "100%",
968
- height: "100%",
969
- inset: 0
970
- },
1080
+ }, sharedShaderCache = new Map, CHROMATIC_CHANNELS = [ {
1081
+ result: "RED_DISPLACED",
1082
+ channelResult: "RED_CHANNEL",
1083
+ aberrationFactor: 0,
1084
+ colorMatrix: "1 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
1085
+ }, {
1086
+ result: "GREEN_DISPLACED",
1087
+ channelResult: "GREEN_CHANNEL",
1088
+ aberrationFactor: .02,
1089
+ colorMatrix: "0 0 0 0 0\n0 1 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
1090
+ }, {
1091
+ result: "BLUE_DISPLACED",
1092
+ channelResult: "BLUE_CHANNEL",
1093
+ aberrationFactor: .03,
1094
+ colorMatrix: "0 0 0 0 0\n0 0 0 0 0\n0 0 1 0 0\n0 0 0 1 0"
1095
+ } ], FILTER_SVG_STYLE = {
1096
+ position: "absolute",
1097
+ width: "100%",
1098
+ height: "100%",
1099
+ inset: 0
1100
+ }, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
1101
+ style: FILTER_SVG_STYLE,
971
1102
  "aria-hidden": "true",
972
1103
  children: jsxs("defs", {
973
1104
  children: [ jsxs("radialGradient", {
@@ -1021,43 +1152,21 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1021
1152
  dx: "0",
1022
1153
  dy: "0",
1023
1154
  result: "CENTER_ORIGINAL"
1024
- }), jsx("feDisplacementMap", {
1025
- in: "SourceGraphic",
1026
- in2: "DISPLACEMENT_MAP",
1027
- scale: displacementScale * ("shader" === mode ? 1 : -1),
1028
- xChannelSelector: "R",
1029
- yChannelSelector: "B",
1030
- result: "RED_DISPLACED"
1031
- }), jsx("feColorMatrix", {
1032
- in: "RED_DISPLACED",
1033
- type: "matrix",
1034
- values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
1035
- result: "RED_CHANNEL"
1036
- }), jsx("feDisplacementMap", {
1037
- in: "SourceGraphic",
1038
- in2: "DISPLACEMENT_MAP",
1039
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .02 * aberrationIntensity),
1040
- xChannelSelector: "R",
1041
- yChannelSelector: "B",
1042
- result: "GREEN_DISPLACED"
1043
- }), jsx("feColorMatrix", {
1044
- in: "GREEN_DISPLACED",
1045
- type: "matrix",
1046
- values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
1047
- result: "GREEN_CHANNEL"
1048
- }), jsx("feDisplacementMap", {
1049
- in: "SourceGraphic",
1050
- in2: "DISPLACEMENT_MAP",
1051
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .03 * aberrationIntensity),
1052
- xChannelSelector: "R",
1053
- yChannelSelector: "B",
1054
- result: "BLUE_DISPLACED"
1055
- }), jsx("feColorMatrix", {
1056
- in: "BLUE_DISPLACED",
1057
- type: "matrix",
1058
- values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0",
1059
- result: "BLUE_CHANNEL"
1060
- }), jsx("feBlend", {
1155
+ }), CHROMATIC_CHANNELS.map((channel => jsxs(React.Fragment, {
1156
+ children: [ jsx("feDisplacementMap", {
1157
+ in: "SourceGraphic",
1158
+ in2: "DISPLACEMENT_MAP",
1159
+ scale: getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channel.aberrationFactor),
1160
+ xChannelSelector: "R",
1161
+ yChannelSelector: "B",
1162
+ result: channel.result
1163
+ }), jsx("feColorMatrix", {
1164
+ in: channel.result,
1165
+ type: "matrix",
1166
+ values: channel.colorMatrix,
1167
+ result: channel.channelResult
1168
+ }) ]
1169
+ }, channel.channelResult))), jsx("feBlend", {
1061
1170
  in: "GREEN_CHANNEL",
1062
1171
  in2: "BLUE_CHANNEL",
1063
1172
  mode: "screen",
@@ -1098,24 +1207,22 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1098
1207
  });
1099
1208
 
1100
1209
  /**
1101
- * Default theme colors for components
1102
- */
1103
- /**
1104
- * Button-specific constants
1210
+ * Module-level LRU cache for shader displacement maps.
1211
+ *
1212
+ * Shared across all `AtomixGlassContainer` instances so identical size and
1213
+ * variant combinations are generated once.
1105
1214
  */ GlassFilterComponent.displayName = "GlassFilter";
1106
1215
 
1107
- // Memoize component to prevent unnecessary re-renders
1216
+ /** Shallow prop comparison to avoid redundant SVG filter regeneration. */
1108
1217
  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 = {
1109
1218
  x: 0,
1110
1219
  y: 0
1111
- }, 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 = {
1220
+ }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, glassSize: glassSize = {
1112
1221
  width: 0,
1113
1222
  height: 0
1114
- }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1,
1115
- // Phase 1: Animation System props
1116
- 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) => {
1223
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1, 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) => {
1117
1224
  // React 18 useId — stable, unique, and SSR-safe (no module-level counter)
1118
- 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);
1225
+ const rawId = useId(), filterId = useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), containerRef = useForkRef(ref, null), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
1119
1226
  // Lazy load shader utilities only when shader mode is needed
1120
1227
  useEffect((() => {
1121
1228
  "shader" === mode ?
@@ -1193,15 +1300,23 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1193
1300
  shaderGeneratorRef.current = null;
1194
1301
  }
1195
1302
  };
1196
- }), [ mode, glassSize, shaderVariant ]),
1197
- // Phase 1: Time-Based Animation Loop - Continuous shader regeneration
1198
- useEffect((() => {
1303
+ }), [ mode, glassSize, shaderVariant ]), useEffect((() => {
1199
1304
  // Only run animations in shader mode with time animation enabled
1200
1305
  if ("shader" !== mode || !withTimeAnimation || effectiveReducedMotion || effectiveWithoutEffects)
1201
1306
  // Cancel any existing animation frame
1202
1307
  return void (null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
1203
1308
  animationFrameRef.current = null));
1204
- const baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed || 1)), complexity = withMultiLayerDistortion ? Math.max(1, (distortionOctaves || 3) / 3 + .25 * Math.max(0, (distortionLacunarity || 2) - 2) + Math.max(0, (distortionGain || .5) - .5)) : 1, frameInterval = 1e3 / Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
1309
+ const targetFps = function(options) {
1310
+ const {distortionQuality: distortionQuality, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5} = options, baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed)), complexity = withMultiLayerDistortion ? Math.max(1, distortionOctaves / 3 + .25 * Math.max(0, distortionLacunarity - 2) + Math.max(0, distortionGain - .5)) : 1;
1311
+ return Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
1312
+ }({
1313
+ distortionQuality: distortionQuality,
1314
+ animationSpeed: animationSpeed,
1315
+ withMultiLayerDistortion: withMultiLayerDistortion,
1316
+ distortionOctaves: distortionOctaves,
1317
+ distortionLacunarity: distortionLacunarity,
1318
+ distortionGain: distortionGain
1319
+ }), frameInterval = 1e3 / targetFps;
1205
1320
  let lastUpdate = 0, isCancelled = !1;
1206
1321
  const animate = currentTime => {
1207
1322
  if (!isCancelled) {
@@ -1225,88 +1340,21 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1225
1340
  };
1226
1341
  }), [ mode, withTimeAnimation, animationSpeed, displacementScale, withMultiLayerDistortion, distortionOctaves, distortionLacunarity, distortionGain, distortionQuality, effectiveReducedMotion, effectiveWithoutEffects, glassSize ]);
1227
1342
  // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
1228
- const [rectCache, setRectCache] = useState(null);
1229
- useEffect((() => {
1230
- if (!ref || "function" == typeof ref) return;
1231
- const element = ref.current;
1232
- if (element) try {
1233
- setRectCache(element.getBoundingClientRect());
1234
- } catch (error) {
1235
- console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
1236
- }
1237
- }), [ ref, glassSize ]);
1238
- const liquidBlur = useMemo((() => {
1239
- const defaultBlur = {
1240
- baseBlur: blurAmount,
1241
- edgeBlur: 1.25 * blurAmount,
1242
- centerBlur: 1.1 * blurAmount,
1243
- flowBlur: 1.2 * blurAmount
1244
- };
1245
- // Enhanced validation for liquid blur
1246
- if (!withLiquidBlur || !rectCache || !mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y || isNaN(mouseOffset.x) || isNaN(mouseOffset.y)) return defaultBlur;
1343
+ const containerVars = useMemo((() => {
1247
1344
  try {
1248
- const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = 2 * blurAmount, baseBlur = Math.min(maxBlur, blurAmount + mouseInfluence * blurAmount * .15), edgeIntensity = .15 * mouseInfluence, edgeBlur = Math.min(maxBlur, baseBlur * (.8 + .4 * edgeIntensity)), centerIntensity = .1 * mouseInfluence, centerBlur = Math.min(maxBlur, baseBlur * (.3 + .3 * centerIntensity)), flowBlur = Math.min(maxBlur, 1.2 * baseBlur);
1249
- // NOTE: hover/active multipliers intentionally omitted here —
1250
- // they belong on opacity layers, not the blur filter itself.
1251
1345
  return {
1252
- baseBlur: clampBlur(baseBlur),
1253
- edgeBlur: clampBlur(edgeBlur),
1254
- centerBlur: clampBlur(centerBlur),
1255
- flowBlur: clampBlur(flowBlur)
1256
- };
1257
- } catch (error) {
1258
- return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
1259
- defaultBlur;
1260
- }
1261
- }), [ withLiquidBlur, blurAmount, mouseOffset, rectCache ]), backdropStyle = useMemo((() => {
1262
- try {
1263
- const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), validatedBaseBlur = "number" != typeof liquidBlur.baseBlur || isNaN(liquidBlur.baseBlur) ? 0 : liquidBlur.baseBlur, validatedEdgeBlur = "number" != typeof liquidBlur.edgeBlur || isNaN(liquidBlur.edgeBlur) ? 0 : liquidBlur.edgeBlur, validatedCenterBlur = "number" != typeof liquidBlur.centerBlur || isNaN(liquidBlur.centerBlur) ? 0 : liquidBlur.centerBlur, validatedFlowBlur = "number" != typeof liquidBlur.flowBlur || isNaN(liquidBlur.flowBlur) ? 0 : liquidBlur.flowBlur, area = rectCache ? rectCache.width * rectCache.height : 0;
1264
- // Validate blur values before using them
1265
- return !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? {
1266
- backdropFilter: `blur(${clampBlur(Math.max(validatedBaseBlur, .8 * validatedEdgeBlur, 1.1 * validatedCenterBlur, .9 * validatedFlowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`
1267
- } : {
1268
- backdropFilter: `blur(${clampBlur(.4 * validatedBaseBlur + .25 * validatedEdgeBlur + .15 * validatedCenterBlur + .2 * validatedFlowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1}) brightness(${overLightConfig?.brightness || 1})`
1269
- };
1270
- // Single-pass fallback: stronger radius to match perceived blur of multi-pass
1271
- } catch (error) {
1272
- return console.warn("AtomixGlassContainer: Error calculating backdrop style", error),
1273
- {
1274
- backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
1275
- };
1276
- }
1277
- }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur, overLightConfig ]), containerVars = useMemo((() => {
1278
- try {
1279
- // Safe extraction of mouse offset values
1280
- const mx = mouseOffset && "number" == typeof mouseOffset.x && !isNaN(mouseOffset.x) ? mouseOffset.x : 0, my = mouseOffset && "number" == typeof mouseOffset.y && !isNaN(mouseOffset.y) ? mouseOffset.y : 0;
1281
- return {
1282
- "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`,
1283
- "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
1284
- "--atomix-glass-container-shadow": overLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig?.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset",
1285
- "--atomix-glass-container-shadow-opacity": effectiveWithoutEffects ? 0 : 1,
1286
- // Background and shadow values use design token-aligned RGB values
1287
- "--atomix-glass-container-bg": overLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none",
1288
- "--atomix-glass-container-text-shadow": overLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
1289
- "--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
1346
+ "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`
1290
1347
  };
1291
1348
  } catch (error) {
1292
1349
  return console.warn("AtomixGlassContainer: Error generating container variables", error),
1293
1350
  {
1294
- "--atomix-glass-container-padding": "0 0",
1295
- "--atomix-glass-container-radius": "0px",
1296
- "--atomix-glass-container-backdrop": "none",
1297
- "--atomix-glass-container-shadow": "none",
1298
- "--atomix-glass-container-shadow-opacity": 1,
1299
- "--atomix-glass-container-bg": "none",
1300
- "--atomix-glass-container-text-shadow": "none"
1351
+ "--atomix-glass-container-radius": "0px"
1301
1352
  };
1302
1353
  }
1303
- }), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
1354
+ }), [ borderRadius ]);
1304
1355
  return jsx("div", {
1305
- ref: el => {
1306
- // Handle forwarded ref
1307
- "function" == typeof ref ? ref(el) : ref && (ref.current = el);
1308
- },
1309
- className: `${ATOMIX_GLASS.CONTAINER_CLASS} ${className} ${isActive ? ATOMIX_GLASS.CLASSES.ACTIVE : ""} ${overLight ? ATOMIX_GLASS.CLASSES.OVER_LIGHT : ""}`,
1356
+ ref: containerRef,
1357
+ className: mergeClassNames(ATOMIX_GLASS.CONTAINER_CLASS, className, isActive && ATOMIX_GLASS.CLASSES.ACTIVE, overLight && ATOMIX_GLASS.CLASSES.OVER_LIGHT),
1310
1358
  style: {
1311
1359
  ...style,
1312
1360
  ...containerVars
@@ -1324,8 +1372,8 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1324
1372
  blurAmount: blurAmount,
1325
1373
  mode: mode,
1326
1374
  id: filterId,
1327
- displacementScale: "number" != typeof displacementScale || isNaN(displacementScale) ? 0 : displacementScale,
1328
- aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
1375
+ displacementScale: toSafeNumber(displacementScale),
1376
+ aberrationIntensity: toSafeNumber(aberrationIntensity),
1329
1377
  shaderMapUrl: shaderMapUrl
1330
1378
  }), jsx("div", {
1331
1379
  className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
@@ -1347,8 +1395,12 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1347
1395
  });
1348
1396
  }));
1349
1397
 
1350
- // ─── Blur multiplier constants (module-level, never change at runtime) ────────
1351
- AtomixGlassContainer.displayName = "AtomixGlassContainer";
1398
+ /**
1399
+ * Internal glass surface that owns backdrop-filter, SVG distortion, and content.
1400
+ *
1401
+ * Layout and stacking styles are applied via the `style` prop from the parent.
1402
+ * The root wrapper supplies CSS custom properties only.
1403
+ */ AtomixGlassContainer.displayName = "AtomixGlassContainer";
1352
1404
 
1353
1405
  // Singleton instance
1354
1406
  const globalMouseTracker = new
@@ -1463,28 +1515,32 @@ class {
1463
1515
  */ getSubscriberCount() {
1464
1516
  return this.listeners.size;
1465
1517
  }
1466
- }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
1518
+ }, {BORDER: BORDER, CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, BORDER_GRADIENT = BORDER.GRADIENT, WHITE = CONSTANTS$2.PALETTE.WHITE, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
1467
1519
  if (!wrapperElement && !containerElement) return;
1468
1520
  if (!validateGlassSize(params.glassSize)) return;
1469
- const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, elasticTranslation: elasticTranslation, elasticVelocity: elasticVelocity, mouseVelocity: mouseVelocity, directionalScale: directionalScale, scaleBase: scaleBase, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, isFixedOrSticky: isFixedOrSticky = !1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
1521
+ const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, elasticTranslation: elasticTranslation, elasticVelocity: elasticVelocity, mouseVelocity: mouseVelocity, directionalScale: directionalScale, scaleBase: scaleBase, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, isFixedOrSticky: isFixedOrSticky = !1, borderAnimated: borderAnimated = !0, borderOpacityMultiplier: borderOpacityMultiplier = 1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
1470
1522
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
1471
1523
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
1472
1524
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
1473
1525
  shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
1474
1526
  borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
1475
1527
  saturationBoost: baseOverLightConfig.saturationBoost
1476
- }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, stretchMagnitude = ((pos1, pos2) => {
1477
- if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
1478
- const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
1479
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
1480
- })({
1481
- x: 0,
1482
- y: 0
1483
- }, elasticTranslation), tensionFactor = smoothstep(stretchMagnitude / 80), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
1528
+ }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, tensionFactor =
1529
+ /**
1530
+ * Computes tension factor from elastic translation magnitude (0–1).
1531
+ */
1532
+ function(elasticTranslation) {
1533
+ const magnitude = Math.hypot(elasticTranslation.x, elasticTranslation.y);
1534
+ return smoothstep(magnitude / 80);
1535
+ }
1536
+ /**
1537
+ * Updates the styles of the AtomixGlass wrapper and container elements imperatively
1538
+ * to avoid React re-renders on mouse movement.
1539
+ */ (elasticTranslation), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
1484
1540
  // Calculate mouse influence
1485
1541
  // Update Wrapper Styles (glassVars)
1486
1542
  if (wrapperElement) {
1487
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, velocityRotation = (mouseVelocity.x + elasticVelocity.x) * (GRADIENT.VELOCITY_ANGLE_MULTIPLIER || 2.5), borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER + velocityRotation, chromaticOffset = GRADIENT.CHROMATIC_OFFSET || 1.5, angleR = borderGradientAngle - chromaticOffset, angleB = borderGradientAngle + chromaticOffset, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), tensionGlow = 1 + .5 * tensionFactor, borderOpacities = [ (GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow ], configBorderOpacity = overLightConfig.borderOpacity, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
1543
+ const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
1488
1544
  hover1: {
1489
1545
  x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
1490
1546
  y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
@@ -1501,28 +1557,55 @@ class {
1501
1557
  x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
1502
1558
  y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
1503
1559
  }, opacityValues = {
1504
- hover1: isHovered || isActive ? .5 : 0,
1505
- hover2: isActive ? .5 : 0,
1506
- hover3: isHovered ? .4 : isActive ? .8 : 0,
1507
- base: isOverLight ? overLightConfig.opacity : 0,
1508
- over: isOverLight ? 1.1 * overLightConfig.opacity : 0
1560
+ // hover-1: ambient highlight glow present on hover and during press
1561
+ hover1: isHovered || isActive ? 1 : 0,
1562
+ // hover-2: press depth shadow only fires on active (mousedown)
1563
+ hover2: isActive ? 1 : 0,
1564
+ // hover-3: global soft-light surface shift half-strength on hover, full on press
1565
+ hover3: isActive ? 1 : isHovered ? .55 : 0,
1566
+ // Dark chrome: faint smoky tint; over-light keeps stronger fill
1567
+ base: isOverLight ? overLightConfig.opacity : .14,
1568
+ over: isOverLight ? 1.1 * overLightConfig.opacity : .1
1509
1569
  }, style = wrapperElement.style;
1510
1570
  style.setProperty("--atomix-glass-transform", transformStyle || "none");
1511
1571
  // Parallax for content (liquid refraction feel)
1512
1572
  const parallaxFactor = .38 + .12 * tensionFactor;
1513
1573
  style.setProperty("--atomix-glass-child-parallax", `translate(${elasticTranslation.x * -parallaxFactor}px, ${elasticTranslation.y * -parallaxFactor}px)`),
1514
- style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString()),
1574
+ style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString());
1515
1575
  // ── Chromatic Rim Lighting ──────────────────────────────────────
1516
- // Layer 1: Core White/Blue highlight
1517
- style.setProperty("--atomix-glass-border-gradient-1", `linear-gradient(${angleB}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
1518
- // Layer 2: Subtle Red/Warm highlight (offset angle)
1519
- style.setProperty("--atomix-glass-border-gradient-2", `linear-gradient(${angleR}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
1520
- // Hover gradients
1521
- style.setProperty("--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}%)`),
1522
- style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`),
1523
- style.setProperty("--atomix-glass-hover-3-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`),
1524
- style.setProperty("--atomix-glass-base-gradient", isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`),
1525
- style.setProperty("--atomix-glass-overlay-gradient", isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`),
1576
+ const borderVars = ATOMIX_GLASS.BORDER.GRADIENT_CSS_VARS;
1577
+ if (borderAnimated && !effectiveWithoutEffects) {
1578
+ const borderCssVars =
1579
+ /**
1580
+ * Builds animated chromatic rim CSS variables for border layers 1 and 2.
1581
+ * When empty, SCSS static conic/linear fallbacks apply.
1582
+ */
1583
+ function(params) {
1584
+ const {mouseOffset: mouseOffset, mouseVelocity: mouseVelocity, elasticVelocity: elasticVelocity, borderOpacity: borderOpacity, opacityMultiplier: opacityMultiplier = 1, tensionFactor: tensionFactor = 0} = params, mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), velocityRotation = (mouseVelocity.x + elasticVelocity.x) * BORDER_GRADIENT.VELOCITY_ANGLE_MULTIPLIER, borderGradientAngle = BORDER_GRADIENT.BASE_ANGLE + mx * BORDER_GRADIENT.ANGLE_MULTIPLIER + velocityRotation, chromaticOffset = BORDER_GRADIENT.CHROMATIC_OFFSET, angleR = borderGradientAngle - chromaticOffset, angleB = borderGradientAngle + chromaticOffset, borderStop1 = Math.max(BORDER_GRADIENT.STOP_1.MIN, BORDER_GRADIENT.STOP_1.BASE + my * BORDER_GRADIENT.STOP_1.MULTIPLIER), borderStop2 = Math.min(BORDER_GRADIENT.STOP_2.MAX, BORDER_GRADIENT.STOP_2.BASE + my * BORDER_GRADIENT.STOP_2.MULTIPLIER), tensionGlow = 1 + .5 * tensionFactor, opacities = BORDER_GRADIENT.OPACITY, borderOpacities = [ (opacities.BASE_1 + absMx * opacities.MULTIPLIER_LOW) * tensionGlow, (opacities.BASE_2 + absMx * opacities.MULTIPLIER_HIGH) * tensionGlow, (opacities.BASE_3 + absMx * opacities.MULTIPLIER_LOW) * tensionGlow, (opacities.BASE_4 + absMx * opacities.MULTIPLIER_HIGH) * tensionGlow ], configBorderOpacity = borderOpacity * opacityMultiplier, gradient1 = `linear-gradient(${angleB}deg, rgba(${WHITE}, 0) 0%, rgba(${WHITE}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${WHITE}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${WHITE}, 0) 100%)`, gradient2 = `linear-gradient(${angleR}deg, rgba(${WHITE}, 0) 0%, rgba(${WHITE}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${WHITE}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${WHITE}, 0) 100%)`;
1585
+ return {
1586
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_1]: gradient1,
1587
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_2]: gradient2
1588
+ };
1589
+ }({
1590
+ mouseOffset: mouseOffset,
1591
+ mouseVelocity: mouseVelocity,
1592
+ elasticVelocity: elasticVelocity,
1593
+ borderOpacity: overLightConfig.borderOpacity,
1594
+ opacityMultiplier: borderOpacityMultiplier,
1595
+ tensionFactor: tensionFactor
1596
+ });
1597
+ style.setProperty(borderVars.GRADIENT_1, borderCssVars[borderVars.GRADIENT_1] ?? ""),
1598
+ style.setProperty(borderVars.GRADIENT_2, borderCssVars[borderVars.GRADIENT_2] ?? "");
1599
+ } else style.removeProperty(borderVars.GRADIENT_1), style.removeProperty(borderVars.GRADIENT_2);
1600
+ // Hover gradients — cursor-relative radial positions for realistic light tracking.
1601
+ // hover-1: white overlay highlight following cursor (works on both dark + light)
1602
+ style.setProperty("--atomix-glass-hover-1-gradient", `radial-gradient(65% 55% at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, 0.24) 0%, rgba(${whiteColor}, 0.06) 45%, rgba(${whiteColor}, 0) 72%)`),
1603
+ // hover-2: press depth — darkens at cursor with multiply blend, isOverLight uses stronger black
1604
+ style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `radial-gradient(60% 50% at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, 0.22) 0%, rgba(${blackColor}, 0.06) 50%, rgba(${blackColor}, 0) 72%)` : `radial-gradient(60% 50% at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, 0.18) 0%, rgba(${blackColor}, 0.04) 50%, rgba(${blackColor}, 0) 72%)`),
1605
+ // hover-3: full-surface soft-light tint; linear gradient angled with cursor X
1606
+ style.setProperty("--atomix-glass-hover-3-gradient", `linear-gradient(${150 + .3 * mx}deg, rgba(${whiteColor}, ${isOverLight ? .08 : .12}) 0%, rgba(${whiteColor}, 0.04) 55%, rgba(${whiteColor}, 0) 100%)`),
1607
+ style.setProperty("--atomix-glass-base-gradient", isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `linear-gradient(180deg, rgba(${blackColor}, 0.42) 0%, rgba(${blackColor}, 0.22) 55%, rgba(${blackColor}, 0.12) 100%)`),
1608
+ style.setProperty("--atomix-glass-overlay-gradient", isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `radial-gradient(120% 80% at 50% 0%, rgba(${whiteColor}, 0.14) 0%, rgba(${whiteColor}, 0) 55%)`),
1526
1609
  // Opacities
1527
1610
  style.setProperty("--atomix-glass-hover-1-opacity", opacityValues.hover1.toString()),
1528
1611
  style.setProperty("--atomix-glass-hover-2-opacity", opacityValues.hover2.toString()),
@@ -1559,21 +1642,17 @@ class {
1559
1642
  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, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})`;
1560
1643
  // Container variables
1561
1644
  const style = containerElement.style;
1562
- style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
1645
+ style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
1563
1646
  style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
1564
1647
  // Shadows
1565
- style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset"),
1648
+ style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.35 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.15 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.06 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.08 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : ATOMIX_GLASS.CONSTANTS.CONTAINER_SHADOW.LIGHT),
1566
1649
  style.setProperty("--atomix-glass-container-shadow-opacity", effectiveWithoutEffects ? "0" : "1"),
1567
1650
  style.setProperty("--atomix-glass-container-bg", isOverLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none"),
1568
1651
  style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
1569
- style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)");
1652
+ style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0 8px 32px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.18)");
1570
1653
  }
1571
1654
  };
1572
1655
 
1573
- /**
1574
- * Updates the styles of the AtomixGlass wrapper and container elements imperatively
1575
- * to avoid React re-renders on mouse movement.
1576
- */
1577
1656
  /**
1578
1657
  * Animation System for AtomixGlass Component
1579
1658
  *
@@ -1718,12 +1797,38 @@ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new Weak
1718
1797
  * Composable hook for AtomixGlass component logic
1719
1798
  * Manages all state, calculations, and event handlers
1720
1799
  */
1721
- function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, priority: priority = 1, withTimeAnimation:
1800
+ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, border: border, withBorder: withBorder = !0, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, priority: priority = 1, withTimeAnimation:
1722
1801
  // Default priority
1723
1802
  // Phase 1: Animation System Props
1724
1803
  withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED, withMultiLayerDistortion: withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION, distortionOctaves: distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES, distortionLacunarity: distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY, distortionGain: distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN, distortionQuality: distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY}) {
1725
1804
  // State
1726
- const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
1805
+ const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), resolvedBorder = useMemo((() =>
1806
+ /**
1807
+ * Resolves `border` and legacy `withBorder` into a single configuration object.
1808
+ */
1809
+ function(border, withBorder) {
1810
+ const legacyDefault = withBorder ?? !0;
1811
+ return void 0 === border ? {
1812
+ enabled: legacyDefault,
1813
+ width: BORDER.DEFAULT_WIDTH,
1814
+ opacityMultiplier: 1,
1815
+ animated: !0
1816
+ } : "boolean" == typeof border ? {
1817
+ enabled: border,
1818
+ width: BORDER.DEFAULT_WIDTH,
1819
+ opacityMultiplier: 1,
1820
+ animated: !0
1821
+ } : {
1822
+ enabled: border.enabled ?? legacyDefault,
1823
+ width: (value = border.width, void 0 === value ? BORDER.DEFAULT_WIDTH : "number" == typeof value ? `${value}px` : value),
1824
+ opacityMultiplier: border.opacity ?? 1,
1825
+ animated: !1 !== border.animated
1826
+ };
1827
+ /**
1828
+ * Formats border width for CSS custom properties.
1829
+ */
1830
+ var value;
1831
+ }(border, withBorder)), [ border, withBorder ]), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
1727
1832
  x: 0,
1728
1833
  y: 0
1729
1834
  }), internalMouseOffsetRef = useRef({
@@ -2018,21 +2123,22 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2018
2123
  isOverLight: isOverLight,
2019
2124
  threshold: .7,
2020
2125
  opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
2021
- contrast: 1.4,
2022
- brightness: .9,
2023
- saturationBoost: 1.3,
2024
- // Fixed value dynamic saturation amplifies perceived displacement
2025
- shadowIntensity: .9,
2026
- borderOpacity: .7
2126
+ // Dark UI (Apple Music): neutral contrast + slight brightness lift
2127
+ contrast: isOverLight ? 1.4 : 1.02,
2128
+ brightness: isOverLight ? .9 : 1.02,
2129
+ saturationBoost: isOverLight ? 1.3 : 1,
2130
+ shadowIntensity: isOverLight ? .9 : 1,
2131
+ borderOpacity: isOverLight ? ATOMIX_GLASS.BORDER.OVER_LIGHT.opacity : ATOMIX_GLASS.BORDER.DARK.opacity
2027
2132
  };
2028
2133
  if ("object" == typeof overLight && null !== overLight) {
2029
- const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), finalConfig = {
2134
+ const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), validatedBorderOpacity = validateConfigValue(objConfig.borderOpacity, .1, 1, baseConfig.borderOpacity), finalConfig = {
2030
2135
  ...baseConfig,
2031
2136
  threshold: validatedThreshold,
2032
2137
  opacity: validatedOpacity * hoverIntensity * activeIntensity,
2033
2138
  contrast: validatedContrast,
2034
2139
  brightness: validatedBrightness,
2035
- saturationBoost: validatedSaturationBoost
2140
+ saturationBoost: validatedSaturationBoost,
2141
+ borderOpacity: validatedBorderOpacity
2036
2142
  };
2037
2143
  return "undefined" == typeof process || process.env, finalConfig;
2038
2144
  }
@@ -2130,12 +2236,13 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2130
2236
  withLiquidBlur: withLiquidBlur,
2131
2237
  blurAmount: blurAmount,
2132
2238
  saturation: saturation,
2133
- padding: padding,
2134
- isFixedOrSticky: isFixedOrSticky
2239
+ isFixedOrSticky: isFixedOrSticky,
2240
+ borderAnimated: resolvedBorder.animated,
2241
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
2135
2242
  }), Math.abs(mouseVelocityRef.current.x) < .001 && Math.abs(mouseVelocityRef.current.y) < .001 && Math.abs(elasticVelocityRef.current.x) < .001 && Math.abs(elasticVelocityRef.current.y) < .001 && Math.abs(scaleVelocityRef.current.x) < .001 && Math.abs(scaleVelocityRef.current.y) < .001 && Math.abs(internalMouseOffsetRef.current.x - targetMouseOffsetRef.current.x) < .001 && Math.abs(internalMouseOffsetRef.current.y - targetMouseOffsetRef.current.y) < .001 ? stopLerpLoop() : lerpRafRef.current = requestAnimationFrame(tick);
2136
2243
  };
2137
2244
  lerpRafRef.current = requestAnimationFrame(tick);
2138
- }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
2245
+ }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, isFixedOrSticky, resolvedBorder.animated, resolvedBorder.opacityMultiplier, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
2139
2246
  if (externalGlobalMousePosition && externalMouseOffset) return;
2140
2247
  if (effectiveWithoutEffects) return;
2141
2248
  const container = mouseContainer?.current || glassRef.current;
@@ -2199,9 +2306,10 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2199
2306
  withLiquidBlur: withLiquidBlur,
2200
2307
  blurAmount: blurAmount,
2201
2308
  saturation: saturation,
2202
- padding: padding
2309
+ borderAnimated: resolvedBorder.animated,
2310
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
2203
2311
  });
2204
- }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, padding, onClick ]);
2312
+ }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, resolvedBorder.animated, resolvedBorder.opacityMultiplier, onClick ]);
2205
2313
  // Event handlers
2206
2314
  const handleMouseEnter = useCallback((() => setIsHovered(!0)), []), handleMouseLeave = useCallback((() => setIsHovered(!1)), []), handleMouseDown = useCallback((() => setIsActive(!0)), []), handleMouseUp = useCallback((() => setIsActive(!1)), []), handleKeyDown = useCallback((e => {
2207
2315
  !onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
@@ -2221,6 +2329,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2221
2329
  mouseOffset: mouseOffset,
2222
2330
  // This is now static (refs or props) unless prop changes
2223
2331
  overLightConfig: overLightConfig,
2332
+ resolvedBorder: resolvedBorder,
2224
2333
  transformStyle: transformStyle,
2225
2334
  getShaderTime: getShaderTime,
2226
2335
  applyTimeBasedDistortion: applyTimeBasedDistortion,
@@ -2281,148 +2390,6 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2281
2390
  *
2282
2391
  * @returns Performance tier classification
2283
2392
  */
2284
- /** Map an FPS value to a semantic color token string. */
2285
- const getQualityColor = quality => {
2286
- switch (quality) {
2287
- case "high":
2288
- return "var(--atomix-color-success, #4ade80)";
2289
-
2290
- case "medium":
2291
- return "var(--atomix-color-warning, #fbbf24)";
2292
-
2293
- case "low":
2294
- return "var(--atomix-color-danger, #ef4444)";
2295
-
2296
- default:
2297
- return "#9ca3af";
2298
- }
2299
- }, getFpsLabel = fps => fps >= 58 ? "Optimal" : fps >= 45 ? "Warning" : "Critical";
2300
-
2301
- /** Map a quality level string to a semantic color token string. */
2302
- // Inject keyframes once
2303
- if ("undefined" != typeof document) {
2304
- const styleId = "perf-dashboard-keyframes";
2305
- if (!document.getElementById(styleId)) {
2306
- const styleEl = document.createElement("style");
2307
- styleEl.id = styleId, styleEl.textContent = "\n@keyframes perf-dashboard-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n",
2308
- document.head.appendChild(styleEl);
2309
- }
2310
- }
2311
-
2312
- /**
2313
- * PerformanceDashboard - Real-time performance monitoring overlay.
2314
- *
2315
- * Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
2316
- * Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
2317
- */ const PerformanceDashboard = memo((({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
2318
- if (!isVisible) return null;
2319
- const fpsColor = (fps = metrics.fps) >= 58 ? "var(--atomix-color-success, #4ade80)" : fps >= 45 ? "var(--atomix-color-warning, #fbbf24)" : "var(--atomix-color-danger, #ef4444)";
2320
- var fps;
2321
- const isCritical = metrics.fps < 45;
2322
- return jsxs("div", {
2323
- 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",
2324
- style: {
2325
- zIndex: 9999,
2326
- minWidth: "12.5rem",
2327
- // 200px
2328
- backgroundColor: "rgba(17, 24, 39, 0.95)",
2329
- backdropFilter: "blur(8px)",
2330
- transition: "opacity 0.3s ease"
2331
- },
2332
- children: [ jsxs("div", {
2333
- className: "u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10",
2334
- children: [ jsx("span", {
2335
- className: "u-text-sm u-font-bold u-text-white",
2336
- children: "Performance Monitor"
2337
- }), onClose && jsx("button", {
2338
- 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",
2339
- onClick: onClose,
2340
- "aria-label": "Close performance dashboard",
2341
- style: {
2342
- transition: "color 0.2s ease"
2343
- },
2344
- children: "×"
2345
- }) ]
2346
- }), jsxs("div", {
2347
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
2348
- children: [ jsx("span", {
2349
- className: "u-text-gray-400 u-me-3",
2350
- children: "FPS"
2351
- }), jsx("span", {
2352
- className: "u-font-bold",
2353
- style: {
2354
- color: fpsColor
2355
- },
2356
- children: Math.round(metrics.fps)
2357
- }) ]
2358
- }), jsxs("div", {
2359
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
2360
- children: [ jsx("span", {
2361
- className: "u-text-gray-400 u-me-3",
2362
- children: "Frame Time"
2363
- }), jsxs("span", {
2364
- className: "u-font-bold",
2365
- children: [ metrics.frameTime.toFixed(2), "ms" ]
2366
- }) ]
2367
- }), jsxs("div", {
2368
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
2369
- children: [ jsx("span", {
2370
- className: "u-text-gray-400 u-me-3",
2371
- children: "Quality"
2372
- }), jsx("span", {
2373
- className: "u-font-bold u-text-uppercase",
2374
- style: {
2375
- fontSize: "0.6875rem",
2376
- // 11px
2377
- color: getQualityColor(metrics.qualityLevel)
2378
- },
2379
- children: metrics.qualityLevel
2380
- }) ]
2381
- }), metrics.gpuMemory && jsxs("div", {
2382
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
2383
- children: [ jsx("span", {
2384
- className: "u-text-gray-400 u-me-3",
2385
- children: "GPU Memory"
2386
- }), jsxs("span", {
2387
- className: "u-font-bold",
2388
- children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
2389
- }) ]
2390
- }), metrics.isAutoScaling && jsx("div", {
2391
- className: "u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center",
2392
- style: {
2393
- fontSize: "0.625rem",
2394
- // 10px
2395
- color: "#6b7280"
2396
- },
2397
- children: "Auto-scaling active"
2398
- }), jsxs("div", {
2399
- className: "u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10",
2400
- children: [ jsx("div", {
2401
- className: "u-rounded-full",
2402
- style: {
2403
- width: "0.5rem",
2404
- height: "0.5rem",
2405
- flexShrink: 0,
2406
- backgroundColor: fpsColor,
2407
- ...isCritical && {
2408
- animation: "perf-dashboard-pulse 1s infinite"
2409
- }
2410
- }
2411
- }), jsx("span", {
2412
- className: "u-text-xs",
2413
- style: {
2414
- fontSize: "0.625rem",
2415
- // 10px
2416
- color: fpsColor
2417
- },
2418
- children: getFpsLabel(metrics.fps)
2419
- }) ]
2420
- }) ]
2421
- });
2422
- }));
2423
-
2424
- PerformanceDashboard.displayName = "PerformanceDashboard";
2425
-
2426
2393
  /**
2427
2394
  * Mobile optimization presets
2428
2395
  *
@@ -2542,18 +2509,13 @@ const PERFORMANCE_PRESET = {
2542
2509
  saturation: 70
2543
2510
  }
2544
2511
  }
2545
- }, 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) {
2546
- const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useMemo((() =>
2547
- // Helper to merge refs
2548
- function(...refs) {
2549
- return node => {
2550
- refs.forEach((ref => {
2551
- "function" == typeof ref ? ref(node) : null != ref && (ref.current = node);
2552
- }));
2553
- };
2554
- }
2555
- // Internal implementation with forwardRef
2556
- (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({
2512
+ }, 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 = "", 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, border: border, withBorder: withBorder = !0, debugBorderRadius: debugBorderRadius = !1, 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) {
2513
+ const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useForkRef(ref, internalWrapperRef), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = (explicit = propsIsFixedOrSticky,
2514
+ position = restStyle.position, Boolean(explicit || "fixed" === position || "sticky" === position));
2515
+ var explicit, position;
2516
+ /**
2517
+ * Extracts layout-related properties from a React `CSSProperties` object.
2518
+ */ const {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, resolvedBorder: resolvedBorder} = useAtomixGlass({
2557
2519
  glassRef: glassRef,
2558
2520
  contentRef: contentRef,
2559
2521
  wrapperRef: internalWrapperRef,
@@ -2573,10 +2535,11 @@ const PERFORMANCE_PRESET = {
2573
2535
  blurAmount: blurAmount,
2574
2536
  saturation: saturation,
2575
2537
  withLiquidBlur: withLiquidBlur,
2576
- padding: padding,
2538
+ border: border,
2539
+ withBorder: withBorder,
2540
+ debugBorderRadius: debugBorderRadius,
2577
2541
  style: style,
2578
2542
  isFixedOrSticky: isFixedOrSticky,
2579
- // Phase 1: Animation System props
2580
2543
  withTimeAnimation: withTimeAnimation,
2581
2544
  animationSpeed: animationSpeed,
2582
2545
  withMultiLayerDistortion: withMultiLayerDistortion,
@@ -2585,8 +2548,7 @@ const PERFORMANCE_PRESET = {
2585
2548
  distortionGain: distortionGain,
2586
2549
  distortionQuality: distortionQuality
2587
2550
  });
2588
- // Responsive breakpoint system - automatically adjusts parameters based on viewport
2589
- !
2551
+ (
2590
2552
  /**
2591
2553
  * Responsive Glass Parameters Hook
2592
2554
  *
@@ -2744,7 +2706,7 @@ const PERFORMANCE_PRESET = {
2744
2706
  }
2745
2707
  /**
2746
2708
  * Get GPU memory info if available (Chrome DevTools only)
2747
- */ ({
2709
+ */)({
2748
2710
  baseParams: {
2749
2711
  ...useMemo((() =>
2750
2712
  /**
@@ -2778,9 +2740,7 @@ const PERFORMANCE_PRESET = {
2778
2740
  breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
2779
2741
  enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
2780
2742
  debug: !1
2781
- });
2782
- // Performance monitoring - tracks FPS, frame time, memory usage
2783
- const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} =
2743
+ }),
2784
2744
  /**
2785
2745
  * Performance Monitor Hook
2786
2746
  *
@@ -2815,7 +2775,13 @@ const PERFORMANCE_PRESET = {
2815
2775
  timestamp: 0,
2816
2776
  isAutoScaling: !0,
2817
2777
  lowFpsCount: 0
2818
- }), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled), frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
2778
+ }), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled);
2779
+ // Sync external `enabled` prop changes into internal state
2780
+ useEffect((() => {
2781
+ setIsEnabled(enabled ?? !0);
2782
+ }), [ enabled ]);
2783
+ // Refs for frame tracking
2784
+ const frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
2819
2785
  setMetrics((prev => ({
2820
2786
  ...prev,
2821
2787
  ...newMetrics,
@@ -2935,191 +2901,214 @@ const PERFORMANCE_PRESET = {
2935
2901
  /**
2936
2902
  * Reset to auto-scaling mode
2937
2903
  */ var fps, currentQuality;
2938
- return {
2939
- metrics: metrics,
2940
- recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
2941
- isUnderperforming: metrics.fps < minFps,
2942
- setQualityLevel: setQualityLevel,
2943
- resetAutoScaling: resetAutoScaling,
2944
- toggleMonitoring: toggleMonitoring
2945
- };
2904
+ fps = metrics.fps, currentQuality = metrics.qualityLevel, metrics.fps;
2946
2905
  }({
2947
2906
  enabled: debugPerformance,
2948
- // Enable when debugPerformance is true
2949
2907
  debug: !1,
2950
2908
  showOverlay: !1
2951
2909
  });
2952
- // Auto-start performance monitoring when debugPerformance is enabled
2953
- React.useEffect((() => {
2954
- debugPerformance && toggleMonitoring();
2955
- // eslint-disable-next-line react-hooks/exhaustive-deps
2956
- }), [ debugPerformance ]);
2957
- // Re-run when debugPerformance changes
2958
- const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
2959
- if (!isFixedOrSticky) return {};
2960
- const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
2961
- return {
2962
- ...p && {
2963
- position: p
2964
- },
2965
- ...void 0 !== t && {
2966
- top: t
2967
- },
2968
- ...void 0 !== l && {
2969
- left: l
2970
- },
2971
- ...void 0 !== r && {
2972
- right: r
2973
- },
2974
- ...void 0 !== b && {
2975
- bottom: b
2976
- }
2977
- };
2978
- }), [ isFixedOrSticky, restStyle ]);
2979
- // Calculate base style with transforms
2980
- // When layout is hoisted to the root, strip those props from the container
2981
- useMemo((() => {
2982
- if (isFixedOrSticky) {
2983
- const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
2984
- return {
2985
- ...visualStyle
2986
- };
2910
+ const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, containerStyle = useMemo((() => ({
2911
+ ...restStyle,
2912
+ ...void 0 !== customZIndex && {
2913
+ zIndex: customZIndex
2987
2914
  }
2915
+ })), [ restStyle, customZIndex ]), componentClassName = mergeClassNames(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), positionStyles = useMemo((() =>
2916
+ /**
2917
+ * Returns the internal positioning context for effect layers relative to the root.
2918
+ */
2919
+ function(isFixedOrSticky, restStyle) {
2988
2920
  return {
2989
- ...restStyle
2921
+ position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
2922
+ top: 0,
2923
+ left: 0,
2924
+ right: "auto",
2925
+ bottom: "auto"
2990
2926
  };
2991
- }), [ isFixedOrSticky, restStyle ]);
2992
- // Build className with state modifiers
2993
- 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((() => ({
2994
- position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
2995
- top: isFixedOrSticky ? restStyle.top ?? 0 : 0,
2996
- left: isFixedOrSticky ? restStyle.left ?? 0 : 0,
2997
- right: isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
2998
- bottom: isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto"
2999
- })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), adjustedSize = useMemo((() => {
3000
- // Keep a reference to positionStyles to avoid unused-variable lint,
3001
- // but sizing is driven by explicit width/height or measured size.
3002
- positionStyles.position;
3003
- 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;
2927
+ }
2928
+ /**
2929
+ * Computes `--atomix-glass-width` and `--atomix-glass-height` values.
2930
+ *
2931
+ * Fixed/sticky elements prefer explicit dimensions or measured size; in-flow
2932
+ * elements default to `100%`.
2933
+ */ (isFixedOrSticky, restStyle)), [ isFixedOrSticky, restStyle ]), adjustedSize = useMemo((() => function(options) {
2934
+ const {width: width, height: height, restStyle: restStyle, glassSize: glassSize, isFixedOrSticky: isFixedOrSticky} = options, 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;
3004
2935
  return {
3005
2936
  width: resolveLength(effectiveWidth, glassSize.width),
3006
2937
  height: resolveLength(effectiveHeight, glassSize.height)
3007
2938
  };
3008
- }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height, isFixedOrSticky ]), gradientValues = useMemo((() => {
3009
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
3010
- return {
3011
- borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
3012
- borderStop1: Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER),
3013
- borderStop2: Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER),
3014
- borderOpacities: [ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH ],
3015
- hoverPositions: {
3016
- hover1: {
3017
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
3018
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
3019
- },
3020
- hover2: {
3021
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
3022
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2
3023
- },
3024
- hover3: {
3025
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
3026
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3
3027
- }
3028
- },
3029
- basePosition: {
3030
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
3031
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
3032
- },
3033
- mx: mx,
3034
- my: my,
3035
- absMx: absMx,
3036
- absMy: absMy
3037
- };
3038
- }), [ 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((() => ({
3039
- hover1: isHovered || isActive ? .5 : 0,
3040
- hover2: isActive ? .5 : 0,
3041
- hover3: isHovered ? .4 : isActive ? .8 : 0,
3042
- base: isOverLight ? clampedOverLightOpacity || .4 : 0,
3043
- over: isOverLight ? 1.1 * (clampedOverLightOpacity || .4) : 0
3044
- })), [ isHovered, isActive, isOverLight, clampedOverLightOpacity ]), glassVars = useMemo((() => {
3045
- 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;
2939
+ }
2940
+ /**
2941
+ * Builds the CSS custom properties applied to the root `.c-atomix-glass` element.
2942
+ *
2943
+ * These variables drive layer geometry, transforms, and stacking offsets. They
2944
+ * must not include layout properties that would interfere with backdrop-filter.
2945
+ */ ({
2946
+ width: width,
2947
+ height: height,
2948
+ restStyle: restStyle,
2949
+ glassSize: glassSize,
2950
+ isFixedOrSticky: isFixedOrSticky
2951
+ })), [ width, height, restStyle, glassSize, isFixedOrSticky ]), glassVars = useMemo((() => function(input) {
2952
+ const {effectiveBorderRadius: effectiveBorderRadius, transformStyle: transformStyle, adjustedSize: adjustedSize, isOverLight: isOverLight, customZIndex: customZIndex, isFixedOrSticky: isFixedOrSticky, positionStyles: positionStyles, restStyle: restStyle, borderWidth: borderWidth = ATOMIX_GLASS.BORDER.DEFAULT_WIDTH} = input, layerPosition =
2953
+ /**
2954
+ * Resolves the `--atomix-glass-position` value for decorative layers.
2955
+ *
2956
+ * Fixed/sticky layers use the same positioning mode as the container; in-flow
2957
+ * layers default to the internal absolute positioning context.
2958
+ */
2959
+ function(isFixedOrSticky, positionStyles, restStyle) {
2960
+ return isFixedOrSticky ? `${function(style) {
2961
+ const {position: position, top: top, left: left, right: right, bottom: bottom, inset: inset} = style;
2962
+ return {
2963
+ ...null != position && {
2964
+ position: position
2965
+ },
2966
+ ...void 0 !== top && {
2967
+ top: top
2968
+ },
2969
+ ...void 0 !== left && {
2970
+ left: left
2971
+ },
2972
+ ...void 0 !== right && {
2973
+ right: right
2974
+ },
2975
+ ...void 0 !== bottom && {
2976
+ bottom: bottom
2977
+ },
2978
+ ...void 0 !== inset && {
2979
+ inset: inset
2980
+ }
2981
+ };
2982
+ }
2983
+ /**
2984
+ * Resolves inset custom properties for decorative layers (hover, borders, backgrounds).
2985
+ *
2986
+ * For fixed and sticky modes, insets mirror the container so sibling layers remain
2987
+ * aligned. In-flow modes, insets follow the consumer `style` when a non-default
2988
+ * `position` is provided.
2989
+ */ (restStyle).position ?? restStyle.position ?? "fixed"}` : `${positionStyles.position}`;
2990
+ }(isFixedOrSticky, positionStyles, restStyle), layerInsets = function(isFixedOrSticky, restStyle) {
2991
+ if (isFixedOrSticky) return {
2992
+ top: formatGlassInsetValue(restStyle.top, 0),
2993
+ left: formatGlassInsetValue(restStyle.left, 0),
2994
+ right: formatGlassInsetValue(restStyle.right, "auto"),
2995
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
2996
+ };
2997
+ const position = restStyle.position;
2998
+ return null != position && "static" !== position && "relative" !== position ? {
2999
+ top: formatGlassInsetValue(restStyle.top, 0),
3000
+ left: formatGlassInsetValue(restStyle.left, 0),
3001
+ right: formatGlassInsetValue(restStyle.right, "auto"),
3002
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
3003
+ } : {
3004
+ top: "0px",
3005
+ left: "0px",
3006
+ right: "auto",
3007
+ bottom: "auto"
3008
+ };
3009
+ }(isFixedOrSticky, restStyle);
3046
3010
  return {
3047
3011
  ...void 0 !== customZIndex && {
3048
3012
  "--atomix-glass-base-z-index": customZIndex
3049
3013
  },
3050
3014
  "--atomix-glass-radius": `${effectiveBorderRadius}px`,
3051
3015
  "--atomix-glass-transform": transformStyle || "none",
3052
- "--atomix-glass-container-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
3053
- "--atomix-glass-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
3054
- "--atomix-glass-top": `${isFixedOrSticky ? restStyle.top ?? 0 : 0}px`,
3055
- "--atomix-glass-left": `${isFixedOrSticky ? restStyle.left ?? 0 : 0}px`,
3056
- "--atomix-glass-right": isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
3057
- "--atomix-glass-bottom": isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto",
3016
+ "--atomix-glass-container-position": layerPosition,
3017
+ "--atomix-glass-position": layerPosition,
3018
+ "--atomix-glass-top": layerInsets.top,
3019
+ "--atomix-glass-left": layerInsets.left,
3020
+ "--atomix-glass-right": layerInsets.right,
3021
+ "--atomix-glass-bottom": layerInsets.bottom,
3058
3022
  "--atomix-glass-width": adjustedSize.width,
3059
3023
  "--atomix-glass-height": adjustedSize.height,
3060
- "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.125rem)",
3061
- "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
3062
- "--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%)`,
3063
- "--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%)`,
3064
- "--atomix-glass-hover-1-opacity": opacityValues.hover1,
3065
- "--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}%)`,
3066
- "--atomix-glass-hover-2-opacity": opacityValues.hover2,
3067
- "--atomix-glass-hover-2-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`,
3068
- "--atomix-glass-hover-3-opacity": opacityValues.hover3,
3069
- "--atomix-glass-hover-3-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`,
3070
- "--atomix-glass-base-opacity": opacityValues.base,
3071
- "--atomix-glass-base-gradient": isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
3072
- "--atomix-glass-overlay-opacity": opacityValues.over,
3073
- "--atomix-glass-overlay-gradient": isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
3074
- "--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
3075
- "--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}%)`
3024
+ // Aliases maintained for backward compatibility and consumer overrides.
3025
+ "--atomix-glass-container-width": adjustedSize.width,
3026
+ "--atomix-glass-container-height": adjustedSize.height,
3027
+ [ATOMIX_GLASS.BORDER.WIDTH_CSS_VAR]: borderWidth,
3028
+ "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay"
3076
3029
  };
3077
- }), [ 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", {
3078
- 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(" ")
3030
+ }
3031
+ /**
3032
+ * Applies mode-specific multipliers and accessibility overrides to container effects.
3033
+ */ ({
3034
+ effectiveBorderRadius: effectiveBorderRadius,
3035
+ transformStyle: transformStyle,
3036
+ adjustedSize: adjustedSize,
3037
+ isOverLight: isOverLight,
3038
+ customZIndex: customZIndex,
3039
+ isFixedOrSticky: isFixedOrSticky,
3040
+ positionStyles: positionStyles,
3041
+ restStyle: restStyle,
3042
+ borderWidth: resolvedBorder.width
3043
+ })), [ effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, customZIndex, isFixedOrSticky, positionStyles, restStyle, resolvedBorder.width ]), containerEffects = useMemo((() => function(options) {
3044
+ const {MULTIPLIERS: MULTIPLIERS, SATURATION: SATURATION} = ATOMIX_GLASS.CONSTANTS, zeroMouse = {
3045
+ x: 0,
3046
+ y: 0
3047
+ }, resolveSaturation = () => options.effectiveHighContrast ? SATURATION.HIGH_CONTRAST : options.isOverLight ? options.saturation * options.saturationBoost : options.saturation;
3048
+ if (options.effectiveWithoutEffects) return {
3049
+ displacementScale: 0,
3050
+ blurAmount: 0,
3051
+ saturation: resolveSaturation(),
3052
+ aberrationIntensity: 0,
3053
+ mouseOffset: zeroMouse,
3054
+ globalMousePosition: zeroMouse
3055
+ };
3056
+ let resolvedDisplacement = options.displacementScale;
3057
+ "shader" === options.mode ? resolvedDisplacement *= MULTIPLIERS.SHADER_DISPLACEMENT : options.isOverLight && (resolvedDisplacement *= MULTIPLIERS.OVER_LIGHT_DISPLACEMENT);
3058
+ let resolvedAberration = options.aberrationIntensity;
3059
+ return "shader" === options.mode && (resolvedAberration *= MULTIPLIERS.SHADER_ABERRATION),
3060
+ {
3061
+ displacementScale: resolvedDisplacement,
3062
+ blurAmount: options.blurAmount,
3063
+ saturation: resolveSaturation(),
3064
+ aberrationIntensity: resolvedAberration,
3065
+ mouseOffset: options.mouseOffset,
3066
+ globalMousePosition: options.globalMousePosition
3067
+ };
3068
+ }({
3069
+ displacementScale: displacementScale,
3070
+ blurAmount: blurAmount,
3071
+ saturation: saturation,
3072
+ aberrationIntensity: aberrationIntensity,
3073
+ mode: mode,
3074
+ effectiveWithoutEffects: effectiveWithoutEffects,
3075
+ effectiveHighContrast: effectiveHighContrast,
3076
+ isOverLight: isOverLight,
3077
+ saturationBoost: overLightConfig.saturationBoost,
3078
+ mouseOffset: mouseOffset,
3079
+ globalMousePosition: globalMousePosition
3080
+ })), [ displacementScale, blurAmount, saturation, aberrationIntensity, mode, effectiveWithoutEffects, effectiveHighContrast, isOverLight, overLightConfig.saturationBoost, mouseOffset, globalMousePosition ]), renderBackgroundLayer = layerType => jsx("div", {
3081
+ "aria-hidden": "true",
3082
+ className: mergeClassNames(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)
3079
3083
  });
3080
- // Calculate position and size styles for internal layers
3081
- // When root is fixed/sticky, internal layers use absolute (relative to root)
3082
- return jsxs("div", {
3084
+ return jsxs("div", {
3083
3085
  ...rest,
3084
3086
  ref: mergedRef,
3085
3087
  className: componentClassName,
3086
- style: {
3087
- ...glassVars
3088
- },
3088
+ style: glassVars,
3089
3089
  role: role || (onClick ? "button" : void 0),
3090
3090
  tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
3091
3091
  "aria-label": ariaLabel,
3092
3092
  "aria-describedby": ariaDescribedBy,
3093
3093
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
3094
- "aria-pressed": onClick ? isActive : void 0,
3095
3094
  onKeyDown: onClick ? handleKeyDown : void 0,
3096
3095
  children: [ jsx(AtomixGlassContainer, {
3097
3096
  ref: glassRef,
3098
3097
  contentRef: contentRef,
3099
3098
  className: className,
3100
- style: {
3101
- ...restStyle
3102
- },
3099
+ style: containerStyle,
3103
3100
  borderRadius: effectiveBorderRadius,
3104
- displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
3105
- blurAmount: effectiveWithoutEffects ? 0 : blurAmount,
3106
- saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
3107
- aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
3101
+ displacementScale: containerEffects.displacementScale,
3102
+ blurAmount: containerEffects.blurAmount,
3103
+ saturation: containerEffects.saturation,
3104
+ aberrationIntensity: containerEffects.aberrationIntensity,
3108
3105
  glassSize: glassSize,
3109
- padding: padding,
3110
- mouseOffset: effectiveWithoutEffects ? {
3111
- x: 0,
3112
- y: 0
3113
- } : mouseOffset,
3114
- globalMousePosition: effectiveWithoutEffects ? {
3115
- x: 0,
3116
- y: 0
3117
- } : globalMousePosition,
3106
+ mouseOffset: containerEffects.mouseOffset,
3107
+ globalMousePosition: containerEffects.globalMousePosition,
3118
3108
  onMouseEnter: handleMouseEnter,
3119
3109
  onMouseLeave: handleMouseLeave,
3120
3110
  onMouseDown: handleMouseDown,
3121
3111
  onMouseUp: handleMouseUp,
3122
- isHovered: isHovered,
3123
3112
  isActive: isActive,
3124
3113
  overLight: isOverLight,
3125
3114
  overLightConfig: {
@@ -3135,7 +3124,6 @@ const PERFORMANCE_PRESET = {
3135
3124
  shaderVariant: shaderVariant,
3136
3125
  withLiquidBlur: withLiquidBlur,
3137
3126
  isFixedOrSticky: isFixedOrSticky,
3138
- // Phase 1: Animation System props
3139
3127
  shaderTime: getShaderTime(),
3140
3128
  withTimeAnimation: withTimeAnimation,
3141
3129
  animationSpeed: animationSpeed,
@@ -3147,32 +3135,39 @@ const PERFORMANCE_PRESET = {
3147
3135
  children: children
3148
3136
  }), Boolean(onClick) && jsxs(Fragment, {
3149
3137
  children: [ jsx("div", {
3138
+ "aria-hidden": "true",
3150
3139
  className: ATOMIX_GLASS.HOVER_1_CLASS
3151
3140
  }), jsx("div", {
3141
+ "aria-hidden": "true",
3152
3142
  className: ATOMIX_GLASS.HOVER_2_CLASS
3153
3143
  }), jsx("div", {
3144
+ "aria-hidden": "true",
3154
3145
  className: ATOMIX_GLASS.HOVER_3_CLASS
3155
3146
  }) ]
3156
- }), renderBackgroundLayer("dark"), renderBackgroundLayer("black"), shouldRenderOverLightLayers && jsxs(Fragment, {
3147
+ }), [ "dark", "black" ].map((layerType => jsx(React.Fragment, {
3148
+ children: renderBackgroundLayer(layerType)
3149
+ }, layerType))), shouldRenderOverLightLayers && jsxs(Fragment, {
3157
3150
  children: [ jsx("div", {
3151
+ "aria-hidden": "true",
3158
3152
  className: ATOMIX_GLASS.BASE_LAYER_CLASS
3159
3153
  }), jsx("div", {
3154
+ "aria-hidden": "true",
3160
3155
  className: ATOMIX_GLASS.OVERLAY_LAYER_CLASS
3161
3156
  }), jsx("div", {
3157
+ "aria-hidden": "true",
3162
3158
  className: ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS
3163
3159
  }) ]
3164
- }), withBorder && jsxs(Fragment, {
3160
+ }), resolvedBorder.enabled && jsxs(Fragment, {
3165
3161
  children: [ jsx("span", {
3162
+ "aria-hidden": "true",
3166
3163
  className: ATOMIX_GLASS.BORDER_BACKDROP_CLASS
3167
3164
  }), jsx("span", {
3165
+ "aria-hidden": "true",
3168
3166
  className: ATOMIX_GLASS.BORDER_1_CLASS
3169
3167
  }), jsx("span", {
3168
+ "aria-hidden": "true",
3170
3169
  className: ATOMIX_GLASS.BORDER_2_CLASS
3171
3170
  }) ]
3172
- }), debugPerformance && performanceMetrics && jsx(PerformanceDashboard, {
3173
- metrics: performanceMetrics,
3174
- isVisible: !0,
3175
- onClose: () => {}
3176
3171
  }) ]
3177
3172
  });
3178
3173
  }));
@@ -3182,10 +3177,7 @@ const PERFORMANCE_PRESET = {
3182
3177
  * Default preset for most mobile devices
3183
3178
  */ AtomixGlassInner.displayName = "AtomixGlass";
3184
3179
 
3185
- /**
3186
- * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
3187
- * Ref is forwarded to the root `<div>` element.
3188
- */
3180
+ /** Memoized public export. Ref targets the root `.c-atomix-glass` wrapper. */
3189
3181
  const AtomixGlass = memo(AtomixGlassInner), DefaultIcon = () => jsx("i", {
3190
3182
  className: "c-accordion__icon",
3191
3183
  "aria-hidden": "true",
@@ -3364,10 +3356,7 @@ const AccordionImpl = memo((({title: title, children: children, defaultOpen: de
3364
3356
  });
3365
3357
  if (glass) {
3366
3358
  // Default glass settings for accordions
3367
- const defaultGlassProps = {
3368
- displacementScale: 20,
3369
- elasticity: 0
3370
- }, glassProps = !0 === glass ? defaultGlassProps : {
3359
+ const defaultGlassProps = GLASS_DEFAULTS_ACCORDION, glassProps = !0 === glass ? defaultGlassProps : {
3371
3360
  ...defaultGlassProps,
3372
3361
  ...glass
3373
3362
  };
@@ -3444,10 +3433,10 @@ const Accordion = AccordionWithSubcomponents, Badge = memo((({label: label, var
3444
3433
  if (glass) {
3445
3434
  // Default glass settings for badges
3446
3435
  const defaultGlassProps = {
3447
- displacementScale: 20,
3448
- borderRadius: ref.current?.getBoundingClientRect().width ? ref.current?.getBoundingClientRect().width / 2 : 16,
3449
- className: "c-badge--glass",
3450
- elasticity: 0
3436
+ ...GLASS_DEFAULTS_BADGE,
3437
+ // Override borderRadius dynamically if the ref is available
3438
+ borderRadius: ref.current?.getBoundingClientRect().width ? ref.current?.getBoundingClientRect().width / 2 : GLASS_DEFAULTS_BADGE.borderRadius,
3439
+ className: "c-badge--glass"
3451
3440
  }, glassProps = !0 === glass ? defaultGlassProps : {
3452
3441
  ...defaultGlassProps,
3453
3442
  ...glass
@@ -3697,12 +3686,7 @@ const Spinner = memo( forwardRef((({size: size = "md", variant: variant = "prim
3697
3686
  })
3698
3687
  });
3699
3688
  if (glass) {
3700
- const defaultGlassProps = {
3701
- displacementScale: 20,
3702
- blurAmount: 1,
3703
- borderRadius: 999,
3704
- mode: "shader"
3705
- }, glassProps = !0 === glass ? defaultGlassProps : {
3689
+ const defaultGlassProps = GLASS_DEFAULTS_SPINNER, glassProps = !0 === glass ? defaultGlassProps : {
3706
3690
  ...defaultGlassProps,
3707
3691
  ...glass
3708
3692
  };
@@ -3982,11 +3966,7 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
3982
3966
  // This is a safe fallback for disabled links.
3983
3967
  if (glass) {
3984
3968
  // Default glass props
3985
- const defaultGlassProps = {
3986
- displacementScale: 20,
3987
- blurAmount: 0,
3988
- saturation: 200
3989
- }, glassProps = !0 === glass ? defaultGlassProps : {
3969
+ const defaultGlassProps = GLASS_DEFAULTS_BUTTON, glassProps = !0 === glass ? defaultGlassProps : {
3990
3970
  ...defaultGlassProps,
3991
3971
  ...glass
3992
3972
  };
@@ -4426,11 +4406,7 @@ const Callout = memo((({title: title, children: children, icon: icon, variant:
4426
4406
  // Determine appropriate ARIA attributes based on variant
4427
4407
  if (glass) {
4428
4408
  // Default glass settings for callouts
4429
- const defaultGlassProps = {
4430
- displacementScale: 30,
4431
- borderRadius: 8,
4432
- elasticity: 0
4433
- }, glassProps = !0 === glass ? defaultGlassProps : {
4409
+ const defaultGlassProps = GLASS_DEFAULTS_CALLOUT, glassProps = !0 === glass ? defaultGlassProps : {
4434
4410
  ...defaultGlassProps,
4435
4411
  ...glass
4436
4412
  };