@shohojdhara/atomix 0.6.3 → 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 +119 -40
  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 +69 -31
  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
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode, forwardRef, createContext } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
- import { POPOVER } from '../../lib/constants/components';
3
+ import { POPOVER, GLASS_DEFAULTS } from '../../lib/constants/components';
4
4
  import { usePopover } from '../../lib/composables/usePopover';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
  import type { PopoverProps, PopoverTriggerProps } from '../../lib/types/components';
@@ -81,14 +81,7 @@ export const Popover: React.FC<PopoverProps> = ({
81
81
  {glass ? (
82
82
  // Default glass settings for popovers
83
83
  (() => {
84
- const defaultGlassProps = {
85
- displacementScale: 50,
86
- blurAmount: 1,
87
- saturation: 160,
88
- aberrationIntensity: 0.5,
89
- borderRadius: 8,
90
- mode: 'shader' as const,
91
- };
84
+ const defaultGlassProps = GLASS_DEFAULTS.POPOVER;
92
85
 
93
86
  const glassProps =
94
87
  glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, memo } from 'react';
2
2
  import { ProgressProps } from '../../lib/types/components';
3
3
  import { useProgress } from '../../lib/composables/useProgress';
4
- import { PROGRESS } from '../../lib/constants/components';
4
+ import { PROGRESS, GLASS_DEFAULTS } from '../../lib/constants/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
7
  export const Progress = memo(
@@ -43,12 +43,7 @@ export const Progress = memo(
43
43
  );
44
44
 
45
45
  if (glass) {
46
- const defaultGlassProps = {
47
- displacementScale: 30,
48
- blurAmount: 0.5,
49
- borderRadius: 8,
50
- mode: 'shader' as const,
51
- };
46
+ const defaultGlassProps = GLASS_DEFAULTS.PROGRESS;
52
47
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
53
48
  return <AtomixGlass {...glassProps}>{progressContent}</AtomixGlass>;
54
49
  }
@@ -1,5 +1,5 @@
1
1
  import React, { useRef, useEffect, useCallback, forwardRef } from 'react';
2
- import { THEME_COLORS, SIZES, RATING } from '../../lib/constants/components';
2
+ import { THEME_COLORS, SIZES, RATING, GLASS_DEFAULTS } from '../../lib/constants/components';
3
3
  import { useRating } from '../../lib/composables/useRating';
4
4
  import type { RatingProps } from '../../lib/types/components';
5
5
  import useForkRef from '../../lib/utils/useForkRef';
@@ -275,15 +275,7 @@ export const Rating = forwardRef<HTMLDivElement, RatingProps>(
275
275
  );
276
276
 
277
277
  if (glass) {
278
- // Default glass settings for ratings
279
- const defaultGlassProps = {
280
- displacementScale: 60,
281
- blurAmount: 1,
282
- saturation: 160,
283
- aberrationIntensity: 0.5,
284
- borderRadius: 8,
285
- mode: 'shader' as const,
286
- };
278
+ const defaultGlassProps = GLASS_DEFAULTS.RATING;
287
279
 
288
280
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
289
281
 
@@ -1,7 +1,7 @@
1
1
  import React, { memo, forwardRef } from 'react';
2
2
  import { SpinnerProps } from '../../lib/types/components';
3
3
  import { useSpinner } from '../../lib/composables/useSpinner';
4
- import { SPINNER } from '../../lib/constants/components';
4
+ import { SPINNER, GLASS_DEFAULTS } from '../../lib/constants/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
7
  export const Spinner = memo(
@@ -51,12 +51,7 @@ export const Spinner = memo(
51
51
  );
52
52
 
53
53
  if (glass) {
54
- const defaultGlassProps = {
55
- displacementScale: 20,
56
- blurAmount: 1,
57
- borderRadius: 999,
58
- mode: 'shader' as const,
59
- };
54
+ const defaultGlassProps = GLASS_DEFAULTS.SPINNER;
60
55
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
61
56
  return <AtomixGlass {...glassProps}>{spinnerContent}</AtomixGlass>;
62
57
  }
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useState, ReactNode, forwardRef, Children, cloneElement, isValidElement } from 'react';
2
- import { STEPS } from '../../lib/constants/components';
2
+ import { STEPS, GLASS_DEFAULTS } from '../../lib/constants/components';
3
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
4
  import { AtomixGlassProps } from '../../lib/types/components';
5
5
 
@@ -225,15 +225,7 @@ const StepsComp: React.FC<StepsProps> = ({
225
225
  );
226
226
 
227
227
  if (glass) {
228
- // Default glass settings for steps
229
- const defaultGlassProps = {
230
- displacementScale: 60,
231
- blurAmount: 1,
232
- saturation: 160,
233
- aberrationIntensity: 0.5,
234
- borderRadius: 8,
235
- mode: 'shader' as const,
236
- };
228
+ const defaultGlassProps = GLASS_DEFAULTS.STEPS;
237
229
 
238
230
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
239
231
 
@@ -7,7 +7,7 @@ import React, {
7
7
  forwardRef,
8
8
  ComponentType,
9
9
  } from 'react';
10
- import { TAB } from '../../lib/constants/components';
10
+ import { TAB, GLASS_DEFAULTS } from '../../lib/constants/components';
11
11
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
12
12
  import { AtomixGlassProps } from '../../lib/types/components';
13
13
 
@@ -354,14 +354,7 @@ const TabsComponentBase = ({
354
354
 
355
355
  if (glass) {
356
356
  // Default glass settings for tabs
357
- const defaultGlassProps = {
358
- displacementScale: 60,
359
- blurAmount: 1,
360
- saturation: 160,
361
- aberrationIntensity: 0.5,
362
- borderRadius: 8,
363
- mode: 'shader' as const,
364
- };
357
+ const defaultGlassProps = GLASS_DEFAULTS.TABS;
365
358
 
366
359
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
367
360
 
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useCallback, useEffect } from 'react';
2
- import { TOGGLE } from '../../lib/constants/components';
2
+ import { TOGGLE, GLASS_DEFAULTS } from '../../lib/constants/components';
3
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
4
  import { AtomixGlassProps, BaseComponentProps } from '../../lib/types/components';
5
5
 
@@ -115,15 +115,7 @@ export const Toggle: React.FC<ToggleProps> = ({
115
115
  );
116
116
 
117
117
  if (glass) {
118
- // Default glass settings for toggles
119
- const defaultGlassProps = {
120
- displacementScale: 60,
121
- blurAmount: 1,
122
- saturation: 160,
123
- aberrationIntensity: 0.5,
124
- borderRadius: 8,
125
- mode: 'shader' as const,
126
- };
118
+ const defaultGlassProps = GLASS_DEFAULTS.TOGGLE;
127
119
 
128
120
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
129
121
 
@@ -1,5 +1,5 @@
1
1
  import React, { ReactNode, memo } from 'react';
2
- import { TOOLTIP } from '../../lib/constants/components';
2
+ import { TOOLTIP, GLASS_DEFAULTS } from '../../lib/constants/components';
3
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
4
  import { AtomixGlassProps } from '../../lib/types/components';
5
5
  import {
@@ -118,10 +118,7 @@ export const Tooltip: React.FC<TooltipProps> = memo(
118
118
  );
119
119
 
120
120
  if (glass) {
121
- const defaultGlassProps = {
122
- displacementScale: 100,
123
- blurAmount: 3,
124
- };
121
+ const defaultGlassProps = GLASS_DEFAULTS.TOOLTIP;
125
122
 
126
123
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
127
124
 
@@ -19,6 +19,10 @@ import {
19
19
  calculateVelocity,
20
20
  smoothstep,
21
21
  } from '../../components/AtomixGlass/glass-utils';
22
+ import {
23
+ normalizeBorderConfig,
24
+ type ResolvedGlassBorderConfig,
25
+ } from '../../components/AtomixGlass/glass-border-styles';
22
26
  import { updateAtomixGlassStyles } from './useAtomixGlassStyles';
23
27
  // Phase 1: Time-Based Animation System
24
28
  import {
@@ -191,6 +195,9 @@ interface UseAtomixGlassReturn {
191
195
  // Transform calculations
192
196
  transformStyle: string;
193
197
 
198
+ /** Resolved liquid glass rim configuration */
199
+ resolvedBorder: ResolvedGlassBorderConfig;
200
+
194
201
  // Phase 1: Animation System - Shader time control
195
202
  getShaderTime: () => number;
196
203
  applyTimeBasedDistortion: (uv: { x: number; y: number }) => { x: number; y: number };
@@ -221,6 +228,8 @@ export function useAtomixGlass({
221
228
  reducedMotion = false,
222
229
  highContrast = false,
223
230
  withoutEffects = false,
231
+ border,
232
+ withBorder = true,
224
233
  elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY,
225
234
  onClick,
226
235
  debugBorderRadius = false,
@@ -228,7 +237,7 @@ export function useAtomixGlass({
228
237
  children,
229
238
  blurAmount,
230
239
  saturation,
231
- padding,
240
+
232
241
  withLiquidBlur,
233
242
  isFixedOrSticky = false,
234
243
  priority = 1, // Default priority
@@ -245,6 +254,11 @@ export function useAtomixGlass({
245
254
  const [isHovered, setIsHovered] = useState(false);
246
255
  const [isActive, setIsActive] = useState(false);
247
256
 
257
+ const resolvedBorder = useMemo(
258
+ () => normalizeBorderConfig(border, withBorder),
259
+ [border, withBorder]
260
+ );
261
+
248
262
  // Mouse tracking refs
249
263
  const cachedRectRef = useRef<DOMRect | null>(null);
250
264
  const internalGlobalMousePositionRef = useRef<MousePosition>({ x: 0, y: 0 });
@@ -678,11 +692,14 @@ export function useAtomixGlass({
678
692
  isOverLight,
679
693
  threshold: 0.7,
680
694
  opacity: baseOpacity,
681
- contrast: 1.4,
682
- brightness: 0.9,
683
- saturationBoost: 1.3, // Fixed value — dynamic saturation amplifies perceived displacement
684
- shadowIntensity: 0.9,
685
- borderOpacity: 0.7,
695
+ // Dark UI (Apple Music): neutral contrast + slight brightness lift
696
+ contrast: isOverLight ? 1.4 : 1.02,
697
+ brightness: isOverLight ? 0.9 : 1.02,
698
+ saturationBoost: isOverLight ? 1.3 : 1.0,
699
+ shadowIntensity: isOverLight ? 0.9 : 1.0,
700
+ borderOpacity: isOverLight
701
+ ? ATOMIX_GLASS.BORDER.OVER_LIGHT.opacity
702
+ : ATOMIX_GLASS.BORDER.DARK.opacity,
686
703
  };
687
704
 
688
705
  if (typeof overLight === 'object' && overLight !== null) {
@@ -713,6 +730,12 @@ export function useAtomixGlass({
713
730
  3.0,
714
731
  baseConfig.saturationBoost
715
732
  );
733
+ const validatedBorderOpacity = validateConfigValue(
734
+ objConfig.borderOpacity,
735
+ 0.1,
736
+ 1.0,
737
+ baseConfig.borderOpacity
738
+ );
716
739
 
717
740
  const finalConfig = {
718
741
  ...baseConfig,
@@ -721,6 +744,7 @@ export function useAtomixGlass({
721
744
  contrast: validatedContrast,
722
745
  brightness: validatedBrightness,
723
746
  saturationBoost: validatedSaturationBoost,
747
+ borderOpacity: validatedBorderOpacity,
724
748
  };
725
749
 
726
750
  if (
@@ -958,8 +982,10 @@ export function useAtomixGlass({
958
982
  withLiquidBlur,
959
983
  blurAmount,
960
984
  saturation,
961
- padding,
985
+
962
986
  isFixedOrSticky,
987
+ borderAnimated: resolvedBorder.animated,
988
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier,
963
989
  });
964
990
 
965
991
  // ── Stop check ──────────────────────────────────────────────────
@@ -1000,8 +1026,10 @@ export function useAtomixGlass({
1000
1026
  withLiquidBlur,
1001
1027
  blurAmount,
1002
1028
  saturation,
1003
- padding,
1029
+
1004
1030
  isFixedOrSticky,
1031
+ resolvedBorder.animated,
1032
+ resolvedBorder.opacityMultiplier,
1005
1033
  stopLerpLoop,
1006
1034
  ]);
1007
1035
 
@@ -1140,7 +1168,8 @@ export function useAtomixGlass({
1140
1168
  withLiquidBlur,
1141
1169
  blurAmount,
1142
1170
  saturation,
1143
- padding,
1171
+ borderAnimated: resolvedBorder.animated,
1172
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier,
1144
1173
  });
1145
1174
  }, [
1146
1175
  isHovered,
@@ -1158,7 +1187,8 @@ export function useAtomixGlass({
1158
1187
  withLiquidBlur,
1159
1188
  blurAmount,
1160
1189
  saturation,
1161
- padding,
1190
+ resolvedBorder.animated,
1191
+ resolvedBorder.opacityMultiplier,
1162
1192
  onClick,
1163
1193
  ]);
1164
1194
 
@@ -1191,6 +1221,7 @@ export function useAtomixGlass({
1191
1221
  globalMousePosition, // This is now static (refs or props) unless prop changes
1192
1222
  mouseOffset, // This is now static (refs or props) unless prop changes
1193
1223
  overLightConfig,
1224
+ resolvedBorder,
1194
1225
  transformStyle,
1195
1226
  getShaderTime,
1196
1227
  applyTimeBasedDistortion,
@@ -1,13 +1,16 @@
1
1
  import { ATOMIX_GLASS } from '../constants/components';
2
2
  import {
3
- calculateDistance,
3
+ buildGlassBorderCssVars,
4
+ computeBorderTensionFactor,
5
+ } from '../../components/AtomixGlass/glass-border-styles';
6
+ import {
4
7
  calculateMouseInfluence,
5
8
  validateGlassSize,
6
9
  clampBlur,
7
10
  smoothstep,
8
11
  softClamp,
9
12
  } from '../../components/AtomixGlass/glass-utils';
10
- import type { GlassSize, MousePosition, OverLightObjectConfig } from '../types/components';
13
+ import type { GlassSize, MousePosition } from '../types/components';
11
14
 
12
15
  /**
13
16
  * Updates the styles of the AtomixGlass wrapper and container elements imperatively
@@ -44,8 +47,10 @@ export const updateAtomixGlassStyles = (
44
47
  withLiquidBlur?: boolean;
45
48
  blurAmount?: number;
46
49
  saturation?: number;
47
- padding?: string;
50
+
48
51
  isFixedOrSticky?: boolean;
52
+ borderAnimated?: boolean;
53
+ borderOpacityMultiplier?: number;
49
54
  }
50
55
  ) => {
51
56
  if (!wrapperElement && !containerElement) return;
@@ -71,8 +76,10 @@ export const updateAtomixGlassStyles = (
71
76
  withLiquidBlur,
72
77
  blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
73
78
  saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
74
- padding = ATOMIX_GLASS.DEFAULTS.PADDING,
79
+
75
80
  isFixedOrSticky = false,
81
+ borderAnimated = true,
82
+ borderOpacityMultiplier = 1,
76
83
  } = params;
77
84
 
78
85
  // Calculate mouse influence
@@ -104,8 +111,7 @@ export const updateAtomixGlassStyles = (
104
111
  : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`;
105
112
 
106
113
  // ── Apple Liquid Depth Refinements ───────────────────────────────
107
- const stretchMagnitude = calculateDistance({ x: 0, y: 0 }, elasticTranslation);
108
- const tensionFactor = smoothstep(stretchMagnitude / 80);
114
+ const tensionFactor = computeBorderTensionFactor(elasticTranslation);
109
115
 
110
116
  // Subtle lighting boost on stretch
111
117
  const lightingContrast = Math.min(1.8, overLightConfig.contrast + tensionFactor * 0.2);
@@ -118,42 +124,6 @@ export const updateAtomixGlassStyles = (
118
124
  const absMx = Math.abs(mx);
119
125
  const absMy = Math.abs(my);
120
126
  const GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
121
-
122
- // ── Velocity-Based Rotation ─────────────────────────────────────
123
- // Combine mouse offset with velocity for dynamic rotation
124
- const velocityRotation =
125
- (mouseVelocity.x + elasticVelocity.x) * (GRADIENT.VELOCITY_ANGLE_MULTIPLIER || 2.5);
126
- const borderGradientAngle =
127
- GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER + velocityRotation;
128
-
129
- // Chromatic offsets for depth
130
- const chromaticOffset = GRADIENT.CHROMATIC_OFFSET || 1.5;
131
- const angleR = borderGradientAngle - chromaticOffset;
132
- const angleB = borderGradientAngle + chromaticOffset;
133
-
134
- const borderStop1 = Math.max(
135
- GRADIENT.BORDER_STOP_1.MIN,
136
- GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
137
- );
138
- const borderStop2 = Math.min(
139
- GRADIENT.BORDER_STOP_2.MAX,
140
- GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
141
- );
142
-
143
- // Modulate border opacity by tension (glow on stretch)
144
- const tensionGlow = 1 + tensionFactor * 0.5;
145
- const borderOpacities = [
146
- (GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) *
147
- tensionGlow,
148
- (GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) *
149
- tensionGlow,
150
- (GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) *
151
- tensionGlow,
152
- (GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) *
153
- tensionGlow,
154
- ];
155
-
156
- const configBorderOpacity = overLightConfig.borderOpacity;
157
127
  const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE;
158
128
  const blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK;
159
129
 
@@ -177,12 +147,19 @@ export const updateAtomixGlassStyles = (
177
147
  y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER,
178
148
  };
179
149
 
150
+ // Opacity is either 0 (hidden) or 1 (visible) — actual visual intensity is
151
+ // encoded in each gradient's rgba alpha. The typed @property CSS vars
152
+ // transition these 0→1 values smoothly via CSS (no JS animation needed).
180
153
  const opacityValues = {
181
- hover1: isHovered || isActive ? 0.5 : 0,
182
- hover2: isActive ? 0.5 : 0,
183
- hover3: isHovered ? 0.4 : isActive ? 0.8 : 0,
184
- base: isOverLight ? overLightConfig.opacity : 0,
185
- over: isOverLight ? overLightConfig.opacity * 1.1 : 0,
154
+ // hover-1: ambient highlight glow present on hover and during press
155
+ hover1: isHovered || isActive ? 1 : 0,
156
+ // hover-2: press depth shadow only fires on active (mousedown)
157
+ hover2: isActive ? 1 : 0,
158
+ // hover-3: global soft-light surface shift half-strength on hover, full on press
159
+ hover3: isActive ? 1 : isHovered ? 0.55 : 0,
160
+ // Dark chrome: faint smoky tint; over-light keeps stronger fill
161
+ base: isOverLight ? overLightConfig.opacity : 0.14,
162
+ over: isOverLight ? overLightConfig.opacity * 1.1 : 0.1,
186
163
  };
187
164
 
188
165
  const style = wrapperElement.style;
@@ -199,51 +176,56 @@ export const updateAtomixGlassStyles = (
199
176
  style.setProperty('--atomix-glass-brightness', lightingBrightness.toString());
200
177
 
201
178
  // ── Chromatic Rim Lighting ──────────────────────────────────────
202
- // Layer 1: Core White/Blue highlight
203
- style.setProperty(
204
- '--atomix-glass-border-gradient-1',
205
- `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%)`
206
- );
207
- // Layer 2: Subtle Red/Warm highlight (offset angle)
208
- style.setProperty(
209
- '--atomix-glass-border-gradient-2',
210
- `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%)`
211
- );
179
+ const borderVars = ATOMIX_GLASS.BORDER.GRADIENT_CSS_VARS;
180
+ if (borderAnimated && !effectiveWithoutEffects) {
181
+ const borderCssVars = buildGlassBorderCssVars({
182
+ mouseOffset,
183
+ mouseVelocity,
184
+ elasticVelocity,
185
+ borderOpacity: overLightConfig.borderOpacity,
186
+ opacityMultiplier: borderOpacityMultiplier,
187
+ tensionFactor,
188
+ });
189
+ style.setProperty(borderVars.GRADIENT_1, borderCssVars[borderVars.GRADIENT_1] ?? '');
190
+ style.setProperty(borderVars.GRADIENT_2, borderCssVars[borderVars.GRADIENT_2] ?? '');
191
+ } else {
192
+ style.removeProperty(borderVars.GRADIENT_1);
193
+ style.removeProperty(borderVars.GRADIENT_2);
194
+ }
212
195
 
213
- // Hover gradients
196
+ // Hover gradients — cursor-relative radial positions for realistic light tracking.
197
+ // hover-1: white overlay highlight following cursor (works on both dark + light)
214
198
  style.setProperty(
215
199
  '--atomix-glass-hover-1-gradient',
216
- isOverLight
217
- ? `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}%)`
218
- : `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}%)`
200
+ `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%)`
219
201
  );
220
202
 
203
+ // hover-2: press depth — darkens at cursor with multiply blend, isOverLight uses stronger black
221
204
  style.setProperty(
222
205
  '--atomix-glass-hover-2-gradient',
223
206
  isOverLight
224
- ? `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}%)`
225
- : `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}%)`
207
+ ? `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%)`
208
+ : `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%)`
226
209
  );
227
210
 
211
+ // hover-3: full-surface soft-light tint; linear gradient angled with cursor X
228
212
  style.setProperty(
229
213
  '--atomix-glass-hover-3-gradient',
230
- isOverLight
231
- ? `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}%)`
232
- : `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}%)`
214
+ `linear-gradient(${150 + mx * 0.3}deg, rgba(${whiteColor}, ${isOverLight ? 0.08 : 0.12}) 0%, rgba(${whiteColor}, 0.04) 55%, rgba(${whiteColor}, 0) 100%)`
233
215
  );
234
216
 
235
217
  style.setProperty(
236
218
  '--atomix-glass-base-gradient',
237
219
  isOverLight
238
220
  ? `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%)`
239
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`
221
+ : `linear-gradient(180deg, rgba(${blackColor}, 0.42) 0%, rgba(${blackColor}, 0.22) 55%, rgba(${blackColor}, 0.12) 100%)`
240
222
  );
241
223
 
242
224
  style.setProperty(
243
225
  '--atomix-glass-overlay-gradient',
244
226
  isOverLight
245
227
  ? `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%)`
246
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`
228
+ : `radial-gradient(120% 80% at 50% 0%, rgba(${whiteColor}, 0.14) 0%, rgba(${whiteColor}, 0) 55%)`
247
229
  );
248
230
 
249
231
  // Opacities
@@ -339,7 +321,7 @@ export const updateAtomixGlassStyles = (
339
321
  // Container variables
340
322
  const style = containerElement.style;
341
323
 
342
- style.setProperty('--atomix-glass-container-padding', padding);
324
+
343
325
  style.setProperty('--atomix-glass-container-radius', `${effectiveBorderRadius}px`);
344
326
 
345
327
  style.setProperty('--atomix-glass-container-backdrop', backdropFilterString);
@@ -349,12 +331,12 @@ export const updateAtomixGlassStyles = (
349
331
  '--atomix-glass-container-shadow',
350
332
  isOverLight
351
333
  ? [
352
- `inset 0 1px 0 rgba(255, 255, 255, ${(0.4 + mx * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
353
- `inset 0 -1px 0 rgba(0, 0, 0, ${(0.2 + Math.abs(my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
354
- `inset 0 0 20px rgba(0, 0, 0, ${(0.08 + Math.abs(mx + my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
355
- `0 2px 12px rgba(0, 0, 0, ${(0.12 + Math.abs(my) * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
334
+ `inset 0 1px 0 rgba(255, 255, 255, ${(0.35 + mx * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
335
+ `inset 0 -1px 0 rgba(0, 0, 0, ${(0.15 + Math.abs(my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
336
+ `inset 0 0 20px rgba(0, 0, 0, ${(0.06 + Math.abs(mx + my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
337
+ `0 2px 12px rgba(0, 0, 0, ${(0.08 + Math.abs(my) * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
356
338
  ].join(', ')
357
- : '0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset'
339
+ : ATOMIX_GLASS.CONSTANTS.CONTAINER_SHADOW.LIGHT
358
340
  );
359
341
 
360
342
  style.setProperty(
@@ -376,7 +358,9 @@ export const updateAtomixGlassStyles = (
376
358
 
377
359
  style.setProperty(
378
360
  '--atomix-glass-container-box-shadow',
379
- isOverLight ? '0px 16px 70px rgba(0, 0, 0, 0.75)' : '0px 12px 40px rgba(0, 0, 0, 0.25)'
361
+ isOverLight
362
+ ? '0px 16px 70px rgba(0, 0, 0, 0.75)'
363
+ : '0 8px 32px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.18)'
380
364
  );
381
365
  }
382
366
  };
@@ -181,6 +181,11 @@ export function usePerformanceMonitor(
181
181
  const [manualOverride, setManualOverride] = useState(false);
182
182
  const [isEnabled, setIsEnabled] = useState(enabled);
183
183
 
184
+ // Sync external `enabled` prop changes into internal state
185
+ useEffect(() => {
186
+ setIsEnabled(enabled ?? true);
187
+ }, [enabled]);
188
+
184
189
  // Refs for frame tracking
185
190
  const frameCountRef = useRef(0);
186
191
  const lastFpsUpdateRef = useRef(0);