@shohojdhara/atomix 0.3.10 → 0.3.12

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 (47) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/atomix.css +9 -6
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +9 -6
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.js +82 -60
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.js +82 -60
  9. package/dist/core.js.map +1 -1
  10. package/dist/forms.js +82 -60
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js +82 -60
  13. package/dist/heavy.js.map +1 -1
  14. package/dist/index.d.ts +11 -107
  15. package/dist/index.esm.js +165 -407
  16. package/dist/index.esm.js.map +1 -1
  17. package/dist/index.js +169 -412
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.min.js +1 -1
  20. package/dist/index.min.js.map +1 -1
  21. package/dist/theme.d.ts +1 -32
  22. package/dist/theme.js +12 -207
  23. package/dist/theme.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/components/AtomixGlass/AtomixGlass.tsx +124 -127
  26. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +28 -32
  27. package/src/components/AtomixGlass/GlassFilter.tsx +15 -4
  28. package/src/components/EdgePanel/EdgePanel.stories.tsx +2 -7
  29. package/src/components/EdgePanel/EdgePanel.tsx +0 -10
  30. package/src/components/Form/Radio.stories.tsx +235 -103
  31. package/src/components/Navigation/Nav/NavDropdown.tsx +8 -4
  32. package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -22
  33. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +11 -15
  34. package/src/lib/config/index.ts +5 -5
  35. package/src/lib/theme/config/index.ts +1 -1
  36. package/src/lib/theme/core/createTheme.ts +11 -40
  37. package/src/lib/theme/generators/index.ts +1 -4
  38. package/src/lib/theme/index.ts +4 -16
  39. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -16
  40. package/src/lib/types/components.ts +2 -26
  41. package/src/styles/06-components/_components.edge-panel.scss +4 -4
  42. package/src/styles/06-components/_components.nav.scss +3 -0
  43. package/src/lib/config/loader.ts +0 -147
  44. package/src/lib/theme/config/__tests__/configLoader.test.ts +0 -207
  45. package/src/lib/theme/config/configLoader.ts +0 -113
  46. package/src/lib/theme/config/loader.ts +0 -293
  47. package/src/lib/theme/generators/cssFile.ts +0 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shohojdhara/atomix",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Atomix Design System - A modern component library for web applications",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -109,7 +109,7 @@ export function AtomixGlass({
109
109
  }: AtomixGlassProps) {
110
110
  const glassRef = useRef<HTMLDivElement>(null);
111
111
  const contentRef = useRef<HTMLDivElement>(null);
112
-
112
+
113
113
  // Use composable hook for all state and logic
114
114
  const {
115
115
  isHovered,
@@ -192,101 +192,132 @@ export function AtomixGlass({
192
192
  : Math.max(glassSize.height, 0),
193
193
  };
194
194
 
195
- // Calculate gradient values directly without excessive memoization
196
- const mx = mouseOffset.x;
197
- const my = mouseOffset.y;
195
+ // Memoize expensive gradient calculations
196
+ const gradientValues = useMemo(() => {
197
+ const mx = mouseOffset.x;
198
+ const my = mouseOffset.y;
199
+ const absMx = Math.abs(mx);
200
+ const absMy = Math.abs(my);
201
+ const GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
198
202
 
199
- // Calculate gradient angles and stops
200
- const GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
201
- const borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER;
202
- const borderStop1 = Math.max(
203
- GRADIENT.BORDER_STOP_1.MIN,
204
- GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
205
- );
206
- const borderStop2 = Math.min(
207
- GRADIENT.BORDER_STOP_2.MAX,
208
- GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
209
- );
210
- const borderOpacity1 =
211
- GRADIENT.BORDER_OPACITY.BASE_1 +
212
- Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW;
213
- const borderOpacity2 =
214
- GRADIENT.BORDER_OPACITY.BASE_2 +
215
- Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH;
216
- const borderOpacity3 =
217
- GRADIENT.BORDER_OPACITY.BASE_3 +
218
- Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW;
219
- const borderOpacity4 =
220
- GRADIENT.BORDER_OPACITY.BASE_4 +
221
- Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH;
203
+ return {
204
+ borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
205
+ borderStop1: Math.max(
206
+ GRADIENT.BORDER_STOP_1.MIN,
207
+ GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
208
+ ),
209
+ borderStop2: Math.min(
210
+ GRADIENT.BORDER_STOP_2.MAX,
211
+ GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
212
+ ),
213
+ borderOpacities: [
214
+ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
215
+ GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
216
+ GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
217
+ GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
218
+ ],
219
+ hoverPositions: {
220
+ hover1: {
221
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
222
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1,
223
+ },
224
+ hover2: {
225
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
226
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2,
227
+ },
228
+ hover3: {
229
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
230
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
231
+ },
232
+ },
233
+ basePosition: {
234
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
235
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER,
236
+ },
237
+ mx,
238
+ my,
239
+ absMx,
240
+ absMy,
241
+ };
242
+ }, [mouseOffset.x, mouseOffset.y]);
222
243
 
223
- // Hover gradient positions
224
- const hover1X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1;
225
- const hover1Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1;
226
- const hover2X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2;
227
- const hover2Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2;
228
- const hover3X = GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3;
229
- const hover3Y = GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3;
244
+ // Memoize opacity calculations
245
+ const opacityValues = useMemo(() => {
246
+ const overLightOpacity = overLightConfig.opacity;
247
+ const BASE_OVER_LIGHT_OPACITY = 0.4;
248
+ const OVER_OPACITY_MULTIPLIER = 1.1;
230
249
 
231
- // Base layer positions
232
- const baseX = GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER;
233
- const baseY = GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER;
250
+ return {
251
+ hover1: isHovered || isActive ? 0.5 : 0,
252
+ hover2: isActive ? 0.5 : 0,
253
+ hover3: isHovered ? 0.4 : isActive ? 0.8 : 0,
254
+ base: isOverLight ? overLightOpacity || BASE_OVER_LIGHT_OPACITY : 0,
255
+ over: isOverLight ? (overLightOpacity || BASE_OVER_LIGHT_OPACITY) * OVER_OPACITY_MULTIPLIER : 0,
256
+ };
257
+ }, [isHovered, isActive, isOverLight, overLightConfig.opacity]);
234
258
 
235
- // Calculate opacity values
236
- const overLightOpacity = overLightConfig.opacity;
237
- const BASE_OVER_LIGHT_OPACITY = 0.4; // Default opacity value
238
- const OVER_OPACITY_MULTIPLIER = 1.1; // Dynamic multiplier for overlay
259
+ // Memoize CSS variables object
260
+ const glassVars = useMemo(() => {
261
+ const whiteColor = '255, 255, 255';
262
+ const blackColor = '0, 0, 0';
263
+ const { borderGradientAngle, borderStop1, borderStop2, borderOpacities, hoverPositions, basePosition, mx, my, absMx, absMy } = gradientValues;
239
264
 
240
- const opacityValues = {
241
- hover1: isHovered || isActive ? 0.5 : 0,
242
- hover2: isActive ? 0.5 : 0,
243
- hover3: isHovered ? 0.4 : isActive ? 0.8 : 0,
244
- base: isOverLight ? overLightOpacity || BASE_OVER_LIGHT_OPACITY : 0,
245
- over: isOverLight ? (overLightOpacity || BASE_OVER_LIGHT_OPACITY) * OVER_OPACITY_MULTIPLIER : 0,
246
- };
265
+ return {
266
+ '--atomix-glass-radius': `${effectiveCornerRadius}px`,
267
+ '--atomix-glass-transform': transformStyle || 'none',
268
+ '--atomix-glass-position': positionStyles.position,
269
+ '--atomix-glass-top': positionStyles.top !== 'fixed' ? `${positionStyles.top}px` : '0',
270
+ '--atomix-glass-left': positionStyles.left !== 'fixed' ? `${positionStyles.left}px` : '0',
271
+ '--atomix-glass-width': style.position !== 'fixed' ? adjustedSize.width : `${adjustedSize.width}px`,
272
+ '--atomix-glass-height': style.position !== 'fixed' ? adjustedSize.height : `${adjustedSize.height}px`,
273
+ '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
274
+ '--atomix-glass-blend-mode': isOverLight ? 'multiply' : 'overlay',
275
+ '--atomix-glass-border-gradient-1': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${borderOpacities[0]}) ${borderStop1}%, rgba(${whiteColor}, ${borderOpacities[1]}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
276
+ '--atomix-glass-border-gradient-2': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${borderOpacities[2]}) ${borderStop1}%, rgba(${whiteColor}, ${borderOpacities[3]}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
277
+ '--atomix-glass-hover-1-opacity': opacityValues.hover1,
278
+ '--atomix-glass-hover-1-gradient': isOverLight
279
+ ? `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}%)`
280
+ : `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}%)`,
281
+ '--atomix-glass-hover-2-opacity': opacityValues.hover2,
282
+ '--atomix-glass-hover-2-gradient': isOverLight
283
+ ? `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}%)`
284
+ : `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}%)`,
285
+ '--atomix-glass-hover-3-opacity': opacityValues.hover3,
286
+ '--atomix-glass-hover-3-gradient': isOverLight
287
+ ? `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}%)`
288
+ : `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}%)`,
289
+ '--atomix-glass-base-opacity': opacityValues.base,
290
+ '--atomix-glass-base-gradient': isOverLight
291
+ ? `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%)`
292
+ : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
293
+ '--atomix-glass-overlay-opacity': opacityValues.over,
294
+ '--atomix-glass-overlay-gradient': isOverLight
295
+ ? `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%)`
296
+ : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
297
+ } as React.CSSProperties;
298
+ }, [gradientValues, opacityValues, effectiveCornerRadius, transformStyle, positionStyles, adjustedSize, style.position, isOverLight]);
247
299
 
248
- // Generate CSS variables for layers
249
- const whiteColor = '255, 255, 255'; // Default white RGB
250
- const blackColor = '0, 0, 0'; // Default black RGB
251
-
252
- const glassVars = {
253
- // Standard CSS custom properties for dynamic values
254
- '--atomix-glass-radius': `${effectiveCornerRadius}px`,
255
- '--atomix-glass-transform': transformStyle || 'none',
256
- '--atomix-glass-position': positionStyles.position,
257
- '--atomix-glass-top': positionStyles.top !== 'fixed' ? `${positionStyles.top}px` : '0',
258
- '--atomix-glass-left': positionStyles.left !== 'fixed' ? `${positionStyles.left}px` : '0',
259
- '--atomix-glass-width':
260
- style.position !== 'fixed' ? adjustedSize.width : `${adjustedSize.width}px`,
261
- '--atomix-glass-height':
262
- style.position !== 'fixed' ? adjustedSize.height : `${adjustedSize.height}px`,
263
- // Border width: Use spacing token for consistency
264
- '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
265
- '--atomix-glass-blend-mode': isOverLight ? 'multiply' : 'overlay',
266
- // Dynamic gradients and backgrounds
267
- '--atomix-glass-border-gradient-1': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${borderOpacity1}) ${borderStop1}%, rgba(${whiteColor}, ${borderOpacity2}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
268
- '--atomix-glass-border-gradient-2': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${borderOpacity3}) ${borderStop1}%, rgba(${whiteColor}, ${borderOpacity4}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
269
- '--atomix-glass-hover-1-opacity': opacityValues.hover1,
270
- '--atomix-glass-hover-1-gradient': isOverLight
271
- ? `radial-gradient(circle at ${hover1X}% ${hover1Y}%, 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}%)`
272
- : `radial-gradient(circle at ${hover1X}% ${hover1Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`,
273
- '--atomix-glass-hover-2-opacity': opacityValues.hover2,
274
- '--atomix-glass-hover-2-gradient': isOverLight
275
- ? `radial-gradient(circle at ${hover2X}% ${hover2Y}%, 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}%)`
276
- : `radial-gradient(circle at ${hover2X}% ${hover2Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`,
277
- '--atomix-glass-hover-3-opacity': opacityValues.hover3,
278
- '--atomix-glass-hover-3-gradient': isOverLight
279
- ? `radial-gradient(circle at ${hover3X}% ${hover3Y}%, 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}%)`
280
- : `radial-gradient(circle at ${hover3X}% ${hover3Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`,
281
- '--atomix-glass-base-opacity': opacityValues.base,
282
- '--atomix-glass-base-gradient': isOverLight
283
- ? `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 + Math.abs(mx) * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
284
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
285
- '--atomix-glass-overlay-opacity': opacityValues.over,
286
- '--atomix-glass-overlay-gradient': isOverLight
287
- ? `radial-gradient(circle at ${baseX}% ${baseY}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + Math.abs(mx) * 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 + Math.abs(my) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
288
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
289
- } as React.CSSProperties;
300
+ // Helper function to render background layers
301
+ const renderBackgroundLayer = (layerType: 'dark' | 'black') => (
302
+ <div
303
+ className={[
304
+ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS,
305
+ layerType === 'dark' ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS,
306
+ isOverLight
307
+ ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS
308
+ : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS,
309
+ ]
310
+ .filter(Boolean)
311
+ .join(' ')}
312
+ style={{
313
+ ...positionStyles,
314
+ height: adjustedSize.height,
315
+ width: adjustedSize.width,
316
+ borderRadius: `${effectiveCornerRadius}px`,
317
+ transform: baseStyle.transform,
318
+ }}
319
+ />
320
+ );
290
321
 
291
322
  return (
292
323
  <div
@@ -298,7 +329,7 @@ export function AtomixGlass({
298
329
  aria-describedby={ariaDescribedBy}
299
330
  aria-disabled={onClick && effectiveDisableEffects ? true : onClick ? false : undefined}
300
331
  aria-pressed={onClick && isActive ? true : onClick ? false : undefined}
301
- onKeyDown={onClick ? handleKeyDown : undefined}
332
+ onKeyDown={onClick ? handleKeyDown : undefined} // Dynamic CSS variables cause hydration mismatch due to mouse position calculations
302
333
  >
303
334
  <AtomixGlassContainer
304
335
  ref={glassRef}
@@ -364,42 +395,8 @@ export function AtomixGlass({
364
395
 
365
396
  {/* Background layers for over-light mode */}
366
397
  {/* Static styles (pointer-events, will-change) are in SCSS */}
367
- <div
368
- className={[
369
- ATOMIX_GLASS.BACKGROUND_LAYER_CLASS,
370
- ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS,
371
- isOverLight
372
- ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS
373
- : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS,
374
- ]
375
- .filter(Boolean)
376
- .join(' ')}
377
- style={{
378
- ...positionStyles,
379
- height: adjustedSize.height,
380
- width: adjustedSize.width,
381
- borderRadius: `${effectiveCornerRadius}px`,
382
- transform: baseStyle.transform,
383
- }}
384
- />
385
- <div
386
- className={[
387
- ATOMIX_GLASS.BACKGROUND_LAYER_CLASS,
388
- ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS,
389
- isOverLight
390
- ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS
391
- : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS,
392
- ]
393
- .filter(Boolean)
394
- .join(' ')}
395
- style={{
396
- ...positionStyles,
397
- height: adjustedSize.height,
398
- width: adjustedSize.width,
399
- borderRadius: `${effectiveCornerRadius}px`,
400
- transform: baseStyle.transform,
401
- }}
402
- />
398
+ {renderBackgroundLayer('dark')}
399
+ {renderBackgroundLayer('black')}
403
400
  {shouldRenderOverLightLayers && (
404
401
  <>
405
402
  {/* Base and overlay layers - opacity and background set via CSS variables in SCSS */}
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useId, useRef, useState, useEffect, useMemo } from 'react';
1
+ import React, { forwardRef, useRef, useState, useEffect, useMemo } from 'react';
2
2
  import type { CSSProperties } from 'react';
3
3
  import type { DisplacementMode, MousePosition, GlassSize } from '../../lib/types/components';
4
4
  import type { FragmentShaderType } from './shader-utils';
@@ -51,11 +51,11 @@ const setCachedShader = (key: string, url: string): void => {
51
51
  }
52
52
  }
53
53
  sharedShaderCache.set(key, { url, timestamp: Date.now() });
54
-
54
+
55
55
  // Development mode: log cache size
56
56
  if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {
57
57
  if (sharedShaderCache.size >= MAX_CACHE_SIZE * 0.8) {
58
- console.log(`AtomixGlass: Shader cache size: ${sharedShaderCache.size}/${MAX_CACHE_SIZE}`);
58
+ console.log(`AtomixGlass: Shader cache size: ${String(sharedShaderCache.size).replace(/[\r\n]/g, '')}/${String(MAX_CACHE_SIZE).replace(/[\r\n]/g, '')}`);
59
59
  }
60
60
  }
61
61
  };
@@ -131,26 +131,26 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
131
131
  ref
132
132
  ) => {
133
133
  // Generate a stable, deterministic ID for SSR compatibility
134
- // React's useId() should produce the same ID on server and client for the same
135
- // component position in the tree. We use useState to ensure the ID is only
136
- // generated once and remains stable across renders.
137
- const baseId = useId();
134
+ // Use a counter-based approach to avoid hydration mismatches
138
135
  const [filterId] = useState(() => {
139
- // Normalize the ID to ensure it's valid and consistent
140
- // Remove colons (which useId() uses) and ensure it starts with a letter
141
- const normalizedId = baseId.replace(/:/g, '-').replace(/^[^a-z]/i, 'atomix-');
142
- return `atomix-glass-filter-${normalizedId}`;
136
+ // Use a simple counter for deterministic IDs
137
+ if (typeof window === 'undefined') {
138
+ // Server-side: use a predictable pattern
139
+ return `atomix-glass-filter-ssr-${Math.random().toString(36).substring(2, 11)}`;
140
+ }
141
+ // Client-side: use timestamp + random for uniqueness
142
+ return `atomix-glass-filter-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
143
143
  });
144
-
144
+
145
145
  const [shaderMapUrl, setShaderMapUrl] = useState<string>('');
146
146
  const shaderGeneratorRef = useRef<any>(null);
147
147
  const shaderUtilsRef = useRef<{
148
148
  ShaderDisplacementGenerator: any;
149
149
  fragmentShaders: any;
150
150
  } | null>(null);
151
-
151
+
152
152
  // Use shared module-level cache (no per-instance cache needed)
153
- const shaderDebounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
153
+ const shaderDebounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
154
154
 
155
155
  // Lazy load shader utilities only when shader mode is needed
156
156
  useEffect(() => {
@@ -162,7 +162,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
162
162
  fragmentShaders: shaderUtils.fragmentShaders,
163
163
  };
164
164
  }).catch((error) => {
165
- console.warn('AtomixGlassContainer: Error loading shader utilities', error);
165
+ console.warn('AtomixGlassContainer: Error loading shader utilities', String(error).replace(/[\r\n]/g, ''));
166
166
  });
167
167
  } else {
168
168
  // Clear shader utils when not in shader mode to free memory
@@ -176,7 +176,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
176
176
  if (mode === 'shader' && glassSize && validateGlassSize(glassSize) && shaderUtilsRef.current) {
177
177
  // Create cache key from size and variant
178
178
  const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`;
179
-
179
+
180
180
  // Check shared cache first
181
181
  const cachedUrl = getCachedShader(cacheKey);
182
182
  if (cachedUrl) {
@@ -206,7 +206,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
206
206
  height: glassSize.height,
207
207
  fragment: selectedShader,
208
208
  });
209
-
209
+
210
210
  // Use requestIdleCallback if available for non-blocking generation
211
211
  const generate = () => {
212
212
  const url = shaderGeneratorRef.current?.updateShader() || '';
@@ -435,11 +435,11 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
435
435
  '--atomix-glass-container-backdrop': backdropStyle?.backdropFilter || 'none',
436
436
  '--atomix-glass-container-shadow': overLight
437
437
  ? [
438
- `inset 0 1px 0 rgba(255, 255, 255, ${0.4 + mx * 0.002})`,
439
- `inset 0 -1px 0 rgba(0, 0, 0, ${0.2 + Math.abs(my) * 0.001})`,
440
- `inset 0 0 20px rgba(0, 0, 0, ${0.08 + Math.abs(mx + my) * 0.001})`,
441
- `0 2px 12px rgba(0, 0, 0, ${0.12 + Math.abs(my) * 0.002})`,
442
- ].join(', ')
438
+ `inset 0 1px 0 rgba(255, 255, 255, ${0.4 + mx * 0.002})`,
439
+ `inset 0 -1px 0 rgba(0, 0, 0, ${0.2 + Math.abs(my) * 0.001})`,
440
+ `inset 0 0 20px rgba(0, 0, 0, ${0.08 + Math.abs(mx + my) * 0.001})`,
441
+ `0 2px 12px rgba(0, 0, 0, ${0.12 + Math.abs(my) * 0.002})`,
442
+ ].join(', ')
443
443
  : '0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset',
444
444
  '--atomix-glass-container-shadow-opacity': effectiveDisableEffects ? 0 : 1,
445
445
  // Background and shadow values use design token-aligned RGB values
@@ -511,25 +511,22 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
511
511
  shaderMapUrl={shaderMapUrl}
512
512
  />
513
513
  {/* Enhanced Apple Liquid Glass Inner Shadow Layer */}
514
-
515
514
  <div
516
515
  className={ATOMIX_GLASS.FILTER_OVERLAY_CLASS}
517
- suppressHydrationWarning
518
516
  style={{
519
517
  filter: `url(#${filterId})`,
520
518
  backdropFilter: `var(--atomix-glass-container-backdrop)`,
521
519
  borderRadius: `var(--atomix-glass-container-radius)`,
522
520
  }}
523
521
  />
524
-
525
522
  <div
526
523
  className={ATOMIX_GLASS.FILTER_SHADOW_CLASS}
527
- style={{
528
- boxShadow: `var(--atomix-glass-container-shadow)`,
529
- opacity: `var(--atomix-glass-container-shadow-opacity)`,
530
- background: `var(--atomix-glass-container-bg)`,
531
- borderRadius: `var(--atomix-glass-container-radius)`,
532
- }}
524
+ style={{
525
+ boxShadow: `var(--atomix-glass-container-shadow)`,
526
+ opacity: `var(--atomix-glass-container-shadow-opacity)`,
527
+ background: `var(--atomix-glass-container-bg)`,
528
+ borderRadius: `var(--atomix-glass-container-radius)`,
529
+ }}
533
530
  />
534
531
  </div>
535
532
 
@@ -538,7 +535,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
538
535
  className={ATOMIX_GLASS.CONTENT_CLASS}
539
536
  style={{
540
537
  position: 'relative',
541
-
542
538
  textShadow: `var(--atomix-glass-container-text-shadow)`,
543
539
  ...(elasticity > 0 ? { zIndex: 100 } : {}),
544
540
  }}
@@ -33,10 +33,14 @@ const GlassFilterComponent: React.FC<GlassFilterProps> = ({
33
33
  inset: 0,
34
34
  }}
35
35
  aria-hidden="true"
36
- suppressHydrationWarning
37
36
  >
38
37
  <defs>
39
- <radialGradient id={`${id}-edge-mask`} cx="50%" cy="50%" r="50%">
38
+ <radialGradient
39
+ id={`${id}-edge-mask`}
40
+ cx="50%"
41
+ cy="50%"
42
+ r="50%"
43
+ >
40
44
  <stop offset="0%" stopColor="black" stopOpacity="0" />
41
45
  <stop
42
46
  offset={`${Math.max(30, 80 - aberrationIntensity * 2)}%`}
@@ -45,9 +49,16 @@ const GlassFilterComponent: React.FC<GlassFilterProps> = ({
45
49
  />
46
50
  <stop offset="100%" stopColor="white" stopOpacity="1" />
47
51
  </radialGradient>
48
- <filter id={id} x="-35%" y="-35%" width="170%" height="170%" colorInterpolationFilters="sRGB">
52
+ <filter
53
+ id={id}
54
+ x="-35%"
55
+ y="-35%"
56
+ width="170%"
57
+ height="170%"
58
+ colorInterpolationFilters="sRGB"
59
+ >
49
60
  <feImage
50
- id="feimage"
61
+ id={`${id}-image`}
51
62
  x="0"
52
63
  y="0"
53
64
  width="100%"
@@ -504,14 +504,12 @@ export const GlassPremium: Story = {
504
504
  <Card
505
505
  title="Enhanced Visuals"
506
506
  text="Premium glass shader provides the most refined appearance."
507
- glass
508
507
  className="u-mb-3"
509
508
  />
510
509
 
511
510
  <Card
512
511
  title="Perfect for Modern Apps"
513
512
  text="Ideal for applications requiring sophisticated UI design."
514
- glass
515
513
  className="u-mb-3"
516
514
  />
517
515
 
@@ -598,7 +596,7 @@ export const GlassShowcase: Story = {
598
596
  <p className="u-mb-4">
599
597
  Classic glass morphism with balanced displacement and blur for general use.
600
598
  </p>
601
- <Card title="Balanced Design" text="Perfect for everyday applications." glass />
599
+ <Card title="Balanced Design" text="Perfect for everyday applications." />
602
600
  </div>
603
601
  </EdgePanel>
604
602
 
@@ -630,7 +628,6 @@ export const GlassShowcase: Story = {
630
628
  <Card
631
629
  title="Radial Distortion"
632
630
  text="Creates circular displacement patterns."
633
- glass
634
631
  />
635
632
  </div>
636
633
  </EdgePanel>
@@ -660,7 +657,7 @@ export const GlassShowcase: Story = {
660
657
  <p className="u-mb-4">
661
658
  Stronger displacement and blur for bold, eye-catching interfaces.
662
659
  </p>
663
- <Card title="Bold Appearance" text="Maximum visual impact and depth." glass />
660
+ <Card title="Bold Appearance" text="Maximum visual impact and depth." />
664
661
  </div>
665
662
  </EdgePanel>
666
663
 
@@ -693,7 +690,6 @@ export const GlassShowcase: Story = {
693
690
  <Card
694
691
  title="Premium Quality"
695
692
  text="Smooth, flowing distortions powered by WebGL shaders."
696
- glass
697
693
  />
698
694
  </div>
699
695
  </EdgePanel>
@@ -727,7 +723,6 @@ export const GlassShowcase: Story = {
727
723
  <Card
728
724
  title="Elite Design"
729
725
  text="The pinnacle of glass morphism for luxury applications."
730
- glass
731
726
  />
732
727
  </div>
733
728
  </EdgePanel>
@@ -127,16 +127,6 @@ export const EdgePanel: React.FC<EdgePanelProps> = ({
127
127
  {glass ? (
128
128
  <AtomixGlass
129
129
  {...glassProps}
130
- className="c-edge-panel__glass-wrapper"
131
- style={{
132
- position: 'fixed',
133
- width: glassContentRef.current?.offsetWidth,
134
- height: glassContentRef.current?.offsetHeight,
135
- top: containerRef.current?.offsetTop,
136
- left: containerRef.current?.offsetLeft,
137
- bottom: containerRef.current?.style.bottom,
138
- right: containerRef.current?.style.right,
139
- }}
140
130
  >
141
131
  <div
142
132
  ref={glassContentRef}