@shohojdhara/atomix 0.5.0 → 0.5.2

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 (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useRef } from 'react';
1
+ import React, { forwardRef, memo, useMemo, useRef } from 'react';
2
2
  import type { AtomixGlassProps } from '../../lib/types/components';
3
3
  import { ATOMIX_GLASS } from '../../lib/constants/components';
4
4
  import { AtomixGlassContainer } from './AtomixGlassContainer';
@@ -7,7 +7,10 @@ import { useAtomixGlass } from '../../lib/composables/useAtomixGlass';
7
7
  import { useResponsiveGlass } from '../../lib/composables/useResponsiveGlass';
8
8
  import { usePerformanceMonitor } from '../../lib/composables/usePerformanceMonitor';
9
9
  import { PerformanceDashboard } from './PerformanceDashboard';
10
- import { getDevicePreset, MOBILE_OPTIMIZED_BREAKPOINTS } from '../../lib/composables/useResponsiveGlass.presets';
10
+ import {
11
+ getDevicePreset,
12
+ MOBILE_OPTIMIZED_BREAKPOINTS,
13
+ } from '../../lib/composables/useResponsiveGlass.presets';
11
14
 
12
15
  /**
13
16
  * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
@@ -88,53 +91,83 @@ import { getDevicePreset, MOBILE_OPTIMIZED_BREAKPOINTS } from '../../lib/composa
88
91
  * <div>Mobile-optimized glass effect</div>
89
92
  * </AtomixGlass>
90
93
  */
