@shohojdhara/atomix 0.4.7 → 0.4.8

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/dist/atomix.css +24 -37
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.js +51 -46
  6. package/dist/charts.js.map +1 -1
  7. package/dist/core.js +51 -46
  8. package/dist/core.js.map +1 -1
  9. package/dist/forms.js +51 -46
  10. package/dist/forms.js.map +1 -1
  11. package/dist/heavy.js +51 -46
  12. package/dist/heavy.js.map +1 -1
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.esm.js +51 -46
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/index.js +51 -46
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.min.js +1 -1
  19. package/dist/index.min.js.map +1 -1
  20. package/package.json +1 -1
  21. package/scripts/atomix-cli.js +40 -1875
  22. package/scripts/cli/commands/build-theme.js +112 -0
  23. package/scripts/cli/commands/generate.js +97 -0
  24. package/scripts/cli/commands/init.js +46 -0
  25. package/scripts/cli/internal/compiler.js +114 -0
  26. package/scripts/cli/internal/filesystem.js +58 -0
  27. package/scripts/cli/internal/generator.js +110 -0
  28. package/scripts/cli/internal/wizard.js +61 -0
  29. package/scripts/cli/utils/error.js +47 -0
  30. package/scripts/cli/utils/helpers.js +43 -0
  31. package/scripts/cli/utils/logger.js +75 -0
  32. package/scripts/cli/utils/validation.js +71 -0
  33. package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
  34. package/src/components/AtomixGlass/AtomixGlass.tsx +41 -29
  35. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +4 -19
  36. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
  37. package/src/lib/composables/useAtomixGlass.ts +4 -1
  38. package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
  39. package/src/lib/constants/components.ts +7 -7
  40. package/src/styles/06-components/_components.atomix-glass.scss +17 -21
  41. package/src/styles/06-components/_components.edge-panel.scss +1 -5
  42. package/src/styles/06-components/_components.modal.scss +1 -4
  43. package/src/styles/06-components/_components.navbar.scss +1 -1
  44. package/src/styles/06-components/_components.tooltip.scss +9 -5
  45. package/scripts/cli/component-generator.js +0 -564
  46. package/scripts/cli/interactive-init.js +0 -357
  47. package/scripts/cli/utils.js +0 -359
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Atomix CLI Logger
3
+ * Standardized logging with support for spinners and debug levels
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import boxen from 'boxen';
9
+
10
+ const DEBUG = process.env.ATOMIX_DEBUG === 'true';
11
+
12
+ export const logger = {
13
+ /**
14
+ * Log a debug message (only visible in debug mode)
15
+ */
16
+ debug: (message, data = null) => {
17
+ if (DEBUG) {
18
+ console.log(chalk.gray(`[DEBUG] ${message}`));
19
+ if (data) {
20
+ console.log(chalk.gray(JSON.stringify(data, null, 2)));
21
+ }
22
+ }
23
+ },
24
+
25
+ /**
26
+ * Log an info message
27
+ */
28
+ info: (message) => {
29
+ console.log(chalk.white(message));
30
+ },
31
+
32
+ /**
33
+ * Log a success message
34
+ */
35
+ success: (message) => {
36
+ console.log(chalk.green(`✓ ${message}`));
37
+ },
38
+
39
+ /**
40
+ * Log a warning message
41
+ */
42
+ warn: (message) => {
43
+ console.log(chalk.yellow(`! ${message}`));
44
+ },
45
+
46
+ /**
47
+ * Log an error message
48
+ */
49
+ error: (message) => {
50
+ console.error(chalk.bold.red(`\n❌ ${message}`));
51
+ },
52
+
53
+ /**
54
+ * Create and return a spinner
55
+ */
56
+ spinner: (text) => {
57
+ return ora({
58
+ text,
59
+ color: 'cyan'
60
+ });
61
+ },
62
+
63
+ /**
64
+ * Create a boxed message
65
+ */
66
+ box: (message, options = {}) => {
67
+ console.log(boxen(message, {
68
+ padding: 1,
69
+ margin: 1,
70
+ borderStyle: 'round',
71
+ borderColor: 'cyan',
72
+ ...options
73
+ }));
74
+ }
75
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Atomix CLI Validation Utilities
3
+ */
4
+
5
+ /**
6
+ * Validates component names according to PascalCase convention
7
+ * @param {string} name - The component name to validate
8
+ * @returns {Object} { isValid: boolean, error?: string }
9
+ */
10
+ export function validateComponentName(name) {
11
+ if (!name || typeof name !== 'string') {
12
+ return {
13
+ isValid: false,
14
+ error: 'Component name must be a non-empty string'
15
+ };
16
+ }
17
+
18
+ // Check PascalCase: starts with uppercase, only contains letters and numbers
19
+ if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
20
+ return {
21
+ isValid: false,
22
+ error: 'Component name must be in PascalCase (e.g., Button, CardHeader)'
23
+ };
24
+ }
25
+
26
+ // Check for reserved words
27
+ const reservedWords = [
28
+ 'Component', 'React', 'Fragment', 'Suspense', 'StrictMode',
29
+ 'Error', 'Loading', 'App', 'Root', 'Document', 'Html'
30
+ ];
31
+
32
+ if (reservedWords.includes(name)) {
33
+ return {
34
+ isValid: false,
35
+ error: `"${name}" is a reserved word. Please choose a different name.`
36
+ };
37
+ }
38
+
39
+ // Check minimum length
40
+ if (name.length < 2) {
41
+ return {
42
+ isValid: false,
43
+ error: 'Component name must be at least 2 characters long'
44
+ };
45
+ }
46
+
47
+ return { isValid: true };
48
+ }
49
+
50
+ /**
51
+ * Validates theme names according to kebab-case convention
52
+ */
53
+ export function validateThemeName(name) {
54
+ if (!name || typeof name !== 'string') return { isValid: false, error: 'Theme name must be a string' };
55
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) return { isValid: false, error: 'Theme name must be kebab-case' };
56
+ if (/--/.test(name)) return { isValid: false, error: 'Cannot contain consecutive hyphens' };
57
+ if (name.endsWith('-')) return { isValid: false, error: 'Cannot end with a hyphen' };
58
+ return { isValid: true };
59
+ }
60
+
61
+ /**
62
+ * Validates SCSS/CSS color values
63
+ */
64
+ export function isValidColor(color) {
65
+ const patterns = [
66
+ /^#[0-9A-F]{3,8}$/i,
67
+ /^(rgb|rgba|hsl|hsla)\(/i,
68
+ /^var\(--/
69
+ ];
70
+ return patterns.some(pattern => pattern.test(color));
71
+ }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import { vi } from 'vitest';
5
5
  import AtomixGlass from './AtomixGlass';
@@ -158,18 +158,52 @@ describe('AtomixGlass Component', () => {
158
158
  expect(container.querySelector('.c-atomix-glass__container')).toBeInTheDocument();
159
159
  });
160
160
 
161
- test('applies custom style', () => {
162
- const customStyle = { backgroundColor: 'red' };
161
+ test('applies custom style to root/container layout', () => {
162
+ const customStyle: React.CSSProperties = { backgroundColor: 'red', position: 'fixed', top: 0, left: 0 };
163
163
  const { container } = render(
164
164
  <AtomixGlass style={customStyle}>
165
165
  <div>Content</div>
166
166
  </AtomixGlass>
167
167
  );
168
168
 
169
+ const root = container.querySelector('.c-atomix-glass');
169
170
  const glassContainer = container.querySelector('.c-atomix-glass__container');
171
+ expect(root).toHaveStyle('position: fixed');
170
172
  expect(glassContainer).toHaveStyle('background-color: rgb(255, 0, 0)');
171
173
  });
172
174
 
175
+ test('sets 100% width/height for fixed/sticky positioning', async () => {
176
+ const { container } = render(
177
+ <AtomixGlass style={{ position: 'fixed' }}>
178
+ <div>Content</div>
179
+ </AtomixGlass>
180
+ );
181
+
182
+ const glassContainer = container.querySelector('.c-atomix-glass__container');
183
+
184
+ // Use waitFor because updateAtomixGlassStyles is called imperatively inside a requestAnimationFrame loop
185
+ await waitFor(() => {
186
+ // With the new logic, fixed/sticky elements use measured sizes,
187
+ // not 100% (which is for standard flow)
188
+ expect(glassContainer).not.toHaveStyle('--atomix-glass-container-width: 100%');
189
+ });
190
+ });
191
+
192
+ test('sets 100% width/height for standard flow (not fixed/sticky)', async () => {
193
+ const { container } = render(
194
+ <AtomixGlass>
195
+ <div>Content</div>
196
+ </AtomixGlass>
197
+ );
198
+
199
+ const glassContainer = container.querySelector('.c-atomix-glass__container');
200
+
201
+ await waitFor(() => {
202
+ expect(glassContainer).toHaveStyle('--atomix-glass-container-width: 100%');
203
+ expect(glassContainer).toHaveStyle('--atomix-glass-container-height: 100%');
204
+ });
205
+ });
206
+
173
207
  test('uses standard mode by default', () => {
174
208
  const { container } = render(
175
209
  <AtomixGlass>
@@ -113,6 +113,17 @@ export function AtomixGlass({
113
113
  const glassRef = useRef<HTMLDivElement>(null);
114
114
  const contentRef = useRef<HTMLDivElement>(null);
115
115
 
116
+ // ── Layout hoisting ──────────────────────────────────────────────────
117
+ // When position is fixed/sticky the layout props must live on the ROOT
118
+ // `.c-atomix-glass` element so that every decorative layer (borders,
119
+ // backgrounds, hover effects) stays in the same stacking context.
120
+
121
+ // Extract zIndex from style so it becomes the base for ALL internal
122
+ // layers via --atomix-glass-base-z-index. It must NOT be applied as a
123
+ // real z-index on the root element — that would break the glass effect.
124
+ const { zIndex: customZIndex, ...restStyle } = style;
125
+ const isFixedOrSticky = restStyle.position === 'fixed' || restStyle.position === 'sticky';
126
+
116
127
  // Use composable hook for all state and logic
117
128
  const {
118
129
  isHovered,
@@ -153,23 +164,13 @@ export function AtomixGlass({
153
164
  withLiquidBlur,
154
165
  padding,
155
166
  style,
167
+ isFixedOrSticky,
156
168
  });
157
169
 
158
170
  const isOverLight = useMemo(() => overLightConfig.isOverLight, [overLightConfig.isOverLight]);
159
171
 
160
172
  const shouldRenderOverLightLayers = withOverLightLayers && isOverLight;
161
173
 
162
- // ── Layout hoisting ──────────────────────────────────────────────────
163
- // When position is fixed/sticky the layout props must live on the ROOT
164
- // `.c-atomix-glass` element so that every decorative layer (borders,
165
- // backgrounds, hover effects) stays in the same stacking context.
166
-
167
- // Extract zIndex from style so it becomes the base for ALL internal
168
- // layers via --atomix-glass-base-z-index. It must NOT be applied as a
169
- // real z-index on the root element — that would break the glass effect.
170
- const { zIndex: customZIndex, ...restStyle } = style;
171
- const isFixedOrSticky = restStyle.position === 'fixed' || restStyle.position === 'sticky';
172
-
173
174
  const rootLayoutStyle = useMemo<React.CSSProperties>(() => {
174
175
  if (!isFixedOrSticky) return {};
175
176
  const { position: p, top: t, left: l, right: r, bottom: b } = restStyle;
@@ -223,21 +224,26 @@ export function AtomixGlass({
223
224
  );
224
225
 
225
226
  const adjustedSize = useMemo(() => {
226
- const resolveSize = (
227
- propValue: string | number | undefined,
228
- styleValue: string | number | undefined,
229
- measuredSize: number
230
- ) => {
231
- const explicitSize = propValue ?? styleValue;
232
- if (explicitSize !== undefined) {
233
- return typeof explicitSize === 'number' ? `${explicitSize}px` : explicitSize;
227
+ // Keep a reference to positionStyles to avoid unused-variable lint,
228
+ // but sizing is driven by explicit width/height or measured size.
229
+ const _position = positionStyles.position;
230
+
231
+ const resolveLength = (value: string | number | undefined, measured: number): string => {
232
+ if (value !== undefined) {
233
+ return typeof value === 'number' ? `${value}px` : value;
234
234
  }
235
- return positionStyles.position === 'fixed' ? `${Math.max(measuredSize, 0)}px` : '100%';
235
+ if (measured > 0) {
236
+ return `${measured}px`;
237
+ }
238
+ return '100%';
236
239
  };
237
240
 
241
+ const effectiveWidth = width ?? restStyle.width;
242
+ const effectiveHeight = height ?? restStyle.height;
243
+
238
244
  return {
239
- width: resolveSize(width, restStyle.width, glassSize.width),
240
- height: resolveSize(height, restStyle.height, glassSize.height),
245
+ width: resolveLength(effectiveWidth, glassSize.width),
246
+ height: resolveLength(effectiveHeight, glassSize.height),
241
247
  };
242
248
  }, [
243
249
  width,
@@ -338,9 +344,10 @@ export function AtomixGlass({
338
344
  ...(customZIndex !== undefined && { '--atomix-glass-base-z-index': customZIndex }),
339
345
  '--atomix-glass-radius': `${effectiveBorderRadius}px`,
340
346
  '--atomix-glass-transform': transformStyle || 'none',
341
- '--atomix-glass-position': positionStyles.position,
342
- '--atomix-glass-top': positionStyles.top !== 'fixed' ? `${positionStyles.top}px` : '0',
343
- '--atomix-glass-left': positionStyles.left !== 'fixed' ? `${positionStyles.left}px` : '0',
347
+ // Internal decorative layers are positioned relative to the root;
348
+ '--atomix-glass-position': rootLayoutStyle.position,
349
+ '--atomix-glass-top': `${isFixedOrSticky ? rootLayoutStyle.top : 0}px`,
350
+ '--atomix-glass-left': `${isFixedOrSticky ? rootLayoutStyle.left : 0}px`,
344
351
  '--atomix-glass-width': adjustedSize.width,
345
352
  '--atomix-glass-height': adjustedSize.height,
346
353
  '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
@@ -376,11 +383,12 @@ export function AtomixGlass({
376
383
  opacityValues,
377
384
  effectiveBorderRadius,
378
385
  transformStyle,
379
- positionStyles,
380
386
  adjustedSize,
381
387
  isOverLight,
382
388
  overLightConfig.borderOpacity,
383
389
  customZIndex,
390
+ rootLayoutStyle,
391
+ isFixedOrSticky,
384
392
  ]);
385
393
 
386
394
  // Helper function to render background layers
@@ -417,7 +425,12 @@ export function AtomixGlass({
417
425
  ref={glassRef}
418
426
  contentRef={contentRef}
419
427
  className={className}
420
- style={rootLayoutStyle}
428
+ style={{
429
+ ...restStyle,
430
+ ...(!isFixedOrSticky && {
431
+ position: 'relative',
432
+ }),
433
+ }}
421
434
  borderRadius={effectiveBorderRadius}
422
435
  displacementScale={
423
436
  effectiveWithoutEffects
@@ -462,7 +475,6 @@ export function AtomixGlass({
462
475
  }}
463
476
  onClick={onClick}
464
477
  mode={mode}
465
- transform={baseStyle.transform}
466
478
  effectiveWithoutEffects={effectiveWithoutEffects}
467
479
  effectiveReducedMotion={effectiveReducedMotion}
468
480
  shaderVariant={shaderVariant}
@@ -480,7 +492,7 @@ export function AtomixGlass({
480
492
  )}
481
493
 
482
494
  {/* Background layers for over-light mode */}
483
- {/* Static styles (pointer-events, will-change) are in SCSS */}
495
+ {/* Static styles (pointer-events) are in SCSS; will-change is managed via .u-glass-clean-root utility for backdrop-filter stability */}
484
496
  {renderBackgroundLayer('dark')}
485
497
  {renderBackgroundLayer('black')}
486
498
  {shouldRenderOverLightLayers && (
@@ -111,12 +111,10 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
111
111
  saturation = 180,
112
112
  aberrationIntensity = 2,
113
113
  mouseOffset = { x: 0, y: 0 },
114
- globalMousePosition = { x: 0, y: 0 },
115
114
  onMouseEnter,
116
115
  onMouseLeave,
117
116
  onMouseDown,
118
117
  onMouseUp,
119
- isHovered = false,
120
118
  isActive = false,
121
119
  overLight = false,
122
120
  overLightConfig = {},
@@ -401,6 +399,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
401
399
  effectiveReducedMotion,
402
400
  effectiveWithoutEffects,
403
401
  withLiquidBlur,
402
+ overLightConfig,
404
403
  ]);
405
404
 
406
405
  const containerVars = useMemo(() => {
@@ -415,9 +414,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
415
414
  ? mouseOffset.y
416
415
  : 0;
417
416
  return {
418
- '--atomix-glass-container-width': `${glassSize?.width}`,
419
- '--atomix-glass-container-height': `${glassSize?.height}`,
420
- '--atomix-glass-container-padding': padding || '0 0',
421
417
  '--atomix-glass-container-radius': `${typeof borderRadius === 'number' && !isNaN(borderRadius) ? borderRadius : 0}px`,
422
418
  '--atomix-glass-container-backdrop': backdropStyle?.backdropFilter || 'none',
423
419
  '--atomix-glass-container-shadow': overLight
@@ -434,7 +430,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
434
430
  ? `linear-gradient(${180 + mx * 0.5}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)`
435
431
  : 'none',
436
432
  '--atomix-glass-container-text-shadow': overLight
437
- ? '0px 2px 12px rgba(0, 0, 0, 0)'
433
+ ? '0px 1px 2px rgba(255, 255, 255, 0.15)'
438
434
  : '0px 2px 12px rgba(0, 0, 0, 0.4)',
439
435
  '--atomix-glass-container-box-shadow': overLight
440
436
  ? '0px 16px 70px rgba(0, 0, 0, 0.75)'
@@ -453,24 +449,14 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
453
449
  } as React.CSSProperties;
454
450
  }
455
451
  }, [
456
- glassSize,
457
- padding,
458
- borderRadius,
452
+ borderRadius,
459
453
  backdropStyle,
460
454
  mouseOffset,
461
455
  overLight,
462
456
  effectiveWithoutEffects,
457
+ overLightConfig,
463
458
  ]);
464
459
 
465
- // Helper to force no transition/animation overrides with !important
466
- const setForceNoTransition = (el: HTMLElement | null) => {
467
- if (el) {
468
- el.style.setProperty('transition-duration', '0s', 'important');
469
- el.style.setProperty('animation-duration', '0s', 'important');
470
- el.style.setProperty('transition-delay', '0s', 'important');
471
- }
472
- };
473
-
474
460
  return (
475
461
  <div
476
462
  ref={el => {
@@ -513,7 +499,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
513
499
  />
514
500
  {/* Enhanced Apple Liquid Glass Inner Shadow Layer */}
515
501
  <div
516
- ref={setForceNoTransition}
517
502
  className={ATOMIX_GLASS.FILTER_OVERLAY_CLASS}
518
503
  style={{
519
504
  filter: `url(#${filterId})`,
@@ -0,0 +1,216 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`] = `
4
+ <div>
5
+ <div
6
+ class="c-atomix-glass"
7
+ style="--atomix-glass-radius: 16px; --atomix-glass-transform: scale(1); --atomix-glass-top: 0px; --atomix-glass-left: 0px; --atomix-glass-width: 270px; --atomix-glass-height: 69px; --atomix-glass-border-width: var(--atomix-spacing-0-5, 0.09375rem); --atomix-glass-blend-mode: overlay; --atomix-glass-border-gradient-1: linear-gradient(135deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.08399999999999999) 33%, rgba(255, 255, 255, 0.27999999999999997) 66%, rgba(255, 255, 255, 0) 100%); --atomix-glass-border-gradient-2: linear-gradient(135deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.22399999999999998) 33%, rgba(255, 255, 255, 0.42) 66%, rgba(255, 255, 255, 0) 100%); --atomix-glass-hover-1-opacity: 0; --atomix-glass-hover-1-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 50%); --atomix-glass-hover-2-opacity: 0; --atomix-glass-hover-2-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 80%); --atomix-glass-hover-3-opacity: 0; --atomix-glass-hover-3-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); --atomix-glass-base-opacity: 0; --atomix-glass-base-gradient: rgba(255, 255, 255, 0.1); --atomix-glass-overlay-opacity: 0; --atomix-glass-overlay-gradient: rgba(255, 255, 255, 0.05); --atomix-glass-overlay-highlight-opacity: 0; --atomix-glass-overlay-highlight-bg: radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.4) 0%, transparent 60%);"
8
+ >
9
+ <div
10
+ class="c-atomix-glass__container "
11
+ style="position: relative; --atomix-glass-container-radius: 16px; --atomix-glass-container-backdrop: blur(0.1px) saturate(140%) contrast(1.4) brightness(0.9); --atomix-glass-container-shadow: 0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset; --atomix-glass-container-shadow-opacity: 1; --atomix-glass-container-bg: none; --atomix-glass-container-text-shadow: 0px 2px 12px rgba(0, 0, 0, 0.4); --atomix-glass-container-box-shadow: 0px 12px 40px rgba(0, 0, 0, 0.25); --atomix-glass-container-width: 100%; --atomix-glass-container-height: 100%; --atomix-glass-container-padding: 0;"
12
+ >
13
+ <div
14
+ class="c-atomix-glass__inner"
15
+ >
16
+ <div
17
+ class="c-atomix-glass__filter"
18
+ >
19
+ <svg
20
+ aria-hidden="true"
21
+ style="position: absolute; width: 100%; height: 100%; inset: 0;"
22
+ >
23
+ <defs>
24
+ <radialgradient
25
+ cx="50%"
26
+ cy="50%"
27
+ id="atomix-glass-filter-16-edge-mask"
28
+ r="50%"
29
+ >
30
+ <stop
31
+ offset="0%"
32
+ stop-color="black"
33
+ stop-opacity="0"
34
+ />
35
+ <stop
36
+ offset="76%"
37
+ stop-color="black"
38
+ stop-opacity="0"
39
+ />
40
+ <stop
41
+ offset="100%"
42
+ stop-color="white"
43
+ stop-opacity="1"
44
+ />
45
+ </radialgradient>
46
+ <filter
47
+ color-interpolation-filters="sRGB"
48
+ height="170%"
49
+ id="atomix-glass-filter-16"
50
+ width="170%"
51
+ x="-35%"
52
+ y="-35%"
53
+ >
54
+ <feimage
55
+ height="100%"
56
+ href="data:image/jpeg;base64,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cUAUAkEkziomgGgkoxZGgxZFQFQlYnQHRdPfj/10KCSCKESCNiVkViPSLpD0h6I0Q0I0A2IoBWBIJIBKBIJoJIJ2R2J0JWBUJ0JUB0XTv479dFZDYiglYigkhEgjZFQjRFQjRFQjQigFYigHYigmgEgmglYlYnQlQlYlQHQlQnQ9P/kf1yVkNiNCNkNiVENiNiViNEViNkVCVgKCViViViSCViSCVgdCViVCViVCdgVCVCdD1D+U/XBWQ2I0I2Q2JUQ2I0JWQ0I2JUQ2JUI2JUI2J0JWJWJWA2R0BWJ0I2JUJ2BUJUJ0P//EABkQAQEBAQEBAAAAAAAAAAAAAAECABEDEP/aAAgBAQABAgB1atWrVq1atWrVq1atWrVq1atWrVq1atWrVq+OrVq1atWrVq1atWrVq1atWrVq1atWrVq1atXxVppppppdWrVq1atWrVq1NNNNNNNNNNNPVWmmmmms6tWrVq1atWpppppppppppppp6q0000uc51atWrVq1ammmmmmmmmmmmmt1Vpppc5znVq1atWrVqaaaaaaaaaaaaaeqtNLnOc51atWrVq1ammmmmmmmmmmmmnqrS5znOc6tWrVq16222mmmmmmlVppp6tKuc5znOrVq1a9TbbbbTTTTTSq000qtLnOc5zq1atWrW0222200000qqqtKqrnOc5zq1atTbbbbbbbbTTTSqqqqqq5znOc6tTTTbbbbbbbbTTTSqqqqrlVznOctNNNtttttttttNNNNKqqqrqznKqrTTTTbbbbbbbbbTTTSqqqqrqznOc5aaaabbbbbbbbbaaaaVVVVVdWc5znVq1NNttttttttttNNKqqqqudWc5znVq16tbbbbbbbbbbTTSqqqq5XVnOc6tWrVrb1tttttttttNNKqqqqrWrK5VWmmm2230bbbbbbaaaXOc5zlVa1KuVVppptttt9G22222mmlzlVznK6tWVVWmmmm2222222222mlznOc5znLWppVVWmmm22222229bTWrOc5znOcq1qaaVpWmm222222229erVqznOc5znKtatStK0rTbTTbbbberXr1as5znOc5aVpppppWlabaabbbb1ta9WrVnOc5znU0rTTTTTTTTTbTTbbbTWvVq1as5znOdTTStNNNNNNNNNtNNtttN6tWvVq1ZznOrU00rTTTTTTTTTTTTTbTWvVq1atWrOc6tTTTStNNNNNNNNNNtNNtNa9WrVq1Z1Z1NNNNNK1q1NNNNNNNNNNNtNatWrVq1atWrU00000rWrVq1atWrVq1alaaa1atWrVq1NNNammmmla1atWrVq1aterVq16tWrVnVqa1NK1qaaaVX/xAAWEAADAAAAAAAAAAAAAAAAAAAhgJD/2gAIAQEAAz8AaExf/8QAGhEBAQEBAQEBAAAAAAAAAAAAAQISEQADEP/aAAgBAgEBAgDx48ePHjx48ePHjx48ePHjx48ePHjx48ePHj86IiIiIiInjx48ePHjx48IiIiIj0oooooooooRERER73ve60UUUUUUVrWiiiiiihERERER73ve97ooooorRWiiiiihKERERER73ve973RRRRWtFFFFFFCIiIiIiPe973ve60UUVrRRRRRRQiIlCIiI973ve973pRRWiiiiiiiiiiiiiiihEe973ve973RRWtFFFFFFFFFFFFFFFFFFa13ve973WitaKKKKKKKKKKKKKKKKKK1rWtd1rutFa1oooooooooooosssooorWta1rWta1rRRRRRRRRRRZZZZZZZZZWta1rWta1rRRRRRRRRZZZZZZZZZZZZe9a1rWta1rWitaKLLLLLLLLLLLLLLLLL3rWta1rWtFbLLLLLLLLLLLLLLLLLLLL3vWta1rWita1ssssssss+hZZZZZZZZe961rWta0Vre97LLLLLLLLLLLPoWWWWWXrWta1oorWta3ssss+hZZZZ9Cyyyyyyyyiita1orWta1ve9llllllllllllllllFFa0VorWta1ve9llllllllllllllllllFFFaK1rWta1rWiyyyyyyyyyyyyiiiiiiitFFa1rWta1oosoosssssoooosoooorRRRWta1rWta0UUUUUWUUUUUUUUUUUVoooorWta1rWtaKKKKKKmiiiiiiiiiiiiiiitd73ve61oSiiipoqaKKKKKKKKKK0UUUVrve973vREREZoSihEooooorRRRRWtd73ve9EREREREoSiiiiitFllllla73ve9ERERERESiiiiiitH0PoWWWWVrXe96IiIiMoiJRRRRRRWjwlFFllllFFd6IiIiIlCUUUUUUUUUePHjx48ePCIiIiIiIiUUUUUUUUUUUePHjx48ePHjx48ePHjx48IiUUUUUUJRRRX//xAAWEQADAAAAAAAAAAAAAAAAAAABYJD/2gAIAQIBAz8AtEV7/8QAFxEBAQEBAAAAAAAAAAAAAAAAAAECEP/aAAgBAwEBAgCtNNNNNNNNNNNNNNNNNNNNNNNNNNNNNcrTTTTTTTTTTTTTTTTTTTTTTTTTTTTTXKrTTTTTTTU000000000000000000001FVpppppqampqaaaaaaaaaaaaaaaaaaaa5Vaaaaampqampqammmmmmmmmmmlaaaaaaiq0001NTU1NTU1NTTTTTTTTTTSqqtNNNcqtNNSyzU1LNTU1NTTTTTTTTTSqqq001ytNLLLLNTU1NTU1NTbbbTTTTTSqqq001ytNLLLLLNTU1NTU3NttttNNNNNKqq001KrSyyyyyzU1NTU3Nzc02220000qqqqrSqqyyyyyzU1NTU3Nzc3NttttNNNKqqqqqqssssss1NTU3Nzc3NzbbbbTTTSqqqqqqrLLLLLNTU1Nzc3Nzc22220000qqqqqqqqssss1NTU3Nzc3NzbbbbbTTSqqqqqqqqqqzU1NTc3Nzc3Nzbc22000qqqqqqqqqqqtTU3Nzc3Nzc3NtzbTTSqqqqrKqqqqqtNNzc23Nzc3Nzc3NTU1KqqqrKqqqqqtNNNNttzc3Nzc3NzU1NLLLLLKqqqqqqqq0022223Nzc3NzU1NSyyyyyyqqqqqqqrTTbbbbc3Nzc3NTU1LLLLLLKsqqqqqqrTTTTbbbc3Nzc1NTUsssssssqqqqqqrTTTTTbbbTc3NTU1NTUsssssqqqqqqqq0000222023NTU1NTUsssssqqqqqqqq000000003NTU1NTU1LLLLLNKrTSqqqqtNNNNNNtNNTU1NSzUssss00qq0qqqqrTTTTTTTTTU1NTUs1LLLNNNKrTTTSqqq00000000001NTU1LNTU0000qtNNNKqqqtNNNNNNNNTU1NTUs1NNNNNKss1NNNK00qtK0000001NNTU0s000000qq000001NKrStNNNNK1NNNNStNNNNNKqtNNNNNNNK0000000rU0000rTTTTTSq00000rTTTTTTTTTTTTTTTTStNNNNKr/xAAUEQEAAAAAAAAAAAAAAAAAAACg/9oACAEDAQM/AAAf/9k="
57
+ id="atomix-glass-filter-16-image"
58
+ preserveAspectRatio="xMidYMid slice"
59
+ result="DISPLACEMENT_MAP"
60
+ width="100%"
61
+ x="0"
62
+ y="0"
63
+ />
64
+ <fecolormatrix
65
+ in="DISPLACEMENT_MAP"
66
+ result="EDGE_INTENSITY"
67
+ type="matrix"
68
+ values="0.3 0.3 0.3 0 0
69
+ 0.3 0.3 0.3 0 0
70
+ 0.3 0.3 0.3 0 0
71
+ 0 0 0 1 0"
72
+ />
73
+ <fecomponenttransfer
74
+ in="EDGE_INTENSITY"
75
+ result="EDGE_MASK"
76
+ >
77
+ <fefunca
78
+ tableValues="0 0.1 1"
79
+ type="discrete"
80
+ />
81
+ </fecomponenttransfer>
82
+ <feoffset
83
+ dx="0"
84
+ dy="0"
85
+ in="SourceGraphic"
86
+ result="CENTER_ORIGINAL"
87
+ />
88
+ <fedisplacementmap
89
+ in="SourceGraphic"
90
+ in2="DISPLACEMENT_MAP"
91
+ result="RED_DISPLACED"
92
+ scale="-70"
93
+ xChannelSelector="R"
94
+ yChannelSelector="B"
95
+ />
96
+ <fecolormatrix
97
+ in="RED_DISPLACED"
98
+ result="RED_CHANNEL"
99
+ type="matrix"
100
+ values="1 0 0 0 0
101
+ 0 0 0 0 0
102
+ 0 0 0 0 0
103
+ 0 0 0 1 0"
104
+ />
105
+ <fedisplacementmap
106
+ in="SourceGraphic"
107
+ in2="DISPLACEMENT_MAP"
108
+ result="GREEN_DISPLACED"
109
+ scale="-72.8"
110
+ xChannelSelector="R"
111
+ yChannelSelector="B"
112
+ />
113
+ <fecolormatrix
114
+ in="GREEN_DISPLACED"
115
+ result="GREEN_CHANNEL"
116
+ type="matrix"
117
+ values="0 0 0 0 0
118
+ 0 1 0 0 0
119
+ 0 0 0 0 0
120
+ 0 0 0 1 0"
121
+ />
122
+ <fedisplacementmap
123
+ in="SourceGraphic"
124
+ in2="DISPLACEMENT_MAP"
125
+ result="BLUE_DISPLACED"
126
+ scale="-74.2"
127
+ xChannelSelector="R"
128
+ yChannelSelector="B"
129
+ />
130
+ <fecolormatrix
131
+ in="BLUE_DISPLACED"
132
+ result="BLUE_CHANNEL"
133
+ type="matrix"
134
+ values="0 0 0 0 0
135
+ 0 0 0 0 0
136
+ 0 0 1 0 0
137
+ 0 0 0 1 0"
138
+ />
139
+ <feblend
140
+ in="GREEN_CHANNEL"
141
+ in2="BLUE_CHANNEL"
142
+ mode="screen"
143
+ result="GB_COMBINED"
144
+ />
145
+ <feblend
146
+ in="RED_CHANNEL"
147
+ in2="GB_COMBINED"
148
+ mode="screen"
149
+ result="RGB_COMBINED"
150
+ />
151
+ <fegaussianblur
152
+ in="RGB_COMBINED"
153
+ result="ABERRATED_BLURRED"
154
+ stdDeviation="0"
155
+ />
156
+ <fecomposite
157
+ in="ABERRATED_BLURRED"
158
+ in2="EDGE_MASK"
159
+ operator="in"
160
+ result="EDGE_ABERRATION"
161
+ />
162
+ <fecomponenttransfer
163
+ in="EDGE_MASK"
164
+ result="INVERTED_MASK"
165
+ >
166
+ <fefunca
167
+ tableValues="1 0"
168
+ type="table"
169
+ />
170
+ </fecomponenttransfer>
171
+ <fecomposite
172
+ in="CENTER_ORIGINAL"
173
+ in2="INVERTED_MASK"
174
+ operator="in"
175
+ result="CENTER_CLEAN"
176
+ />
177
+ <fecomposite
178
+ in="EDGE_ABERRATION"
179
+ in2="CENTER_CLEAN"
180
+ operator="over"
181
+ />
182
+ </filter>
183
+ </defs>
184
+ </svg>
185
+ <div
186
+ class="c-atomix-glass__filter-overlay"
187
+ style="filter: url(#atomix-glass-filter-16);"
188
+ />
189
+ <div
190
+ class="c-atomix-glass__filter-shadow"
191
+ />
192
+ </div>
193
+ <div
194
+ class="c-atomix-glass__content"
195
+ >
196
+ <div>
197
+ Default Glass
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ <div
203
+ class="c-atomix-glass__background-layer c-atomix-glass__background-layer--dark c-atomix-glass__background-layer--hidden"
204
+ />
205
+ <div
206
+ class="c-atomix-glass__background-layer c-atomix-glass__background-layer--black c-atomix-glass__background-layer--hidden"
207
+ />
208
+ <span
209
+ class="c-atomix-glass__border-1"
210
+ />
211
+ <span
212
+ class="c-atomix-glass__border-2"
213
+ />
214
+ </div>
215
+ </div>
216
+ `;