@oalacea/chaosui 0.1.0

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 (45) hide show
  1. package/bin/cli.js +216 -0
  2. package/components/backgrounds/glow-orbs/glow-orbs.module.css +31 -0
  3. package/components/backgrounds/glow-orbs/index.tsx +87 -0
  4. package/components/backgrounds/light-beams/index.tsx +80 -0
  5. package/components/backgrounds/light-beams/light-beams.module.css +27 -0
  6. package/components/backgrounds/noise-canvas/index.tsx +113 -0
  7. package/components/backgrounds/noise-canvas/noise-canvas.module.css +8 -0
  8. package/components/backgrounds/particle-field/index.tsx +81 -0
  9. package/components/backgrounds/particle-field/particle-field.module.css +31 -0
  10. package/components/buttons/chaos-button/chaos-button.module.css +173 -0
  11. package/components/buttons/chaos-button/index.tsx +60 -0
  12. package/components/buttons/glitch-button/glitch-button.module.css +197 -0
  13. package/components/buttons/glitch-button/index.tsx +53 -0
  14. package/components/effects/cursor-follower/cursor-follower.module.css +50 -0
  15. package/components/effects/cursor-follower/index.tsx +83 -0
  16. package/components/effects/screen-distortion/index.tsx +54 -0
  17. package/components/effects/screen-distortion/screen-distortion.module.css +127 -0
  18. package/components/effects/warning-tape/index.tsx +64 -0
  19. package/components/effects/warning-tape/warning-tape.module.css +29 -0
  20. package/components/glow-orbs/glow-orbs.module.css +31 -0
  21. package/components/glow-orbs/index.tsx +87 -0
  22. package/components/light-beams/index.tsx +80 -0
  23. package/components/light-beams/light-beams.module.css +27 -0
  24. package/components/noise-canvas/index.tsx +113 -0
  25. package/components/noise-canvas/noise-canvas.module.css +8 -0
  26. package/components/overlays/noise-overlay/index.tsx +56 -0
  27. package/components/overlays/noise-overlay/noise-overlay.module.css +20 -0
  28. package/components/overlays/scanlines/index.tsx +61 -0
  29. package/components/overlays/scanlines/scanlines.module.css +16 -0
  30. package/components/overlays/static-flicker/index.tsx +51 -0
  31. package/components/overlays/static-flicker/static-flicker.module.css +27 -0
  32. package/components/overlays/vignette/index.tsx +58 -0
  33. package/components/overlays/vignette/vignette.module.css +7 -0
  34. package/components/package.json +13 -0
  35. package/components/particle-field/index.tsx +81 -0
  36. package/components/particle-field/particle-field.module.css +31 -0
  37. package/components/text/distortion-text/distortion-text.module.css +100 -0
  38. package/components/text/distortion-text/index.tsx +53 -0
  39. package/components/text/falling-text/falling-text.module.css +57 -0
  40. package/components/text/falling-text/index.tsx +61 -0
  41. package/components/text/flicker-text/flicker-text.module.css +91 -0
  42. package/components/text/flicker-text/index.tsx +48 -0
  43. package/components/text/glitch-text/glitch-text.module.css +142 -0
  44. package/components/text/glitch-text/index.tsx +53 -0
  45. package/package.json +38 -0
