@oalacea/chaosui 0.1.0 → 0.4.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 (73) hide show
  1. package/bin/cli.js +75 -13
  2. package/components/backgrounds/glow-orbs/index.tsx +1 -1
  3. package/components/buttons/chaos-button/chaos-button.module.css +3 -2
  4. package/components/buttons/glitch-button/glitch-button.module.css +7 -7
  5. package/components/chaos-vars.css +27 -0
  6. package/components/cyber/cyber-avatar/css/cyber-avatar.module.css +60 -0
  7. package/components/cyber/cyber-avatar/css/index.tsx +28 -0
  8. package/components/cyber/cyber-avatar/tailwind/index.tsx +46 -0
  9. package/components/cyber/cyber-input/css/cyber-input.module.css +87 -0
  10. package/components/cyber/cyber-input/css/index.tsx +49 -0
  11. package/components/cyber/cyber-input/tailwind/index.tsx +55 -0
  12. package/components/cyber/cyber-loader/css/cyber-loader.module.css +102 -0
  13. package/components/cyber/cyber-loader/css/index.tsx +58 -0
  14. package/components/cyber/cyber-loader/tailwind/index.tsx +63 -0
  15. package/components/cyber/cyber-modal/css/cyber-modal.module.css +124 -0
  16. package/components/cyber/cyber-modal/css/index.tsx +75 -0
  17. package/components/cyber/cyber-modal/tailwind/index.tsx +87 -0
  18. package/components/cyber/cyber-slider/css/cyber-slider.module.css +61 -0
  19. package/components/cyber/cyber-slider/css/index.tsx +41 -0
  20. package/components/cyber/cyber-slider/tailwind/index.tsx +51 -0
  21. package/components/cyber/cyber-tooltip/css/cyber-tooltip.module.css +67 -0
  22. package/components/cyber/cyber-tooltip/css/index.tsx +36 -0
  23. package/components/cyber/cyber-tooltip/tailwind/index.tsx +48 -0
  24. package/components/effects/glitch-image/css/glitch-image.module.css +64 -0
  25. package/components/effects/glitch-image/css/index.tsx +25 -0
  26. package/components/effects/glitch-image/tailwind/index.tsx +49 -0
  27. package/components/effects/glowing-border/css/glowing-border.module.css +73 -0
  28. package/components/effects/glowing-border/css/index.tsx +45 -0
  29. package/components/effects/glowing-border/tailwind/index.tsx +40 -0
  30. package/components/effects/screen-distortion/screen-distortion.module.css +2 -2
  31. package/components/effects/warning-tape/index.tsx +4 -4
  32. package/components/effects/warning-tape/warning-tape.module.css +2 -0
  33. package/components/layout/data-grid/css/data-grid.module.css +52 -0
  34. package/components/layout/data-grid/css/index.tsx +76 -0
  35. package/components/layout/data-grid/tailwind/index.tsx +74 -0
  36. package/components/layout/hologram-card/css/hologram-card.module.css +102 -0
  37. package/components/layout/hologram-card/css/index.tsx +46 -0
  38. package/components/layout/hologram-card/tailwind/index.tsx +61 -0
  39. package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +55 -0
  40. package/components/navigation/hexagon-menu/css/index.tsx +35 -0
  41. package/components/navigation/hexagon-menu/tailwind/index.tsx +53 -0
  42. package/components/neon/neon-alert/css/index.tsx +53 -0
  43. package/components/neon/neon-alert/css/neon-alert.module.css +60 -0
  44. package/components/neon/neon-alert/tailwind/index.tsx +59 -0
  45. package/components/neon/neon-badge/css/index.tsx +49 -0
  46. package/components/neon/neon-badge/css/neon-badge.module.css +53 -0
  47. package/components/neon/neon-badge/tailwind/index.tsx +50 -0
  48. package/components/neon/neon-button/css/index.tsx +54 -0
  49. package/components/neon/neon-button/css/neon-button.module.css +114 -0
  50. package/components/neon/neon-button/tailwind/index.tsx +51 -0
  51. package/components/neon/neon-divider/css/index.tsx +26 -0
  52. package/components/neon/neon-divider/css/neon-divider.module.css +43 -0
  53. package/components/neon/neon-divider/tailwind/index.tsx +36 -0
  54. package/components/neon/neon-progress/css/index.tsx +65 -0
  55. package/components/neon/neon-progress/css/neon-progress.module.css +88 -0
  56. package/components/neon/neon-progress/tailwind/index.tsx +46 -0
  57. package/components/neon/neon-tabs/css/index.tsx +41 -0
  58. package/components/neon/neon-tabs/css/neon-tabs.module.css +45 -0
  59. package/components/neon/neon-tabs/tailwind/index.tsx +53 -0
  60. package/components/neon/neon-toggle/css/index.tsx +58 -0
  61. package/components/neon/neon-toggle/css/neon-toggle.module.css +79 -0
  62. package/components/neon/neon-toggle/tailwind/index.tsx +57 -0
  63. package/components/text/glitch-text/glitch-text.module.css +2 -2
  64. package/package.json +1 -1
  65. package/components/glow-orbs/glow-orbs.module.css +0 -31
  66. package/components/glow-orbs/index.tsx +0 -87
  67. package/components/light-beams/index.tsx +0 -80
  68. package/components/light-beams/light-beams.module.css +0 -27
  69. package/components/noise-canvas/index.tsx +0 -113
  70. package/components/noise-canvas/noise-canvas.module.css +0 -8
  71. package/components/package.json +0 -13
  72. package/components/particle-field/index.tsx +0 -81
  73. package/components/particle-field/particle-field.module.css +0 -31
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes } from 'react';
4
+ import styles from './neon-toggle.module.css';
5
+
6
+ export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
7
+ /** Current checked state */
8
+ checked: boolean;
9
+ /** Change callback */
10
+ onChange: (checked: boolean) => void;
11
+ /** Neon color variant */
12
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
13
+ /** Toggle size */
14
+ size?: 'sm' | 'md' | 'lg';
15
+ }
16
+
17
+ export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
18
+ (
19
+ {
20
+ checked,
21
+ onChange,
22
+ variant = 'cyan',
23
+ size = 'md',
24
+ disabled = false,
25
+ className,
26
+ ...props
27
+ },
28
+ ref
29
+ ) => {
30
+ const classes = [
31
+ styles.toggle,
32
+ styles[variant],
33
+ styles[size],
34
+ checked && styles.checked,
35
+ className,
36
+ ].filter(Boolean).join(' ');
37
+
38
+ return (
39
+ <button
40
+ ref={ref}
41
+ type="button"
42
+ role="switch"
43
+ aria-checked={checked}
44
+ disabled={disabled}
45
+ className={classes}
46
+ onClick={() => !disabled && onChange(!checked)}
47
+ {...props}
48
+ >
49
+ <span className={styles.track} />
50
+ <span className={styles.thumb} />
51
+ </button>
52
+ );
53
+ }
54
+ );
55
+
56
+ NeonToggle.displayName = 'NeonToggle';
57
+
58
+ export default NeonToggle;
@@ -0,0 +1,79 @@
1
+ .toggle {
2
+ --toggle-color: #00f0ff;
3
+ --toggle-width: 3.25rem;
4
+ --toggle-height: 1.75rem;
5
+ --thumb-size: 1.25rem;
6
+
7
+ position: relative;
8
+ width: var(--toggle-width);
9
+ height: var(--toggle-height);
10
+ padding: 0;
11
+ background: transparent;
12
+ border: none;
13
+ cursor: pointer;
14
+ }
15
+
16
+ /* Sizes */
17
+ .sm {
18
+ --toggle-width: 2.5rem;
19
+ --toggle-height: 1.375rem;
20
+ --thumb-size: 1rem;
21
+ }
22
+
23
+ .lg {
24
+ --toggle-width: 4rem;
25
+ --toggle-height: 2.125rem;
26
+ --thumb-size: 1.625rem;
27
+ }
28
+
29
+ /* Variants */
30
+ .cyan { --toggle-color: #00f0ff; }
31
+ .pink { --toggle-color: #ff00ff; }
32
+ .green { --toggle-color: #00ff88; }
33
+ .purple { --toggle-color: #a855f7; }
34
+
35
+ .track {
36
+ position: absolute;
37
+ top: 0;
38
+ left: 0;
39
+ width: 100%;
40
+ height: 100%;
41
+ background: rgba(255, 255, 255, 0.1);
42
+ border: 1px solid rgba(255, 255, 255, 0.2);
43
+ border-radius: var(--toggle-height);
44
+ transition: all 0.3s ease;
45
+ }
46
+
47
+ .checked .track {
48
+ background: rgba(0, 240, 255, 0.2);
49
+ border-color: var(--toggle-color);
50
+ box-shadow: 0 0 10px var(--toggle-color), inset 0 0 10px rgba(0, 240, 255, 0.1);
51
+ }
52
+
53
+ .thumb {
54
+ position: absolute;
55
+ top: 50%;
56
+ left: 0.25rem;
57
+ width: var(--thumb-size);
58
+ height: var(--thumb-size);
59
+ background: #666;
60
+ border-radius: 50%;
61
+ transform: translateY(-50%);
62
+ transition: all 0.3s ease;
63
+ }
64
+
65
+ .checked .thumb {
66
+ left: calc(100% - var(--thumb-size) - 0.25rem);
67
+ background: var(--toggle-color);
68
+ box-shadow: 0 0 10px var(--toggle-color), 0 0 20px var(--toggle-color);
69
+ }
70
+
71
+ .toggle:disabled {
72
+ opacity: 0.5;
73
+ cursor: not-allowed;
74
+ }
75
+
76
+ .toggle:focus-visible .track {
77
+ outline: 2px solid var(--toggle-color);
78
+ outline-offset: 2px;
79
+ }
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, ButtonHTMLAttributes } from 'react';
4
+
5
+ export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
6
+ checked: boolean;
7
+ onChange: (checked: boolean) => void;
8
+ variant?: 'cyan' | 'pink' | 'green' | 'purple';
9
+ size?: 'sm' | 'md' | 'lg';
10
+ }
11
+
12
+ const sizeStyles = {
13
+ sm: { track: 'w-10 h-5', thumb: 'w-4 h-4', translate: 'translate-x-5' },
14
+ md: { track: 'w-[52px] h-7', thumb: 'w-5 h-5', translate: 'translate-x-6' },
15
+ lg: { track: 'w-16 h-8', thumb: 'w-6 h-6', translate: 'translate-x-8' },
16
+ };
17
+
18
+ const variantStyles = {
19
+ cyan: { active: 'bg-cyan-400/20 border-cyan-400 shadow-[0_0_10px_#00f0ff]', thumb: 'bg-cyan-400 shadow-[0_0_10px_#00f0ff]' },
20
+ pink: { active: 'bg-fuchsia-500/20 border-fuchsia-500 shadow-[0_0_10px_#ff00ff]', thumb: 'bg-fuchsia-500 shadow-[0_0_10px_#ff00ff]' },
21
+ green: { active: 'bg-emerald-400/20 border-emerald-400 shadow-[0_0_10px_#00ff88]', thumb: 'bg-emerald-400 shadow-[0_0_10px_#00ff88]' },
22
+ purple: { active: 'bg-purple-500/20 border-purple-500 shadow-[0_0_10px_#a855f7]', thumb: 'bg-purple-500 shadow-[0_0_10px_#a855f7]' },
23
+ };
24
+
25
+ export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
26
+ ({ checked, onChange, variant = 'cyan', size = 'md', disabled = false, className = '', ...props }, ref) => {
27
+ const sizes = sizeStyles[size];
28
+ const colors = variantStyles[variant];
29
+
30
+ return (
31
+ <button
32
+ ref={ref}
33
+ type="button"
34
+ role="switch"
35
+ aria-checked={checked}
36
+ disabled={disabled}
37
+ className={`
38
+ relative ${sizes.track} rounded-full border transition-all duration-300
39
+ ${checked ? colors.active : 'bg-white/10 border-white/20'}
40
+ ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
41
+ ${className}
42
+ `}
43
+ onClick={() => !disabled && onChange(!checked)}
44
+ {...props}
45
+ >
46
+ <span className={`
47
+ absolute top-1/2 left-1 -translate-y-1/2
48
+ ${sizes.thumb} rounded-full transition-all duration-300
49
+ ${checked ? `${sizes.translate} ${colors.thumb}` : 'bg-gray-500'}
50
+ `} />
51
+ </button>
52
+ );
53
+ }
54
+ );
55
+
56
+ NeonToggle.displayName = 'NeonToggle';
57
+ export default NeonToggle;
@@ -1,8 +1,8 @@
1
1
  .glitch {
2
2
  position: relative;
3
3
  display: inline-block;
4
- --glitch-color: #ff0040;
5
- --glitch-color-alt: #00ffff;
4
+ --glitch-color: hsl(var(--primary, 347 100% 50%));
5
+ --glitch-color-alt: hsl(var(--secondary, 180 100% 50%));
6
6
  }
7
7
 
8
8
  .glitch::before,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oalacea/chaosui",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "Glitch, noise, and distortion components for React. Copy-paste like shadcn.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,31 +0,0 @@
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
- }
@@ -1,87 +0,0 @@
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;
@@ -1,80 +0,0 @@
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;
@@ -1,27 +0,0 @@
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
- }
@@ -1,113 +0,0 @@
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;
@@ -1,8 +0,0 @@
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
- }
@@ -1,13 +0,0 @@
1
- {
2
- "name": "@chaos/components",
3
- "version": "0.1.0",
4
- "description": "Glitch, noise, and distortion UI components",
5
- "type": "module",
6
- "exports": {
7
- "./text/*": "./text/*/index.tsx",
8
- "./overlays/*": "./overlays/*/index.tsx",
9
- "./buttons/*": "./buttons/*/index.tsx",
10
- "./effects/*": "./effects/*/index.tsx",
11
- "./backgrounds/*": "./backgrounds/*/index.tsx"
12
- }
13
- }
@@ -1,81 +0,0 @@
1
- 'use client';
2
-
3
- import { forwardRef, HTMLAttributes, useMemo } from 'react';
4
- import styles from './particle-field.module.css';
5
-
6
- export interface ParticleFieldProps extends HTMLAttributes<HTMLDivElement> {
7
- /** Number of particles */
8
- count?: number;
9
- /** Particle color */
10
- color?: string;
11
- /** Particle size range [min, max] in pixels */
12
- sizeRange?: [number, number];
13
- /** Animation duration range [min, max] in seconds */
14
- durationRange?: [number, number];
15
- /** Particle opacity */
16
- opacity?: number;
17
- /** Fixed or absolute positioning */
18
- position?: 'fixed' | 'absolute';
19
- }
20
-
21
- export const ParticleField = forwardRef<HTMLDivElement, ParticleFieldProps>(
22
- (
23
- {
24
- count = 50,
25
- color = '#ffffff',
26
- sizeRange = [1, 3],
27
- durationRange = [10, 20],
28
- opacity = 0.5,
29
- position = 'fixed',
30
- className,
31
- style,
32
- ...props
33
- },
34
- ref
35
- ) => {
36
- const particles = useMemo(() => {
37
- return 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 startX = Math.random() * 100;
42
- const startY = Math.random() * 100;
43
- const drift = (Math.random() - 0.5) * 100;
44
-
45
- return { id: i, size, duration, delay, startX, startY, drift };
46
- });
47
- }, [count, sizeRange, durationRange]);
48
-
49
- return (
50
- <div
51
- ref={ref}
52
- className={`${styles.container} ${className || ''}`}
53
- style={{ position, ...style }}
54
- aria-hidden="true"
55
- {...props}
56
- >
57
- {particles.map((p) => (
58
- <div
59
- key={p.id}
60
- className={styles.particle}
61
- style={{
62
- width: p.size,
63
- height: p.size,
64
- backgroundColor: color,
65
- opacity,
66
- left: `${p.startX}%`,
67
- top: `${p.startY}%`,
68
- animationDuration: `${p.duration}s`,
69
- animationDelay: `${p.delay}s`,
70
- '--drift': `${p.drift}px`,
71
- } as React.CSSProperties}
72
- />
73
- ))}
74
- </div>
75
- );
76
- }
77
- );
78
-
79
- ParticleField.displayName = 'ParticleField';
80
-
81
- export default ParticleField;
@@ -1,31 +0,0 @@
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
- .particle {
11
- position: absolute;
12
- border-radius: 50%;
13
- animation: drift linear infinite;
14
- }
15
-
16
- @keyframes drift {
17
- 0% {
18
- transform: translate(0, 0);
19
- opacity: 0;
20
- }
21
- 10% {
22
- opacity: var(--opacity, 0.5);
23
- }
24
- 90% {
25
- opacity: var(--opacity, 0.5);
26
- }
27
- 100% {
28
- transform: translate(var(--drift, 50px), -100vh);
29
- opacity: 0;
30
- }
31
- }