@oalacea/chaosui 0.1.0 → 0.5.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 (139) hide show
  1. package/bin/cli.js +105 -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/cta-brutal/cta-brutal.module.css +81 -0
  5. package/components/buttons/cta-brutal/index.tsx +56 -0
  6. package/components/buttons/dead-button/dead-button.module.css +111 -0
  7. package/components/buttons/dead-button/index.tsx +47 -0
  8. package/components/buttons/deeper-button/deeper-button.module.css +76 -0
  9. package/components/buttons/deeper-button/index.tsx +51 -0
  10. package/components/buttons/dual-choice/dual-choice.module.css +90 -0
  11. package/components/buttons/dual-choice/index.tsx +54 -0
  12. package/components/buttons/glitch-button/glitch-button.module.css +7 -7
  13. package/components/buttons/tension-bar/index.tsx +79 -0
  14. package/components/buttons/tension-bar/tension-bar.module.css +105 -0
  15. package/components/chaos-vars.css +27 -0
  16. package/components/cyber/cyber-avatar/css/cyber-avatar.module.css +60 -0
  17. package/components/cyber/cyber-avatar/css/index.tsx +28 -0
  18. package/components/cyber/cyber-avatar/tailwind/index.tsx +46 -0
  19. package/components/cyber/cyber-input/css/cyber-input.module.css +87 -0
  20. package/components/cyber/cyber-input/css/index.tsx +49 -0
  21. package/components/cyber/cyber-input/tailwind/index.tsx +55 -0
  22. package/components/cyber/cyber-loader/css/cyber-loader.module.css +102 -0
  23. package/components/cyber/cyber-loader/css/index.tsx +58 -0
  24. package/components/cyber/cyber-loader/tailwind/index.tsx +63 -0
  25. package/components/cyber/cyber-modal/css/cyber-modal.module.css +124 -0
  26. package/components/cyber/cyber-modal/css/index.tsx +75 -0
  27. package/components/cyber/cyber-modal/tailwind/index.tsx +87 -0
  28. package/components/cyber/cyber-slider/css/cyber-slider.module.css +61 -0
  29. package/components/cyber/cyber-slider/css/index.tsx +41 -0
  30. package/components/cyber/cyber-slider/tailwind/index.tsx +51 -0
  31. package/components/cyber/cyber-tooltip/css/cyber-tooltip.module.css +67 -0
  32. package/components/cyber/cyber-tooltip/css/index.tsx +36 -0
  33. package/components/cyber/cyber-tooltip/tailwind/index.tsx +48 -0
  34. package/components/decorative/coffee-stain/coffee-stain.module.css +24 -0
  35. package/components/decorative/coffee-stain/index.tsx +55 -0
  36. package/components/decorative/ornaments/index.tsx +51 -0
  37. package/components/decorative/ornaments/ornaments.module.css +33 -0
  38. package/components/decorative/rune-symbols/index.tsx +55 -0
  39. package/components/decorative/rune-symbols/rune-symbols.module.css +22 -0
  40. package/components/effects/glitch-image/css/glitch-image.module.css +64 -0
  41. package/components/effects/glitch-image/css/index.tsx +25 -0
  42. package/components/effects/glitch-image/tailwind/index.tsx +49 -0
  43. package/components/effects/glowing-border/css/glowing-border.module.css +73 -0
  44. package/components/effects/glowing-border/css/index.tsx +45 -0
  45. package/components/effects/glowing-border/tailwind/index.tsx +40 -0
  46. package/components/effects/screen-distortion/screen-distortion.module.css +2 -2
  47. package/components/effects/warning-tape/index.tsx +4 -4
  48. package/components/effects/warning-tape/warning-tape.module.css +2 -0
  49. package/components/layout/data-grid/css/data-grid.module.css +52 -0
  50. package/components/layout/data-grid/css/index.tsx +76 -0
  51. package/components/layout/data-grid/tailwind/index.tsx +74 -0
  52. package/components/layout/hologram-card/css/hologram-card.module.css +102 -0
  53. package/components/layout/hologram-card/css/index.tsx +46 -0
  54. package/components/layout/hologram-card/tailwind/index.tsx +61 -0
  55. package/components/layout/horizontal-scroll/horizontal-scroll.module.css +30 -0
  56. package/components/layout/horizontal-scroll/index.tsx +78 -0
  57. package/components/layout/spec-grid/index.tsx +56 -0
  58. package/components/layout/spec-grid/spec-grid.module.css +21 -0
  59. package/components/layout/tower-pricing/index.tsx +56 -0
  60. package/components/layout/tower-pricing/tower-pricing.module.css +27 -0
  61. package/components/layout/tracklist/index.tsx +45 -0
  62. package/components/layout/tracklist/tracklist.module.css +24 -0
  63. package/components/layout/void-frame/index.tsx +32 -0
  64. package/components/layout/void-frame/void-frame.module.css +38 -0
  65. package/components/navigation/brutal-nav/brutal-nav.module.css +85 -0
  66. package/components/navigation/brutal-nav/index.tsx +71 -0
  67. package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +55 -0
  68. package/components/navigation/hexagon-menu/css/index.tsx +35 -0
  69. package/components/navigation/hexagon-menu/tailwind/index.tsx +53 -0
  70. package/components/navigation/progress-dots/index.tsx +55 -0
  71. package/components/navigation/progress-dots/progress-dots.module.css +91 -0
  72. package/components/navigation/scattered-nav/index.tsx +59 -0
  73. package/components/navigation/scattered-nav/scattered-nav.module.css +113 -0
  74. package/components/navigation/scroll-indicator/index.tsx +58 -0
  75. package/components/navigation/scroll-indicator/scroll-indicator.module.css +82 -0
  76. package/components/navigation/vertical-nav/index.tsx +59 -0
  77. package/components/navigation/vertical-nav/vertical-nav.module.css +98 -0
  78. package/components/neon/neon-alert/css/index.tsx +53 -0
  79. package/components/neon/neon-alert/css/neon-alert.module.css +60 -0
  80. package/components/neon/neon-alert/tailwind/index.tsx +59 -0
  81. package/components/neon/neon-badge/css/index.tsx +49 -0
  82. package/components/neon/neon-badge/css/neon-badge.module.css +53 -0
  83. package/components/neon/neon-badge/tailwind/index.tsx +50 -0
  84. package/components/neon/neon-button/css/index.tsx +54 -0
  85. package/components/neon/neon-button/css/neon-button.module.css +114 -0
  86. package/components/neon/neon-button/tailwind/index.tsx +51 -0
  87. package/components/neon/neon-divider/css/index.tsx +26 -0
  88. package/components/neon/neon-divider/css/neon-divider.module.css +43 -0
  89. package/components/neon/neon-divider/tailwind/index.tsx +36 -0
  90. package/components/neon/neon-progress/css/index.tsx +65 -0
  91. package/components/neon/neon-progress/css/neon-progress.module.css +88 -0
  92. package/components/neon/neon-progress/tailwind/index.tsx +46 -0
  93. package/components/neon/neon-tabs/css/index.tsx +41 -0
  94. package/components/neon/neon-tabs/css/neon-tabs.module.css +45 -0
  95. package/components/neon/neon-tabs/tailwind/index.tsx +53 -0
  96. package/components/neon/neon-toggle/css/index.tsx +58 -0
  97. package/components/neon/neon-toggle/css/neon-toggle.module.css +79 -0
  98. package/components/neon/neon-toggle/tailwind/index.tsx +57 -0
  99. package/components/text/ascii-art/css/ascii-art.module.css +173 -0
  100. package/components/text/ascii-art/css/index.tsx +116 -0
  101. package/components/text/ascii-art/tailwind/index.tsx +124 -0
  102. package/components/text/blood-drip/css/blood-drip.module.css +142 -0
  103. package/components/text/blood-drip/css/index.tsx +113 -0
  104. package/components/text/blood-drip/tailwind/index.tsx +133 -0
  105. package/components/text/char-glitch/css/char-glitch.module.css +124 -0
  106. package/components/text/char-glitch/css/index.tsx +153 -0
  107. package/components/text/char-glitch/tailwind/index.tsx +126 -0
  108. package/components/text/countdown-display/css/countdown-display.module.css +179 -0
  109. package/components/text/countdown-display/css/index.tsx +190 -0
  110. package/components/text/countdown-display/tailwind/index.tsx +155 -0
  111. package/components/text/giant-layers/css/giant-layers.module.css +156 -0
  112. package/components/text/giant-layers/css/index.tsx +97 -0
  113. package/components/text/giant-layers/tailwind/index.tsx +111 -0
  114. package/components/text/glitch-text/glitch-text.module.css +2 -2
  115. package/components/text/reveal-text/css/index.tsx +180 -0
  116. package/components/text/reveal-text/css/reveal-text.module.css +129 -0
  117. package/components/text/reveal-text/tailwind/index.tsx +135 -0
  118. package/components/text/rotate-text/css/index.tsx +139 -0
  119. package/components/text/rotate-text/css/rotate-text.module.css +162 -0
  120. package/components/text/rotate-text/tailwind/index.tsx +127 -0
  121. package/components/text/strike-reveal/css/index.tsx +124 -0
  122. package/components/text/strike-reveal/css/strike-reveal.module.css +139 -0
  123. package/components/text/strike-reveal/tailwind/index.tsx +138 -0
  124. package/components/text/terminal-output/css/index.tsx +179 -0
  125. package/components/text/terminal-output/css/terminal-output.module.css +203 -0
  126. package/components/text/terminal-output/tailwind/index.tsx +174 -0
  127. package/components/text/typing-text/css/index.tsx +115 -0
  128. package/components/text/typing-text/css/typing-text.module.css +84 -0
  129. package/components/text/typing-text/tailwind/index.tsx +126 -0
  130. package/package.json +1 -1
  131. package/components/glow-orbs/glow-orbs.module.css +0 -31
  132. package/components/glow-orbs/index.tsx +0 -87
  133. package/components/light-beams/index.tsx +0 -80
  134. package/components/light-beams/light-beams.module.css +0 -27
  135. package/components/noise-canvas/index.tsx +0 -113
  136. package/components/noise-canvas/noise-canvas.module.css +0 -8
  137. package/components/package.json +0 -13
  138. package/components/particle-field/index.tsx +0 -81
  139. package/components/particle-field/particle-field.module.css +0 -31
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState } from 'react';
4
+
5
+ export interface AsciiArtProps extends HTMLAttributes<HTMLPreElement> {
6
+ children: string;
7
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
8
+ variant?: 'default' | 'blood' | 'cyber' | 'matrix' | 'amber' | 'ghost' | 'gradient';
9
+ animation?: 'none' | 'typing' | 'reveal' | 'glitch' | 'flicker' | 'pulse';
10
+ scanlines?: boolean;
11
+ bordered?: boolean;
12
+ title?: string;
13
+ revealDelay?: number;
14
+ color?: string;
15
+ }
16
+
17
+ const sizeClasses = {
18
+ xs: 'text-[0.5rem]',
19
+ sm: 'text-[0.65rem]',
20
+ md: 'text-[0.8rem]',
21
+ lg: 'text-base',
22
+ xl: 'text-xl',
23
+ };
24
+
25
+ const variantClasses = {
26
+ default: '',
27
+ blood: 'text-rose-500 drop-shadow-[0_0_5px_#ff004080]',
28
+ cyber: 'text-cyan-400 drop-shadow-[0_0_5px_#00ffff80]',
29
+ matrix: 'text-green-400 drop-shadow-[0_0_5px_#00ff0080]',
30
+ amber: 'text-amber-500 drop-shadow-[0_0_5px_#ffaa0080]',
31
+ ghost: 'text-gray-500 opacity-70',
32
+ gradient: 'bg-gradient-to-b from-rose-500 via-cyan-400 to-green-400 bg-clip-text text-transparent',
33
+ };
34
+
35
+ const animationClasses = {
36
+ none: '',
37
+ typing: 'overflow-hidden whitespace-nowrap animate-[typeIn_2s_steps(40,end)_forwards]',
38
+ reveal: '',
39
+ glitch: 'relative',
40
+ flicker: 'animate-pulse',
41
+ pulse: 'animate-[pulse_2s_ease-in-out_infinite]',
42
+ };
43
+
44
+ export const AsciiArt = forwardRef<HTMLPreElement, AsciiArtProps>(
45
+ (
46
+ {
47
+ children,
48
+ size = 'md',
49
+ variant = 'default',
50
+ animation = 'none',
51
+ scanlines = false,
52
+ bordered = false,
53
+ title,
54
+ revealDelay = 100,
55
+ color,
56
+ className = '',
57
+ style,
58
+ ...props
59
+ },
60
+ ref
61
+ ) => {
62
+ const [revealedLines, setRevealedLines] = useState<number>(0);
63
+ const lines = children.split('\n');
64
+
65
+ useEffect(() => {
66
+ if (animation === 'reveal') {
67
+ setRevealedLines(0);
68
+ let lineIndex = 0;
69
+
70
+ const interval = setInterval(() => {
71
+ if (lineIndex < lines.length) {
72
+ setRevealedLines(lineIndex + 1);
73
+ lineIndex++;
74
+ } else {
75
+ clearInterval(interval);
76
+ }
77
+ }, revealDelay);
78
+
79
+ return () => clearInterval(interval);
80
+ }
81
+ }, [animation, lines.length, revealDelay, children]);
82
+
83
+ const renderContent = () => {
84
+ if (animation === 'reveal') {
85
+ return lines.map((line, i) => (
86
+ <span
87
+ key={i}
88
+ className="block transition-all duration-300 ease-out"
89
+ style={{
90
+ opacity: i < revealedLines ? 1 : 0,
91
+ transform: i < revealedLines ? 'translateX(0)' : 'translateX(-10px)',
92
+ }}
93
+ >
94
+ {line}
95
+ {'\n'}
96
+ </span>
97
+ ));
98
+ }
99
+ return children;
100
+ };
101
+
102
+ return (
103
+ <pre
104
+ ref={ref}
105
+ className={`font-mono whitespace-pre leading-tight overflow-x-auto ${sizeClasses[size]} ${variantClasses[variant]} ${animationClasses[animation]} ${
106
+ bordered ? 'border border-current p-4 relative' : ''
107
+ } ${scanlines ? 'relative after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,transparent,transparent_2px,rgba(0,0,0,0.1)_2px,rgba(0,0,0,0.1)_4px)] after:pointer-events-none' : ''
108
+ } ${className}`}
109
+ style={{ color, ...style }}
110
+ {...props}
111
+ >
112
+ {bordered && title && (
113
+ <span className="absolute -top-2.5 left-4 bg-inherit px-2 text-[0.8em]">
114
+ ┌{title}┐
115
+ </span>
116
+ )}
117
+ {renderContent()}
118
+ </pre>
119
+ );
120
+ }
121
+ );
122
+
123
+ AsciiArt.displayName = 'AsciiArt';
124
+ export default AsciiArt;
@@ -0,0 +1,142 @@
1
+ .container {
2
+ position: relative;
3
+ display: inline-block;
4
+ --drip-color: #ff0040;
5
+ }
6
+
7
+ .char {
8
+ display: inline-block;
9
+ position: relative;
10
+ }
11
+
12
+ /* Drip elements */
13
+ .drip {
14
+ position: absolute;
15
+ left: 50%;
16
+ top: 100%;
17
+ transform: translateX(-50%);
18
+ width: 4px;
19
+ height: 0;
20
+ background: var(--drip-color);
21
+ border-radius: 0 0 50% 50%;
22
+ animation: drip var(--drip-duration, 2s) ease-in infinite;
23
+ animation-delay: var(--drip-delay, 0s);
24
+ }
25
+
26
+ .drip::after {
27
+ content: '';
28
+ position: absolute;
29
+ bottom: 0;
30
+ left: 50%;
31
+ transform: translateX(-50%);
32
+ width: 8px;
33
+ height: 8px;
34
+ background: var(--drip-color);
35
+ border-radius: 50%;
36
+ opacity: 0;
37
+ animation: droplet var(--drip-duration, 2s) ease-in infinite;
38
+ animation-delay: var(--drip-delay, 0s);
39
+ }
40
+
41
+ @keyframes drip {
42
+ 0% { height: 0; opacity: 1; }
43
+ 20% { height: 20px; opacity: 1; }
44
+ 80% { height: 60px; opacity: 1; }
45
+ 100% { height: 80px; opacity: 0; }
46
+ }
47
+
48
+ @keyframes droplet {
49
+ 0%, 70% { opacity: 0; transform: translateX(-50%) scale(1); }
50
+ 75% { opacity: 1; transform: translateX(-50%) scale(1); }
51
+ 100% { opacity: 0; transform: translateX(-50%) scale(0) translateY(20px); }
52
+ }
53
+
54
+ /* Multiple drips per character */
55
+ .drip.secondary {
56
+ left: 30%;
57
+ animation-delay: calc(var(--drip-delay, 0s) + 0.5s);
58
+ }
59
+
60
+ .drip.tertiary {
61
+ left: 70%;
62
+ animation-delay: calc(var(--drip-delay, 0s) + 1s);
63
+ }
64
+
65
+ /* Intensity levels */
66
+ .light .drip {
67
+ width: 2px;
68
+ }
69
+
70
+ .light .drip::after {
71
+ width: 4px;
72
+ height: 4px;
73
+ }
74
+
75
+ .heavy .drip {
76
+ width: 6px;
77
+ }
78
+
79
+ .heavy .drip::after {
80
+ width: 12px;
81
+ height: 12px;
82
+ }
83
+
84
+ /* Color variants */
85
+ .blood { --drip-color: #ff0040; }
86
+ .acid { --drip-color: #aaff00; }
87
+ .cyber { --drip-color: #00ffff; }
88
+ .void { --drip-color: #0a0a0a; }
89
+ .chrome { --drip-color: linear-gradient(180deg, #888 0%, #fff 50%, #888 100%); }
90
+
91
+ /* Text glow matching drip */
92
+ .glowing .char {
93
+ text-shadow: 0 0 10px var(--drip-color), 0 0 20px var(--drip-color);
94
+ }
95
+
96
+ /* Melting effect */
97
+ .melting .char {
98
+ animation: melt 3s ease-in-out infinite;
99
+ animation-delay: var(--char-delay, 0s);
100
+ }
101
+
102
+ @keyframes melt {
103
+ 0%, 100% { transform: scaleY(1); transform-origin: top; }
104
+ 50% { transform: scaleY(1.1); transform-origin: top; }
105
+ }
106
+
107
+ /* Pooling effect at bottom */
108
+ .pool {
109
+ position: absolute;
110
+ bottom: -20px;
111
+ left: 0;
112
+ right: 0;
113
+ height: 10px;
114
+ background: var(--drip-color);
115
+ border-radius: 50%;
116
+ opacity: 0.5;
117
+ filter: blur(5px);
118
+ animation: poolGrow 3s ease-out forwards;
119
+ }
120
+
121
+ @keyframes poolGrow {
122
+ 0% { transform: scaleX(0); opacity: 0; }
123
+ 100% { transform: scaleX(1); opacity: 0.5; }
124
+ }
125
+
126
+ /* Hover pause */
127
+ .pauseOnHover:hover .drip {
128
+ animation-play-state: paused;
129
+ }
130
+
131
+ /* Static drip (no animation) */
132
+ .static .drip {
133
+ animation: none;
134
+ height: 40px;
135
+ opacity: 1;
136
+ }
137
+
138
+ .static .drip::after {
139
+ animation: none;
140
+ opacity: 1;
141
+ bottom: -4px;
142
+ }
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useMemo } from 'react';
4
+ import styles from './blood-drip.module.css';
5
+
6
+ export interface BloodDripProps extends HTMLAttributes<HTMLSpanElement> {
7
+ /** Text to display */
8
+ children: string;
9
+ /** Drip color variant */
10
+ variant?: 'blood' | 'acid' | 'cyber' | 'void' | 'chrome';
11
+ /** Drip intensity */
12
+ intensity?: 'light' | 'medium' | 'heavy';
13
+ /** Number of drips per character (1-3) */
14
+ dripsPerChar?: 1 | 2 | 3;
15
+ /** Base drip duration in seconds */
16
+ duration?: number;
17
+ /** Add text glow effect */
18
+ glowing?: boolean;
19
+ /** Add melting animation */
20
+ melting?: boolean;
21
+ /** Show pool at bottom */
22
+ showPool?: boolean;
23
+ /** Pause animation on hover */
24
+ pauseOnHover?: boolean;
25
+ /** Static drips (no animation) */
26
+ static?: boolean;
27
+ /** Custom drip color */
28
+ dripColor?: string;
29
+ /** Probability of drip per character (0-1) */
30
+ dripProbability?: number;
31
+ }
32
+
33
+ export const BloodDrip = forwardRef<HTMLSpanElement, BloodDripProps>(
34
+ (
35
+ {
36
+ children,
37
+ variant = 'blood',
38
+ intensity = 'medium',
39
+ dripsPerChar = 1,
40
+ duration = 2,
41
+ glowing = false,
42
+ melting = false,
43
+ showPool = false,
44
+ pauseOnHover = false,
45
+ static: isStatic = false,
46
+ dripColor,
47
+ dripProbability = 0.7,
48
+ className,
49
+ style,
50
+ ...props
51
+ },
52
+ ref
53
+ ) => {
54
+ const chars = children.split('');
55
+
56
+ const dripMap = useMemo(() => {
57
+ return chars.map(() => Math.random() < dripProbability);
58
+ }, [chars.length, dripProbability]);
59
+
60
+ const containerClasses = [
61
+ styles.container,
62
+ styles[variant],
63
+ styles[intensity],
64
+ glowing && styles.glowing,
65
+ melting && styles.melting,
66
+ pauseOnHover && styles.pauseOnHover,
67
+ isStatic && styles.static,
68
+ className
69
+ ].filter(Boolean).join(' ');
70
+
71
+ return (
72
+ <span
73
+ ref={ref}
74
+ className={containerClasses}
75
+ style={{
76
+ '--drip-color': dripColor,
77
+ ...style
78
+ } as React.CSSProperties}
79
+ {...props}
80
+ >
81
+ {chars.map((char, i) => {
82
+ const hasDrip = dripMap[i] && char !== ' ';
83
+ const delay = Math.random() * 2;
84
+
85
+ return (
86
+ <span
87
+ key={i}
88
+ className={styles.char}
89
+ style={{
90
+ '--char-delay': `${i * 0.1}s`,
91
+ '--drip-delay': `${delay}s`,
92
+ '--drip-duration': `${duration}s`
93
+ } as React.CSSProperties}
94
+ >
95
+ {char === ' ' ? '\u00A0' : char}
96
+ {hasDrip && (
97
+ <>
98
+ <span className={styles.drip} />
99
+ {dripsPerChar >= 2 && <span className={`${styles.drip} ${styles.secondary}`} />}
100
+ {dripsPerChar >= 3 && <span className={`${styles.drip} ${styles.tertiary}`} />}
101
+ </>
102
+ )}
103
+ </span>
104
+ );
105
+ })}
106
+ {showPool && <span className={styles.pool} />}
107
+ </span>
108
+ );
109
+ }
110
+ );
111
+
112
+ BloodDrip.displayName = 'BloodDrip';
113
+ export default BloodDrip;
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useMemo } from 'react';
4
+
5
+ export interface BloodDripProps extends HTMLAttributes<HTMLSpanElement> {
6
+ children: string;
7
+ variant?: 'blood' | 'acid' | 'cyber' | 'void' | 'chrome';
8
+ intensity?: 'light' | 'medium' | 'heavy';
9
+ dripsPerChar?: 1 | 2 | 3;
10
+ duration?: number;
11
+ glowing?: boolean;
12
+ melting?: boolean;
13
+ showPool?: boolean;
14
+ pauseOnHover?: boolean;
15
+ static?: boolean;
16
+ dripColor?: string;
17
+ dripProbability?: number;
18
+ }
19
+
20
+ const variantColors = {
21
+ blood: '#ff0040',
22
+ acid: '#aaff00',
23
+ cyber: '#00ffff',
24
+ void: '#0a0a0a',
25
+ chrome: '#888888',
26
+ };
27
+
28
+ const dripWidths = { light: 2, medium: 4, heavy: 6 };
29
+ const dropletSizes = { light: 4, medium: 8, heavy: 12 };
30
+
31
+ export const BloodDrip = forwardRef<HTMLSpanElement, BloodDripProps>(
32
+ (
33
+ {
34
+ children,
35
+ variant = 'blood',
36
+ intensity = 'medium',
37
+ dripsPerChar = 1,
38
+ duration = 2,
39
+ glowing = false,
40
+ melting = false,
41
+ showPool = false,
42
+ pauseOnHover = false,
43
+ static: isStatic = false,
44
+ dripColor,
45
+ dripProbability = 0.7,
46
+ className = '',
47
+ style,
48
+ ...props
49
+ },
50
+ ref
51
+ ) => {
52
+ const chars = children.split('');
53
+ const color = dripColor || variantColors[variant];
54
+ const dripWidth = dripWidths[intensity];
55
+ const dropletSize = dropletSizes[intensity];
56
+
57
+ const dripMap = useMemo(() => {
58
+ return chars.map(() => Math.random() < dripProbability);
59
+ }, [chars.length, dripProbability]);
60
+
61
+ const glowStyle = glowing ? { textShadow: `0 0 10px ${color}, 0 0 20px ${color}` } : {};
62
+
63
+ return (
64
+ <span
65
+ ref={ref}
66
+ className={`relative inline-block ${pauseOnHover ? 'hover:[&_.drip]:pause' : ''} ${className}`}
67
+ style={{ ...glowStyle, ...style }}
68
+ {...props}
69
+ >
70
+ {chars.map((char, i) => {
71
+ const hasDrip = dripMap[i] && char !== ' ';
72
+ const delay = Math.random() * 2;
73
+
74
+ return (
75
+ <span
76
+ key={i}
77
+ className={`inline-block relative ${melting ? 'animate-pulse' : ''}`}
78
+ >
79
+ {char === ' ' ? '\u00A0' : char}
80
+ {hasDrip && (
81
+ <span
82
+ className="drip absolute left-1/2 top-full -translate-x-1/2"
83
+ style={{
84
+ width: dripWidth,
85
+ height: isStatic ? 40 : 0,
86
+ background: color,
87
+ borderRadius: '0 0 50% 50%',
88
+ animation: isStatic ? 'none' : `drip ${duration}s ease-in infinite`,
89
+ animationDelay: `${delay}s`,
90
+ }}
91
+ >
92
+ <span
93
+ className="absolute bottom-0 left-1/2 -translate-x-1/2 rounded-full"
94
+ style={{
95
+ width: dropletSize,
96
+ height: dropletSize,
97
+ background: color,
98
+ opacity: isStatic ? 1 : 0,
99
+ animation: isStatic ? 'none' : `droplet ${duration}s ease-in infinite`,
100
+ animationDelay: `${delay}s`,
101
+ }}
102
+ />
103
+ </span>
104
+ )}
105
+ </span>
106
+ );
107
+ })}
108
+ {showPool && (
109
+ <span
110
+ className="absolute -bottom-5 left-0 right-0 h-2.5 rounded-full opacity-50 blur-sm"
111
+ style={{ background: color }}
112
+ />
113
+ )}
114
+ <style>{`
115
+ @keyframes drip {
116
+ 0% { height: 0; opacity: 1; }
117
+ 20% { height: 20px; opacity: 1; }
118
+ 80% { height: 60px; opacity: 1; }
119
+ 100% { height: 80px; opacity: 0; }
120
+ }
121
+ @keyframes droplet {
122
+ 0%, 70% { opacity: 0; transform: translateX(-50%) scale(1); }
123
+ 75% { opacity: 1; transform: translateX(-50%) scale(1); }
124
+ 100% { opacity: 0; transform: translateX(-50%) scale(0) translateY(20px); }
125
+ }
126
+ `}</style>
127
+ </span>
128
+ );
129
+ }
130
+ );
131
+
132
+ BloodDrip.displayName = 'BloodDrip';
133
+ export default BloodDrip;
@@ -0,0 +1,124 @@
1
+ .container {
2
+ display: inline-block;
3
+ position: relative;
4
+ }
5
+
6
+ .char {
7
+ display: inline-block;
8
+ position: relative;
9
+ transition: transform 0.1s ease-out;
10
+ }
11
+
12
+ .char.glitching {
13
+ animation: charGlitch 0.15s ease-in-out;
14
+ }
15
+
16
+ .char::before,
17
+ .char::after {
18
+ content: attr(data-char);
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ opacity: 0;
23
+ pointer-events: none;
24
+ }
25
+
26
+ .char::before {
27
+ color: #ff0040;
28
+ z-index: -1;
29
+ }
30
+
31
+ .char::after {
32
+ color: #00ffff;
33
+ z-index: -2;
34
+ }
35
+
36
+ .char.glitching::before {
37
+ animation: charLayer1 0.15s ease-in-out;
38
+ opacity: 0.8;
39
+ }
40
+
41
+ .char.glitching::after {
42
+ animation: charLayer2 0.15s ease-in-out;
43
+ opacity: 0.8;
44
+ }
45
+
46
+ /* Intensity levels */
47
+ .subtle .char.glitching {
48
+ animation-duration: 0.3s;
49
+ }
50
+
51
+ .intense .char.glitching {
52
+ animation-duration: 0.08s;
53
+ }
54
+
55
+ /* Variants */
56
+ .blood .char::before { color: #ff0040; }
57
+ .blood .char::after { color: #880020; }
58
+
59
+ .cyber .char::before { color: #00ffff; }
60
+ .cyber .char::after { color: #ff00ff; }
61
+
62
+ .matrix .char::before { color: #00ff00; }
63
+ .matrix .char::after { color: #008800; }
64
+
65
+ .corrupt .char::before { color: #ffffff; }
66
+ .corrupt .char::after { color: #000000; }
67
+
68
+ /* Hover mode */
69
+ .hover .char:hover {
70
+ animation: charGlitch 0.15s ease-in-out infinite;
71
+ }
72
+
73
+ .hover .char:hover::before,
74
+ .hover .char:hover::after {
75
+ opacity: 0.8;
76
+ }
77
+
78
+ .hover .char:hover::before {
79
+ animation: charLayer1 0.15s ease-in-out infinite;
80
+ }
81
+
82
+ .hover .char:hover::after {
83
+ animation: charLayer2 0.15s ease-in-out infinite;
84
+ }
85
+
86
+ @keyframes charGlitch {
87
+ 0%, 100% { transform: translate(0); }
88
+ 20% { transform: translate(-2px, 1px); }
89
+ 40% { transform: translate(2px, -1px); }
90
+ 60% { transform: translate(-1px, -1px); }
91
+ 80% { transform: translate(1px, 1px); }
92
+ }
93
+
94
+ @keyframes charLayer1 {
95
+ 0%, 100% { transform: translate(0); opacity: 0; }
96
+ 20% { transform: translate(-3px, 0); opacity: 0.8; }
97
+ 40% { transform: translate(3px, 0); opacity: 0.6; }
98
+ 60% { transform: translate(-2px, 0); opacity: 0.8; }
99
+ 80% { transform: translate(2px, 0); opacity: 0.6; }
100
+ }
101
+
102
+ @keyframes charLayer2 {
103
+ 0%, 100% { transform: translate(0); opacity: 0; }
104
+ 20% { transform: translate(3px, 0); opacity: 0.6; }
105
+ 40% { transform: translate(-3px, 0); opacity: 0.8; }
106
+ 60% { transform: translate(2px, 0); opacity: 0.6; }
107
+ 80% { transform: translate(-2px, 0); opacity: 0.8; }
108
+ }
109
+
110
+ /* Scramble effect */
111
+ .scramble .char {
112
+ font-family: monospace;
113
+ }
114
+
115
+ .scramble .char.scrambling {
116
+ color: transparent;
117
+ }
118
+
119
+ .scramble .char.scrambling::before {
120
+ color: inherit;
121
+ opacity: 1;
122
+ animation: none;
123
+ transform: none;
124
+ }