@@ -0,0 +1,127 @@
1
+ .distortion {
2
+ inset: 0;
3
+ width: 100%;
4
+ height: 100%;
5
+ pointer-events: none;
6
+ z-index: 9990;
7
+ }
8
+
9
+ /* WAVE */
10
+ .wave {
11
+ background: transparent;
12
+ }
13
+
14
+ .wave::before {
15
+ content: '';
16
+ position: absolute;
17
+ inset: 0;
18
+ background: inherit;
19
+ animation: wave-distort var(--duration, 3s) ease-in-out infinite;
20
+ }
21
+
22
+ @keyframes wave-distort {
23
+ 0%, 100% { transform: scaleY(1) skewX(0); }
24
+ 25% { transform: scaleY(1.002) skewX(0.5deg); }
25
+ 50% { transform: scaleY(0.998) skewX(0); }
26
+ 75% { transform: scaleY(1.001) skewX(-0.5deg); }
27
+ }
28
+
29
+ /* GLITCH */
30
+ .glitch::before,
31
+ .glitch::after {
32
+ content: '';
33
+ position: absolute;
34
+ inset: 0;
35
+ background: inherit;
36
+ opacity: 0;
37
+ }
38
+
39
+ .glitch::before {
40
+ animation: glitch-distort-1 var(--duration, 4s) infinite;
41
+ }
42
+
43
+ .glitch::after {
44
+ animation: glitch-distort-2 var(--duration, 4s) infinite;
45
+ }
46
+
47
+ @keyframes glitch-distort-1 {
48
+ 0%, 85%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
49
+ 86% { opacity: 1; clip-path: inset(20% 0 60% 0); transform: translateX(var(--shift, -5px)); background: rgba(255, 0, 64, 0.1); }
50
+ 88% { opacity: 1; clip-path: inset(50% 0 30% 0); transform: translateX(var(--shift, 5px)); }
51
+ 90% { opacity: 0; }
52
+ }
53
+
54
+ @keyframes glitch-distort-2 {
55
+ 0%, 85%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
56
+ 87% { opacity: 1; clip-path: inset(40% 0 40% 0); transform: translateX(var(--shift, 3px)); background: rgba(0, 255, 255, 0.1); }
57
+ 89% { opacity: 1; clip-path: inset(70% 0 10% 0); transform: translateX(var(--shift, -3px)); }
58
+ 91% { opacity: 0; }
59
+ }
60
+
61
+ /* CHROMATIC */
62
+ .chromatic::before,
63
+ .chromatic::after {
64
+ content: '';
65
+ position: absolute;
66
+ inset: 0;
67
+ mix-blend-mode: screen;
68
+ }
69
+
70
+ .chromatic::before {
71
+ background: rgba(255, 0, 0, var(--opacity, 0.02));
72
+ animation: chromatic-r var(--duration, 5s) ease-in-out infinite;
73
+ }
74
+
75
+ .chromatic::after {
76
+ background: rgba(0, 255, 255, var(--opacity, 0.02));
77
+ animation: chromatic-c var(--duration, 5s) ease-in-out infinite reverse;
78
+ }
79
+
80
+ @keyframes chromatic-r {
81
+ 0%, 100% { transform: translate(0); }
82
+ 50% { transform: translate(var(--shift, 2px), var(--shift, -1px)); }
83
+ }
84
+
85
+ @keyframes chromatic-c {
86
+ 0%, 100% { transform: translate(0); }
87
+ 50% { transform: translate(calc(var(--shift, 2px) * -1), var(--shift, 1px)); }
88
+ }
89
+
90
+ /* NOISE */
91
+ .noise {
92
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
93
+ opacity: var(--opacity, 0.02);
94
+ animation: noise-animate var(--duration, 0.2s) steps(5) infinite;
95
+ }
96
+
97
+ @keyframes noise-animate {
98
+ 0% { transform: translate(0, 0); }
99
+ 25% { transform: translate(-1px, 1px); }
100
+ 50% { transform: translate(1px, -1px); }
101
+ 75% { transform: translate(-1px, -1px); }
102
+ 100% { transform: translate(0, 0); }
103
+ }
104
+
105
+ /* INTENSITY */
106
+ .subtle { --shift: 2px; --opacity: 0.01; }
107
+ .medium { --shift: 5px; --opacity: 0.02; }
108
+ .intense { --shift: 10px; --opacity: 0.04; }
109
+
110
+ /* SPEED */
111
+ .slow { --duration: 6s; }
112
+ .normal { --duration: 4s; }
113
+ .fast { --duration: 2s; }
114
+
115
+ .noise.slow { --duration: 0.3s; }
116
+ .noise.normal { --duration: 0.2s; }
117
+ .noise.fast { --duration: 0.1s; }
118
+
119
+ /* HOVER ONLY */
120
+ .hoverOnly {
121
+ opacity: 0;
122
+ }
123
+
124
+ .hoverOnly:hover,
125
+ *:hover > .hoverOnly {
126
+ opacity: 1;
127
+ }
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './warning-tape.module.css';
5
+
6
+ export interface WarningTapeProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** The text to scroll */
8
+ children: string;
9
+ /** Background color */
10
+ bgColor?: string;
11
+ /** Text color */
12
+ textColor?: string;
13
+ /** Scroll speed in seconds */
14
+ duration?: number;
15
+ /** Rotation angle in degrees */
16
+ rotation?: number;
17
+ /** Reverse scroll direction */
18
+ reverse?: boolean;
19
+ }
20
+
21
+ export const WarningTape = forwardRef<HTMLDivElement, WarningTapeProps>(
22
+ (
23
+ {
24
+ children,
25
+ bgColor = '#ff0040',
26
+ textColor = '#000000',
27
+ duration = 20,
28
+ rotation = -1,
29
+ reverse = false,
30
+ className,
31
+ style,
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ // Repeat text for seamless scroll
37
+ const repeatedText = `${children} • `.repeat(20);
38
+
39
+ return (
40
+ <div
41
+ ref={ref}
42
+ className={`${styles.tape} ${className || ''}`}
43
+ style={{
44
+ backgroundColor: bgColor,
45
+ color: textColor,
46
+ transform: `rotate(${rotation}deg) scale(1.1)`,
47
+ ...style,
48
+ }}
49
+ {...props}
50
+ >
51
+ <span
52
+ className={`${styles.scroll} ${reverse ? styles.reverse : ''}`}
53
+ style={{ animationDuration: `${duration}s` }}
54
+ >
55
+ {repeatedText}
56
+ </span>
57
+ </div>
58
+ );
59
+ }
60
+ );
61
+
62
+ WarningTape.displayName = 'WarningTape';
63
+
64
+ export default WarningTape;
@@ -0,0 +1,29 @@
1
+ .tape {
2
+ width: 100%;
3
+ padding: 0.75rem 0;
4
+ overflow: hidden;
5
+ white-space: nowrap;
6
+ font-weight: 700;
7
+ font-size: 0.75rem;
8
+ letter-spacing: 0.1em;
9
+ text-transform: uppercase;
10
+ }
11
+
12
+ .scroll {
13
+ display: inline-block;
14
+ animation: scroll-left 20s linear infinite;
15
+ }
16
+
17
+ .reverse {
18
+ animation-name: scroll-right;
19
+ }
20
+
21
+ @keyframes scroll-left {
22
+ from { transform: translateX(0); }
23
+ to { transform: translateX(-50%); }
24
+ }
25
+
26
+ @keyframes scroll-right {
27
+ from { transform: translateX(-50%); }
28
+ to { transform: translateX(0); }
29
+ }
@@ -0,0 +1,31 @@
1
+ .container {
2
+ inset: 0;
3
+ width: 100%;
4
+ height: 100%;
5
+ overflow: hidden;
6
+ pointer-events: none;
7
+ z-index: 1;
8
+ }
9
+
10
+ .orb {
11
+ position: absolute;
12
+ border-radius: 50%;
13
+ opacity: 0.3;
14
+ transform: translate(-50%, -50%);
15
+ animation: float 20s ease-in-out infinite;
16
+ }
17
+
18
+ @keyframes float {
19
+ 0%, 100% {
20
+ transform: translate(-50%, -50%) translate(0, 0);
21
+ }
22
+ 25% {
23
+ transform: translate(-50%, -50%) translate(30px, -40px);
24
+ }
25
+ 50% {
26
+ transform: translate(-50%, -50%) translate(-20px, 20px);
27
+ }
28
+ 75% {
29
+ transform: translate(-50%, -50%) translate(40px, 30px);
30
+ }
31
+ }
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './glow-orbs.module.css';
5
+
6
+ export interface GlowOrbsProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** Array of orb colors (will cycle through) */
8
+ colors?: string[];
9
+ /** Number of orbs */
10
+ count?: number;
11
+ /** Orb size range [min, max] in pixels */
12
+ sizeRange?: [number, number];
13
+ /** Blur amount in pixels */
14
+ blur?: number;
15
+ /** Animation duration range [min, max] in seconds */
16
+ durationRange?: [number, number];
17
+ /** Fixed or absolute positioning */
18
+ position?: 'fixed' | 'absolute';
19
+ }
20
+
21
+ export const GlowOrbs = forwardRef<HTMLDivElement, GlowOrbsProps>(
22
+ (
23
+ {
24
+ colors = ['#7c3aed', '#06b6d4', '#ec4899', '#10b981'],
25
+ count = 5,
26
+ sizeRange = [100, 300],
27
+ blur = 80,
28
+ durationRange = [15, 25],
29
+ position = 'fixed',
30
+ className,
31
+ style,
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ // Generate random orbs
37
+ const orbs = Array.from({ length: count }, (_, i) => {
38
+ const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
39
+ const duration = durationRange[0] + Math.random() * (durationRange[1] - durationRange[0]);
40
+ const delay = Math.random() * -duration;
41
+ const color = colors[i % colors.length];
42
+ const startX = Math.random() * 100;
43
+ const startY = Math.random() * 100;
44
+
45
+ return {
46
+ id: i,
47
+ size,
48
+ duration,
49
+ delay,
50
+ color,
51
+ startX,
52
+ startY,
53
+ };
54
+ });
55
+
56
+ return (
57
+ <div
58
+ ref={ref}
59
+ className={`${styles.container} ${className || ''}`}
60
+ style={{ position, ...style }}
61
+ aria-hidden="true"
62
+ {...props}
63
+ >
64
+ {orbs.map((orb) => (
65
+ <div
66
+ key={orb.id}
67
+ className={styles.orb}
68
+ style={{
69
+ width: orb.size,
70
+ height: orb.size,
71
+ background: orb.color,
72
+ filter: `blur(${blur}px)`,
73
+ left: `${orb.startX}%`,
74
+ top: `${orb.startY}%`,
75
+ animationDuration: `${orb.duration}s`,
76
+ animationDelay: `${orb.delay}s`,
77
+ }}
78
+ />
79
+ ))}
80
+ </div>
81
+ );
82
+ }
83
+ );
84
+
85
+ GlowOrbs.displayName = 'GlowOrbs';
86
+
87
+ export default GlowOrbs;
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './light-beams.module.css';
5
+
6
+ export interface LightBeamsProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** Array of beam colors */
8
+ colors?: string[];
9
+ /** Number of beams */
10
+ count?: number;
11
+ /** Beam width in pixels */
12
+ beamWidth?: number;
13
+ /** Beam opacity (0-1) */
14
+ opacity?: number;
15
+ /** Animation duration range [min, max] in seconds */
16
+ durationRange?: [number, number];
17
+ /** Fixed or absolute positioning */
18
+ position?: 'fixed' | 'absolute';
19
+ }
20
+
21
+ export const LightBeams = forwardRef<HTMLDivElement, LightBeamsProps>(
22
+ (
23
+ {
24
+ colors = ['#7c3aed', '#06b6d4', '#ec4899', '#10b981', '#f59e0b'],
25
+ count = 5,
26
+ beamWidth = 2,
27
+ opacity = 0.15,
28
+ durationRange = [15, 25],
29
+ position = 'fixed',
30
+ className,
31
+ style,
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ const beams = Array.from({ length: count }, (_, i) => {
37
+ const duration = durationRange[0] + Math.random() * (durationRange[1] - durationRange[0]);
38
+ const delay = Math.random() * -duration;
39
+ const color = colors[i % colors.length];
40
+ const positionX = ((i + 1) / (count + 1)) * 100;
41
+
42
+ return {
43
+ id: i,
44
+ duration,
45
+ delay,
46
+ color,
47
+ positionX,
48
+ };
49
+ });
50
+
51
+ return (
52
+ <div
53
+ ref={ref}
54
+ className={`${styles.container} ${className || ''}`}
55
+ style={{ position, ...style }}
56
+ aria-hidden="true"
57
+ {...props}
58
+ >
59
+ {beams.map((beam) => (
60
+ <div
61
+ key={beam.id}
62
+ className={styles.beam}
63
+ style={{
64
+ left: `${beam.positionX}%`,
65
+ width: beamWidth,
66
+ background: `linear-gradient(180deg, transparent, ${beam.color}, transparent)`,
67
+ opacity,
68
+ animationDuration: `${beam.duration}s`,
69
+ animationDelay: `${beam.delay}s`,
70
+ }}
71
+ />
72
+ ))}
73
+ </div>
74
+ );
75
+ }
76
+ );
77
+
78
+ LightBeams.displayName = 'LightBeams';
79
+
80
+ export default LightBeams;
@@ -0,0 +1,27 @@
1
+ .container {
2
+ inset: 0;
3
+ width: 100%;
4
+ height: 100%;
5
+ overflow: hidden;
6
+ pointer-events: none;
7
+ z-index: 1;
8
+ }
9
+
10
+ .beam {
11
+ position: absolute;
12
+ top: 0;
13
+ height: 100%;
14
+ filter: blur(1px);
15
+ animation: beam-move 20s ease-in-out infinite;
16
+ }
17
+
18
+ @keyframes beam-move {
19
+ 0%, 100% {
20
+ transform: translateX(-50px) skewX(-5deg);
21
+ opacity: 0.1;
22
+ }
23
+ 50% {
24
+ transform: translateX(50px) skewX(5deg);
25
+ opacity: 0.2;
26
+ }
27
+ }
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './noise-canvas.module.css';
5
+
6
+ export interface NoiseCanvasProps extends HTMLAttributes<HTMLCanvasElement> {
7
+ /** Noise opacity (0-1) */
8
+ opacity?: number;
9
+ /** Animation speed (fps) */
10
+ fps?: number;
11
+ /** Noise intensity (0-255) */
12
+ intensity?: number;
13
+ /** Monochrome or colored noise */
14
+ monochrome?: boolean;
15
+ /** Fixed or absolute positioning */
16
+ position?: 'fixed' | 'absolute';
17
+ }
18
+
19
+ export const NoiseCanvas = forwardRef<HTMLCanvasElement, NoiseCanvasProps>(
20
+ (
21
+ {
22
+ opacity = 0.05,
23
+ fps = 24,
24
+ intensity = 50,
25
+ monochrome = true,
26
+ position = 'fixed',
27
+ className,
28
+ style,
29
+ ...props
30
+ },
31
+ ref
32
+ ) => {
33
+ const canvasRef = useRef<HTMLCanvasElement>(null);
34
+ const animationRef = useRef<number>();
35
+
36
+ useEffect(() => {
37
+ const canvas = canvasRef.current;
38
+ if (!canvas) return;
39
+
40
+ const ctx = canvas.getContext('2d');
41
+ if (!ctx) return;
42
+
43
+ const resize = () => {
44
+ canvas.width = window.innerWidth;
45
+ canvas.height = window.innerHeight;
46
+ };
47
+
48
+ resize();
49
+ window.addEventListener('resize', resize);
50
+
51
+ let lastFrame = 0;
52
+ const frameInterval = 1000 / fps;
53
+
54
+ const render = (timestamp: number) => {
55
+ if (timestamp - lastFrame >= frameInterval) {
56
+ const imageData = ctx.createImageData(canvas.width, canvas.height);
57
+ const data = imageData.data;
58
+
59
+ for (let i = 0; i < data.length; i += 4) {
60
+ const value = Math.random() * intensity;
61
+
62
+ if (monochrome) {
63
+ data[i] = value;
64
+ data[i + 1] = value;
65
+ data[i + 2] = value;
66
+ } else {
67
+ data[i] = Math.random() * intensity;
68
+ data[i + 1] = Math.random() * intensity;
69
+ data[i + 2] = Math.random() * intensity;
70
+ }
71
+ data[i + 3] = 255;
72
+ }
73
+
74
+ ctx.putImageData(imageData, 0, 0);
75
+ lastFrame = timestamp;
76
+ }
77
+
78
+ animationRef.current = requestAnimationFrame(render);
79
+ };
80
+
81
+ animationRef.current = requestAnimationFrame(render);
82
+
83
+ return () => {
84
+ window.removeEventListener('resize', resize);
85
+ if (animationRef.current) {
86
+ cancelAnimationFrame(animationRef.current);
87
+ }
88
+ };
89
+ }, [fps, intensity, monochrome]);
90
+
91
+ return (
92
+ <canvas
93
+ ref={(node) => {
94
+ (canvasRef as React.MutableRefObject<HTMLCanvasElement | null>).current = node;
95
+ if (typeof ref === 'function') ref(node);
96
+ else if (ref) ref.current = node;
97
+ }}
98
+ className={`${styles.canvas} ${className || ''}`}
99
+ style={{
100
+ position,
101
+ opacity,
102
+ ...style,
103
+ }}
104
+ aria-hidden="true"
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+ );
110
+
111
+ NoiseCanvas.displayName = 'NoiseCanvas';
112
+
113
+ export default NoiseCanvas;
@@ -0,0 +1,8 @@
1
+ .canvas {
2
+ inset: 0;
3
+ width: 100%;
4
+ height: 100%;
5
+ pointer-events: none;
6
+ z-index: 9999;
7
+ mix-blend-mode: overlay;
8
+ }
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+ import styles from './noise-overlay.module.css';
5
+
6
+ export interface NoiseOverlayProps extends HTMLAttributes<HTMLDivElement> {
7
+ /** Noise opacity (0-1) */
8
+ opacity?: number;
9
+ /** Noise frequency (higher = finer grain) */
10
+ frequency?: number;
11
+ /** Enable animation */
12
+ animated?: boolean;
13
+ /** Blend mode */
14
+ blendMode?: 'overlay' | 'multiply' | 'screen' | 'soft-light' | 'normal';
15
+ /** Fixed position (covers viewport) or absolute (covers parent) */
16
+ position?: 'fixed' | 'absolute';
17
+ }
18
+
19
+ export const NoiseOverlay = forwardRef<HTMLDivElement, NoiseOverlayProps>(
20
+ (
21
+ {
22
+ opacity = 0.05,
23
+ frequency = 0.8,
24
+ animated = false,
25
+ blendMode = 'overlay',
26
+ position = 'fixed',
27
+ className,
28
+ style,
29
+ ...props
30
+ },
31
+ ref
32
+ ) => {
33
+ // Generate SVG noise inline
34
+ const noiseSvg = `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='${frequency}' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E")`;
35
+
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ className={`${styles.noise} ${animated ? styles.animated : ''} ${className || ''}`}
40
+ style={{
41
+ position,
42
+ opacity,
43
+ mixBlendMode: blendMode,
44
+ backgroundImage: noiseSvg,
45
+ ...style,
46
+ }}
47
+ aria-hidden="true"
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+ );
53
+
54
+ NoiseOverlay.displayName = 'NoiseOverlay';
55
+
56
+ export default NoiseOverlay;
@@ -0,0 +1,20 @@
1
+ .noise {
2
+ inset: 0;
3
+ width: 100%;
4
+ height: 100%;
5
+ pointer-events: none;
6
+ z-index: 9999;
7
+ }
8
+
9
+ .animated {
10
+ animation: noise-shift 0.2s steps(5) infinite;
11
+ }
12
+
13
+ @keyframes noise-shift {
14
+ 0% { transform: translate(0, 0); }
15
+ 20% { transform: translate(-2px, 1px); }
16
+ 40% { transform: translate(1px, -1px); }
17
+ 60% { transform: translate(2px, 2px); }
18
+ 80% { transform: translate(-1px, -2px); }
19
+ 100% { transform: translate(0, 0); }
20
+ }