91
- export function AtomixGlass({
92
- children,
93
- displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
94
- blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
95
- saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
96
- aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
97
- elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY,
98
- borderRadius,
99
- globalMousePosition: externalGlobalMousePosition,
100
- mouseOffset: externalMouseOffset,
101
- mouseContainer = null,
102
- className = '',
103
- padding = ATOMIX_GLASS.DEFAULTS.PADDING,
104
- overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT,
105
- style = {},
106
- mode = ATOMIX_GLASS.DEFAULTS.MODE,
107
- onClick,
108
- shaderVariant = 'liquidGlass',
109
- 'aria-label': ariaLabel,
110
- 'aria-describedby': ariaDescribedBy,
111
- role,
112
- tabIndex,
113
- reducedMotion = false,
114
- highContrast = false,
115
- withoutEffects = false,
116
- withLiquidBlur = false,
117
- withBorder = true,
118
- withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS,
119
- debugPerformance = false,
120
- debugOverLight = false,
121
- height,
122
- width,
123
- withTimeAnimation = false,
124
- animationSpeed = 1.0,
125
- withMultiLayerDistortion = false,
126
- distortionOctaves = 3,
127
- distortionLacunarity = 2.0,
128
- distortionGain = 0.5,
129
- distortionQuality = 'medium',
130
- devicePreset = 'balanced',
131
- disableResponsiveBreakpoints = false,
132
- ...rest
133
- }: AtomixGlassProps) {
94
+
95
+ // ─── Type guard for the dev-only performance flag ────────────────────────────
96
+ declare global {
97
+ interface Window {
98
+ enablePerformanceMonitoring?: boolean;
99
+ }
100
+ }
101
+
102
+ // Helper to merge refs
103
+ function mergeRefs<T = any>(
104
+ ...refs: (React.MutableRefObject<T> | React.LegacyRef<T> | undefined | null)[]
105
+ ) {
106
+ return (node: T) => {
107
+ refs.forEach(ref => {
108
+ if (typeof ref === 'function') {
109
+ ref(node);
110
+ } else if (ref != null) {
111
+ (ref as React.MutableRefObject<T | null>).current = node;
112
+ }
113
+ });
114
+ };
115
+ }
116
+
117
+ // Internal implementation with forwardRef
118
+ const AtomixGlassInner = forwardRef<HTMLDivElement, AtomixGlassProps>(function AtomixGlass(
119
+ {
120
+ children,
121
+ displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
122
+ blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
123
+ saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
124
+ aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
125
+ elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY,
126
+ borderRadius,
127
+ globalMousePosition: externalGlobalMousePosition,
128
+ mouseOffset: externalMouseOffset,
129
+ mouseContainer = null,
130
+ className = '',
131
+ padding = ATOMIX_GLASS.DEFAULTS.PADDING,
132
+ overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT,
133
+ style = {},
134
+ mode = ATOMIX_GLASS.DEFAULTS.MODE,
135
+ onClick,
136
+ shaderVariant = 'liquidGlass',
137
+ 'aria-label': ariaLabel,
138
+ 'aria-describedby': ariaDescribedBy,
139
+ role,
140
+ tabIndex,
141
+ reducedMotion = false,
142
+ highContrast = false,
143
+ withoutEffects = false,
144
+ withLiquidBlur = false,
145
+ withBorder = true,
146
+ withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS,
147
+ debugPerformance = false,
148
+ debugOverLight = false,
149
+ height,
150
+ width,
151
+ withTimeAnimation = false,
152
+ animationSpeed = 1.0,
153
+ withMultiLayerDistortion = false,
154
+ distortionOctaves = 3,
155
+ distortionLacunarity = 2.0,
156
+ distortionGain = 0.5,
157
+ distortionQuality = 'medium',
158
+ devicePreset = 'balanced',
159
+ disableResponsiveBreakpoints = false,
160
+ isFixedOrSticky: propsIsFixedOrSticky,
161
+ ...rest
162
+ },
163
+ ref
164
+ ) {
134
165
  const glassRef = useRef<HTMLDivElement>(null);
135
166
  const contentRef = useRef<HTMLDivElement>(null);
167
+ const internalWrapperRef = useRef<HTMLDivElement>(null);
168
+ const mergedRef = useMemo(() => mergeRefs(ref, internalWrapperRef), [ref]);
136
169
 
137
- // ── Layout hoisting ──────────────────────────────────────────────────
170
+ // ── Layout hoisting ──────────────────────────────────────────────────────
138
171
  // When position is fixed/sticky the layout props must live on the ROOT
139
172
  // `.c-atomix-glass` element so that every decorative layer (borders,
140
173
  // backgrounds, hover effects) stays in the same stacking context.
@@ -143,7 +176,8 @@ export function AtomixGlass({
143
176
  // layers via --atomix-glass-base-z-index. It must NOT be applied as a
144
177
  // real z-index on the root element — that would break the glass effect.
145
178
  const { zIndex: customZIndex, ...restStyle } = style;
146
- const isFixedOrSticky = restStyle.position === 'fixed' || restStyle.position === 'sticky';
179
+ const isFixedOrSticky =
180
+ propsIsFixedOrSticky || restStyle.position === 'fixed' || restStyle.position === 'sticky';
147
181
 
148
182
  // Use composable hook for all state and logic
149
183
  const {
@@ -159,7 +193,6 @@ export function AtomixGlass({
159
193
  mouseOffset,
160
194
  transformStyle,
161
195
  getShaderTime,
162
- applyTimeBasedDistortion,
163
196
  handleMouseEnter,
164
197
  handleMouseLeave,
165
198
  handleMouseDown,
@@ -168,6 +201,7 @@ export function AtomixGlass({
168
201
  } = useAtomixGlass({
169
202
  glassRef,
170
203
  contentRef,
204
+ wrapperRef: internalWrapperRef,
171
205
  borderRadius,
172
206
  globalMousePosition: externalGlobalMousePosition,
173
207
  mouseOffset: externalMouseOffset,
@@ -204,18 +238,15 @@ export function AtomixGlass({
204
238
  // Get device preset parameters - memoized to prevent recalculation
205
239
  const devicePresetParams = useMemo(() => {
206
240
  return getDevicePreset(devicePreset);
207
- }, [devicePreset]); // Re-calculate only when devicePreset changes
241
+ }, [devicePreset]);
208
242
 
209
243
  // Responsive breakpoint system - automatically adjusts parameters based on viewport
210
- const {
211
- responsiveParams,
212
- currentBreakpoint,
213
- performanceTier,
214
- isActive: isResponsiveActive
215
- } = useResponsiveGlass({
244
+ useResponsiveGlass({
216
245
  baseParams: {
217
246
  ...devicePresetParams,
218
- distortionOctaves: Math.round((displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE) / 25),
247
+ distortionOctaves: Math.round(
248
+ (displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE) / 25
249
+ ),
219
250
  displacementScale: displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
220
251
  blurAmount: blurAmount || ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
221
252
  saturation: saturation || ATOMIX_GLASS.DEFAULTS.SATURATION,
@@ -224,30 +255,24 @@ export function AtomixGlass({
224
255
  chromaticIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
225
256
  },
226
257
  breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
227
- enabled: !disableResponsiveBreakpoints && typeof window !== 'undefined', // Enable unless disabled
228
- debug: false
258
+ enabled: !disableResponsiveBreakpoints && typeof window !== 'undefined',
259
+ debug: false,
229
260
  });
230
261
 
231
262
  // Performance monitoring - tracks FPS, frame time, memory usage
232
- const {
233
- metrics: performanceMetrics,
234
- recommendedQuality,
235
- isUnderperforming,
236
- setQualityLevel,
237
- toggleMonitoring
238
- } = usePerformanceMonitor({
239
- enabled: false, // We'll toggle manually based on prop
263
+ const { metrics: performanceMetrics, toggleMonitoring } = usePerformanceMonitor({
264
+ enabled: debugPerformance, // Enable when debugPerformance is true
240
265
  debug: false,
241
- showOverlay: false
266
+ showOverlay: false,
242
267
  });
243
268
 
244
- // Auto-start performance monitoring if enabled (only in development)
269
+ // Auto-start performance monitoring when debugPerformance is enabled
245
270
  React.useEffect(() => {
246
- if (process.env.NODE_ENV === 'development' && (window as any)?.enablePerformanceMonitoring) {
271
+ if (debugPerformance) {
247
272
  toggleMonitoring();
248
273
  }
249
274
  // eslint-disable-next-line react-hooks/exhaustive-deps
250
- }, []); // Only run once on mount
275
+ }, [debugPerformance]); // Re-run when debugPerformance changes
251
276
 
252
277
  const isOverLight = useMemo(() => overLightConfig.isOverLight, [overLightConfig.isOverLight]);
253
278
 
@@ -272,14 +297,12 @@ export function AtomixGlass({
272
297
  const { position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle } = restStyle;
273
298
  return {
274
299
  ...visualStyle,
275
- ...(!effectiveWithoutEffects && { transform: transformStyle }),
276
300
  };
277
301
  }
278
302
  return {
279
303
  ...restStyle,
280
- ...(!effectiveWithoutEffects && { transform: transformStyle }),
281
304
  };
282
- }, [isFixedOrSticky, restStyle, effectiveWithoutEffects, transformStyle]);
305
+ }, [isFixedOrSticky, restStyle]);
283
306
 
284
307
  // Build className with state modifiers
285
308
  const componentClassName = [
@@ -299,10 +322,19 @@ export function AtomixGlass({
299
322
  position: (isFixedOrSticky
300
323
  ? 'absolute'
301
324
  : restStyle.position || 'absolute') as React.CSSProperties['position'],
302
- top: isFixedOrSticky ? 0 : restStyle.top || 0,
303
- left: isFixedOrSticky ? 0 : restStyle.left || 0,
325
+ top: !isFixedOrSticky ? 0 : (restStyle.top ?? 0),
326
+ left: !isFixedOrSticky ? 0 : (restStyle.left ?? 0),
327
+ right: !isFixedOrSticky ? 'auto' : (restStyle.right ?? 'auto'),
328
+ bottom: !isFixedOrSticky ? 'auto' : (restStyle.bottom ?? 'auto'),
304
329
  }),
305
- [isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left]
330
+ [
331
+ isFixedOrSticky,
332
+ restStyle.position,
333
+ restStyle.top,
334
+ restStyle.left,
335
+ restStyle.right,
336
+ restStyle.bottom,
337
+ ]
306
338
  );
307
339
 
308
340
  const adjustedSize = useMemo(() => {
@@ -311,12 +343,14 @@ export function AtomixGlass({
311
343
  const _position = positionStyles.position;
312
344
 
313
345
  const resolveLength = (value: string | number | undefined, measured: number): string => {
314
- if (value !== undefined) {
346
+ if (value !== undefined && isFixedOrSticky) {
315
347
  return typeof value === 'number' ? `${value}px` : value;
316
348
  }
317
- if (measured > 0) {
349
+
350
+ if (measured > 0 && isFixedOrSticky) {
318
351
  return `${measured}px`;
319
352
  }
353
+
320
354
  return '100%';
321
355
  };
322
356
 
@@ -335,6 +369,7 @@ export function AtomixGlass({
335
369
  positionStyles.position,
336
370
  glassSize.width,
337
371
  glassSize.height,
372
+ isFixedOrSticky,
338
373
  ]);
339
374
 
340
375
  // Memoize expensive gradient calculations
@@ -386,9 +421,14 @@ export function AtomixGlass({
386
421
  };
387
422
  }, [mouseOffset.x, mouseOffset.y]);
388
423
 
424
+ // Clamp overLight opacities to [0,1]
425
+ const clampedOverLightOpacity = Math.max(0, Math.min(1, overLightConfig?.opacity ?? 0.4));
426
+ const clampedBorderOpacity = Math.max(0, Math.min(1, overLightConfig?.borderOpacity ?? 1));
427
+
389
428
  // Memoize opacity calculations
390
429
  const opacityValues = useMemo(() => {
391
- const overLightOpacity = overLightConfig.opacity;
430
+ // Use clamped opacity value here
431
+ const overLightOpacity = clampedOverLightOpacity;
392
432
  const BASE_OVER_LIGHT_OPACITY = 0.4;
393
433
  const OVER_OPACITY_MULTIPLIER = 1.1;
394
434
 
@@ -401,7 +441,7 @@ export function AtomixGlass({
401
441
  ? (overLightOpacity || BASE_OVER_LIGHT_OPACITY) * OVER_OPACITY_MULTIPLIER
402
442
  : 0,
403
443
  };
404
- }, [isHovered, isActive, isOverLight, overLightConfig.opacity]);
444
+ }, [isHovered, isActive, isOverLight, clampedOverLightOpacity]);
405
445
 
406
446
  // Memoize CSS variables object
407
447
  const glassVars = useMemo(() => {
@@ -420,22 +460,22 @@ export function AtomixGlass({
420
460
  absMy,
421
461
  } = gradientValues;
422
462
 
423
- const configBorderOpacity = overLightConfig?.borderOpacity ?? 1;
424
-
425
463
  return {
426
464
  ...(customZIndex !== undefined && { '--atomix-glass-base-z-index': customZIndex }),
427
465
  '--atomix-glass-radius': `${effectiveBorderRadius}px`,
428
466
  '--atomix-glass-transform': transformStyle || 'none',
429
- // Internal decorative layers are positioned relative to the root;
430
- '--atomix-glass-position': rootLayoutStyle.position,
431
- '--atomix-glass-top': `${isFixedOrSticky ? rootLayoutStyle.top : 0}px`,
432
- '--atomix-glass-left': `${isFixedOrSticky ? rootLayoutStyle.left : 0}px`,
467
+ '--atomix-glass-container-position': `${!isFixedOrSticky ? positionStyles.position : rootLayoutStyle.position}`,
468
+ '--atomix-glass-position': `${!isFixedOrSticky ? positionStyles.position : rootLayoutStyle.position}`,
469
+ '--atomix-glass-top': `${!isFixedOrSticky ? 0 : (restStyle.top ?? 0)}px`,
470
+ '--atomix-glass-left': `${!isFixedOrSticky ? 0 : (restStyle.left ?? 0)}px`,
471
+ '--atomix-glass-right': !isFixedOrSticky ? 'auto' : (restStyle.right ?? 'auto'),
472
+ '--atomix-glass-bottom': !isFixedOrSticky ? 'auto' : (restStyle.bottom ?? 'auto'),
433
473
  '--atomix-glass-width': adjustedSize.width,
434
474
  '--atomix-glass-height': adjustedSize.height,
435
- '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
475
+ '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.125rem)',
436
476
  '--atomix-glass-blend-mode': isOverLight ? 'multiply' : 'overlay',
437
- '--atomix-glass-border-gradient-1': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
438
- '--atomix-glass-border-gradient-2': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
477
+ '--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%)`,
478
+ '--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%)`,
439
479
  '--atomix-glass-hover-1-opacity': opacityValues.hover1,
440
480
  '--atomix-glass-hover-1-gradient': isOverLight
441
481
  ? `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}%)`
@@ -467,13 +507,28 @@ export function AtomixGlass({
467
507
  transformStyle,
468
508
  adjustedSize,
469
509
  isOverLight,
470
- overLightConfig.borderOpacity,
510
+ clampedBorderOpacity,
471
511
  customZIndex,
472
- rootLayoutStyle,
473
512
  isFixedOrSticky,
513
+ positionStyles.position,
514
+ rootLayoutStyle.position,
515
+ restStyle.top,
516
+ restStyle.left,
517
+ restStyle.right,
518
+ restStyle.bottom,
474
519
  ]);
475
520
 
476
- // Helper function to render background layers
521
+ // ─── Render helpers ──────────────────────────────────────────────────────
522
+
523
+ const renderHoverLayers = () => (
524
+ <>
525
+ {/* Hover layers - opacity and background set via CSS variables in SCSS */}
526
+ <div className={ATOMIX_GLASS.HOVER_1_CLASS} />
527
+ <div className={ATOMIX_GLASS.HOVER_2_CLASS} />
528
+ <div className={ATOMIX_GLASS.HOVER_3_CLASS} />
529
+ </>
530
+ );
531
+
477
532
  const renderBackgroundLayer = (layerType: 'dark' | 'black') => (
478
533
  <div
479
534
  className={[
@@ -490,9 +545,30 @@ export function AtomixGlass({
490
545
  />
491
546
  );
492
547
 
548
+ const renderOverLightLayers = () => (
549
+ <>
550
+ {/* Base and overlay layers - opacity and background set via CSS variables in SCSS */}
551
+ <div className={ATOMIX_GLASS.BASE_LAYER_CLASS} />
552
+ <div className={ATOMIX_GLASS.OVERLAY_LAYER_CLASS} />
553
+ {/* Overlay highlight - opacity and background are dynamic, calculated inline */}
554
+ <div className={ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS} />
555
+ </>
556
+ );
557
+
558
+ const renderBorderElements = () => (
559
+ <>
560
+ {/* Border elements - all styles (static and dynamic via CSS variables) are in SCSS */}
561
+ {/* Position, size, transform, transition, border-radius all use CSS variables set in glassVars */}
562
+ <span className={ATOMIX_GLASS.BORDER_BACKDROP_CLASS} />
563
+ <span className={ATOMIX_GLASS.BORDER_1_CLASS} />
564
+ <span className={ATOMIX_GLASS.BORDER_2_CLASS} />
565
+ </>
566
+ );
567
+
493
568
  return (
494
569
  <div
495
570
  {...rest}
571
+ ref={mergedRef}
496
572
  className={componentClassName}
497
573
  style={{ ...glassVars }}
498
574
  role={role || (onClick ? 'button' : undefined)}
@@ -500,19 +576,14 @@ export function AtomixGlass({
500
576
  aria-label={ariaLabel}
501
577
  aria-describedby={ariaDescribedBy}
502
578
  aria-disabled={onClick && effectiveWithoutEffects ? true : onClick ? false : undefined}
503
- aria-pressed={onClick && isActive ? true : onClick ? false : undefined}
504
- onKeyDown={onClick ? handleKeyDown : undefined} // Dynamic CSS variables cause hydration mismatch due to mouse position calculations
579
+ aria-pressed={undefined}
580
+ onKeyDown={onClick ? handleKeyDown : undefined}
505
581
  >
506
582
  <AtomixGlassContainer
507
583
  ref={glassRef}
508
584
  contentRef={contentRef}
509
585
  className={className}
510
- style={{
511
- ...restStyle,
512
- ...(!isFixedOrSticky && {
513
- position: 'relative',
514
- }),
515
- }}
586
+ style={{ ...restStyle } as React.CSSProperties}
516
587
  borderRadius={effectiveBorderRadius}
517
588
  displacementScale={
518
589
  effectiveWithoutEffects
@@ -561,6 +632,7 @@ export function AtomixGlass({
561
632
  effectiveReducedMotion={effectiveReducedMotion}
562
633
  shaderVariant={shaderVariant}
563
634
  withLiquidBlur={withLiquidBlur}
635
+ isFixedOrSticky={isFixedOrSticky}
564
636
  // Phase 1: Animation System props
565
637
  shaderTime={getShaderTime()}
566
638
  withTimeAnimation={withTimeAnimation}
@@ -573,40 +645,20 @@ export function AtomixGlass({
573
645
  >
574
646
  {children}
575
647
  </AtomixGlassContainer>
576
- {Boolean(onClick) && (
577
- <>
578
- {/* Hover layers - opacity and background set via CSS variables in SCSS */}
579
- <div className={ATOMIX_GLASS.HOVER_1_CLASS} />
580
- <div className={ATOMIX_GLASS.HOVER_2_CLASS} />
581
- <div className={ATOMIX_GLASS.HOVER_3_CLASS} />
582
- </>
583
- )}
648
+
649
+ {Boolean(onClick) && renderHoverLayers()}
584
650
 
585
651
  {/* Background layers for over-light mode */}
586
652
  {/* Static styles (pointer-events) are in SCSS; will-change is managed via .u-glass-clean-root utility for backdrop-filter stability */}
587
653
  {renderBackgroundLayer('dark')}
588
654
  {renderBackgroundLayer('black')}
589
- {shouldRenderOverLightLayers && (
590
- <>
591
- {/* Base and overlay layers - opacity and background set via CSS variables in SCSS */}
592
- <div className={ATOMIX_GLASS.BASE_LAYER_CLASS} />
593
- <div className={ATOMIX_GLASS.OVERLAY_LAYER_CLASS} />
594
- {/* Overlay highlight - opacity and background are dynamic, calculated inline */}
595
- <div className={ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS} />
596
- </>
597
- )}
598
- {withBorder && (
599
- <>
600
- {/* Border elements - all styles (static and dynamic via CSS variables) are in SCSS */}
601
- {/* Position, size, transform, transition, border-radius all use CSS variables set in glassVars */}
602
- <span className={ATOMIX_GLASS.BORDER_1_CLASS} />
603
- <span className={ATOMIX_GLASS.BORDER_2_CLASS} />
604
- </>
605
- )}
606
-
655
+ {shouldRenderOverLightLayers && renderOverLightLayers()}
656
+
657
+ {withBorder && renderBorderElements()}
658
+
607
659
  {/* Phase 3: Performance Monitoring Dashboard - Only in development with debugPerformance enabled */}
608
660
  {debugPerformance && performanceMetrics && (
609
- <PerformanceDashboard
661
+ <PerformanceDashboard
610
662
  metrics={performanceMetrics}
611
663
  isVisible={true}
612
664
  onClose={() => {}} // No-op, dashboard always visible when debugPerformance is true
@@ -614,6 +666,14 @@ export function AtomixGlass({
614
666
  )}
615
667
  </div>
616
668
  );
617
- }
669
+ });
670
+
671
+ AtomixGlassInner.displayName = 'AtomixGlass';
672
+
673
+ /**
674
+ * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
675
+ * Ref is forwarded to the root `<div>` element.
676
+ */
677
+ export const AtomixGlass = memo(AtomixGlassInner);
618
678
 
619
679
  export default AtomixGlass;