@oalacea/chaosui 0.4.0 → 0.5.1

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 (178) hide show
  1. package/buttons/cta-brutal/css/cta-brutal.module.css +175 -0
  2. package/buttons/cta-brutal/css/index.tsx +78 -0
  3. package/buttons/cta-brutal/tailwind/index.tsx +85 -0
  4. package/buttons/dead-button/css/dead-button.module.css +216 -0
  5. package/buttons/dead-button/css/index.tsx +47 -0
  6. package/buttons/dead-button/tailwind/index.tsx +90 -0
  7. package/buttons/deeper-button/css/deeper-button.module.css +141 -0
  8. package/buttons/deeper-button/css/index.tsx +62 -0
  9. package/buttons/deeper-button/tailwind/index.tsx +78 -0
  10. package/buttons/dual-choice/css/dual-choice.module.css +176 -0
  11. package/buttons/dual-choice/css/index.tsx +56 -0
  12. package/buttons/dual-choice/tailwind/index.tsx +89 -0
  13. package/buttons/tension-bar/css/index.tsx +96 -0
  14. package/buttons/tension-bar/css/tension-bar.module.css +204 -0
  15. package/buttons/tension-bar/tailwind/index.tsx +122 -0
  16. package/decorative/inscription/css/index.tsx +66 -0
  17. package/decorative/inscription/css/inscription.module.css +160 -0
  18. package/decorative/inscription/tailwind/index.tsx +91 -0
  19. package/decorative/ornaments/css/index.tsx +136 -0
  20. package/decorative/ornaments/css/ornaments.module.css +144 -0
  21. package/decorative/ornaments/tailwind/index.tsx +122 -0
  22. package/decorative/rune-symbols/css/index.tsx +116 -0
  23. package/decorative/rune-symbols/css/rune-symbols.module.css +116 -0
  24. package/decorative/rune-symbols/tailwind/index.tsx +108 -0
  25. package/decorative/sheet-music/css/index.tsx +144 -0
  26. package/decorative/sheet-music/css/sheet-music.module.css +152 -0
  27. package/decorative/sheet-music/tailwind/index.tsx +111 -0
  28. package/layout/horizontal-scroll/css/horizontal-scroll.module.css +93 -0
  29. package/layout/horizontal-scroll/css/index.tsx +114 -0
  30. package/layout/horizontal-scroll/tailwind/index.tsx +108 -0
  31. package/layout/spec-grid/css/index.tsx +103 -0
  32. package/layout/spec-grid/css/spec-grid.module.css +125 -0
  33. package/layout/spec-grid/tailwind/index.tsx +91 -0
  34. package/layout/tower-pricing/css/index.tsx +91 -0
  35. package/layout/tower-pricing/css/tower-pricing.module.css +177 -0
  36. package/layout/tower-pricing/tailwind/index.tsx +106 -0
  37. package/layout/tracklist/css/index.tsx +96 -0
  38. package/layout/tracklist/css/tracklist.module.css +141 -0
  39. package/layout/tracklist/tailwind/index.tsx +92 -0
  40. package/layout/void-frame/css/index.tsx +60 -0
  41. package/layout/void-frame/css/void-frame.module.css +141 -0
  42. package/layout/void-frame/tailwind/index.tsx +64 -0
  43. package/navigation/brutal-nav/css/brutal-nav.module.css +178 -0
  44. package/navigation/brutal-nav/css/index.tsx +80 -0
  45. package/navigation/brutal-nav/tailwind/index.tsx +95 -0
  46. package/navigation/progress-dots/css/index.tsx +60 -0
  47. package/navigation/progress-dots/css/progress-dots.module.css +152 -0
  48. package/navigation/progress-dots/tailwind/index.tsx +105 -0
  49. package/navigation/scattered-nav/css/index.tsx +59 -0
  50. package/navigation/scattered-nav/css/scattered-nav.module.css +121 -0
  51. package/navigation/scattered-nav/tailwind/index.tsx +77 -0
  52. package/navigation/scroll-indicator/css/index.tsx +72 -0
  53. package/navigation/scroll-indicator/css/scroll-indicator.module.css +129 -0
  54. package/navigation/scroll-indicator/tailwind/index.tsx +107 -0
  55. package/navigation/vertical-nav/css/index.tsx +69 -0
  56. package/navigation/vertical-nav/css/vertical-nav.module.css +117 -0
  57. package/navigation/vertical-nav/tailwind/index.tsx +91 -0
  58. package/package.json +8 -33
  59. package/text/ascii-art/css/ascii-art.module.css +173 -0
  60. package/text/ascii-art/css/index.tsx +116 -0
  61. package/text/ascii-art/tailwind/index.tsx +124 -0
  62. package/text/blood-drip/css/blood-drip.module.css +142 -0
  63. package/text/blood-drip/css/index.tsx +113 -0
  64. package/text/blood-drip/tailwind/index.tsx +133 -0
  65. package/text/char-glitch/css/char-glitch.module.css +124 -0
  66. package/text/char-glitch/css/index.tsx +153 -0
  67. package/text/char-glitch/tailwind/index.tsx +126 -0
  68. package/text/countdown-display/css/countdown-display.module.css +179 -0
  69. package/text/countdown-display/css/index.tsx +190 -0
  70. package/text/countdown-display/tailwind/index.tsx +155 -0
  71. package/text/giant-layers/css/giant-layers.module.css +156 -0
  72. package/text/giant-layers/css/index.tsx +97 -0
  73. package/text/giant-layers/tailwind/index.tsx +111 -0
  74. package/text/reveal-text/css/index.tsx +180 -0
  75. package/text/reveal-text/css/reveal-text.module.css +129 -0
  76. package/text/reveal-text/tailwind/index.tsx +135 -0
  77. package/text/rotate-text/css/index.tsx +139 -0
  78. package/text/rotate-text/css/rotate-text.module.css +162 -0
  79. package/text/rotate-text/tailwind/index.tsx +127 -0
  80. package/text/strike-reveal/css/index.tsx +124 -0
  81. package/text/strike-reveal/css/strike-reveal.module.css +139 -0
  82. package/text/strike-reveal/tailwind/index.tsx +138 -0
  83. package/text/terminal-output/css/index.tsx +179 -0
  84. package/text/terminal-output/css/terminal-output.module.css +203 -0
  85. package/text/terminal-output/tailwind/index.tsx +174 -0
  86. package/text/typing-text/css/index.tsx +115 -0
  87. package/text/typing-text/css/typing-text.module.css +84 -0
  88. package/text/typing-text/tailwind/index.tsx +126 -0
  89. package/bin/cli.js +0 -278
  90. package/components/backgrounds/light-beams/index.tsx +0 -80
  91. package/components/backgrounds/light-beams/light-beams.module.css +0 -27
  92. package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +0 -55
  93. package/components/navigation/hexagon-menu/css/index.tsx +0 -35
  94. package/components/navigation/hexagon-menu/tailwind/index.tsx +0 -53
  95. /package/{components/backgrounds → backgrounds}/glow-orbs/glow-orbs.module.css +0 -0
  96. /package/{components/backgrounds → backgrounds}/glow-orbs/index.tsx +0 -0
  97. /package/{components/backgrounds → backgrounds}/noise-canvas/index.tsx +0 -0
  98. /package/{components/backgrounds → backgrounds}/noise-canvas/noise-canvas.module.css +0 -0
  99. /package/{components/backgrounds → backgrounds}/particle-field/index.tsx +0 -0
  100. /package/{components/backgrounds → backgrounds}/particle-field/particle-field.module.css +0 -0
  101. /package/{components/buttons → buttons}/chaos-button/chaos-button.module.css +0 -0
  102. /package/{components/buttons → buttons}/chaos-button/index.tsx +0 -0
  103. /package/{components/buttons → buttons}/glitch-button/glitch-button.module.css +0 -0
  104. /package/{components/buttons → buttons}/glitch-button/index.tsx +0 -0
  105. /package/{components/chaos-vars.css → chaos-vars.css} +0 -0
  106. /package/{components/cyber → cyber}/cyber-avatar/css/cyber-avatar.module.css +0 -0
  107. /package/{components/cyber → cyber}/cyber-avatar/css/index.tsx +0 -0
  108. /package/{components/cyber → cyber}/cyber-avatar/tailwind/index.tsx +0 -0
  109. /package/{components/cyber → cyber}/cyber-input/css/cyber-input.module.css +0 -0
  110. /package/{components/cyber → cyber}/cyber-input/css/index.tsx +0 -0
  111. /package/{components/cyber → cyber}/cyber-input/tailwind/index.tsx +0 -0
  112. /package/{components/cyber → cyber}/cyber-loader/css/cyber-loader.module.css +0 -0
  113. /package/{components/cyber → cyber}/cyber-loader/css/index.tsx +0 -0
  114. /package/{components/cyber → cyber}/cyber-loader/tailwind/index.tsx +0 -0
  115. /package/{components/cyber → cyber}/cyber-modal/css/cyber-modal.module.css +0 -0
  116. /package/{components/cyber → cyber}/cyber-modal/css/index.tsx +0 -0
  117. /package/{components/cyber → cyber}/cyber-modal/tailwind/index.tsx +0 -0
  118. /package/{components/cyber → cyber}/cyber-slider/css/cyber-slider.module.css +0 -0
  119. /package/{components/cyber → cyber}/cyber-slider/css/index.tsx +0 -0
  120. /package/{components/cyber → cyber}/cyber-slider/tailwind/index.tsx +0 -0
  121. /package/{components/cyber → cyber}/cyber-tooltip/css/cyber-tooltip.module.css +0 -0
  122. /package/{components/cyber → cyber}/cyber-tooltip/css/index.tsx +0 -0
  123. /package/{components/cyber → cyber}/cyber-tooltip/tailwind/index.tsx +0 -0
  124. /package/{components/effects → effects}/cursor-follower/cursor-follower.module.css +0 -0
  125. /package/{components/effects → effects}/cursor-follower/index.tsx +0 -0
  126. /package/{components/effects → effects}/glitch-image/css/glitch-image.module.css +0 -0
  127. /package/{components/effects → effects}/glitch-image/css/index.tsx +0 -0
  128. /package/{components/effects → effects}/glitch-image/tailwind/index.tsx +0 -0
  129. /package/{components/effects → effects}/glowing-border/css/glowing-border.module.css +0 -0
  130. /package/{components/effects → effects}/glowing-border/css/index.tsx +0 -0
  131. /package/{components/effects → effects}/glowing-border/tailwind/index.tsx +0 -0
  132. /package/{components/effects → effects}/screen-distortion/index.tsx +0 -0
  133. /package/{components/effects → effects}/screen-distortion/screen-distortion.module.css +0 -0
  134. /package/{components/effects → effects}/warning-tape/index.tsx +0 -0
  135. /package/{components/effects → effects}/warning-tape/warning-tape.module.css +0 -0
  136. /package/{components/layout → layout}/data-grid/css/data-grid.module.css +0 -0
  137. /package/{components/layout → layout}/data-grid/css/index.tsx +0 -0
  138. /package/{components/layout → layout}/data-grid/tailwind/index.tsx +0 -0
  139. /package/{components/layout → layout}/hologram-card/css/hologram-card.module.css +0 -0
  140. /package/{components/layout → layout}/hologram-card/css/index.tsx +0 -0
  141. /package/{components/layout → layout}/hologram-card/tailwind/index.tsx +0 -0
  142. /package/{components/neon → neon}/neon-alert/css/index.tsx +0 -0
  143. /package/{components/neon → neon}/neon-alert/css/neon-alert.module.css +0 -0
  144. /package/{components/neon → neon}/neon-alert/tailwind/index.tsx +0 -0
  145. /package/{components/neon → neon}/neon-badge/css/index.tsx +0 -0
  146. /package/{components/neon → neon}/neon-badge/css/neon-badge.module.css +0 -0
  147. /package/{components/neon → neon}/neon-badge/tailwind/index.tsx +0 -0
  148. /package/{components/neon → neon}/neon-button/css/index.tsx +0 -0
  149. /package/{components/neon → neon}/neon-button/css/neon-button.module.css +0 -0
  150. /package/{components/neon → neon}/neon-button/tailwind/index.tsx +0 -0
  151. /package/{components/neon → neon}/neon-divider/css/index.tsx +0 -0
  152. /package/{components/neon → neon}/neon-divider/css/neon-divider.module.css +0 -0
  153. /package/{components/neon → neon}/neon-divider/tailwind/index.tsx +0 -0
  154. /package/{components/neon → neon}/neon-progress/css/index.tsx +0 -0
  155. /package/{components/neon → neon}/neon-progress/css/neon-progress.module.css +0 -0
  156. /package/{components/neon → neon}/neon-progress/tailwind/index.tsx +0 -0
  157. /package/{components/neon → neon}/neon-tabs/css/index.tsx +0 -0
  158. /package/{components/neon → neon}/neon-tabs/css/neon-tabs.module.css +0 -0
  159. /package/{components/neon → neon}/neon-tabs/tailwind/index.tsx +0 -0
  160. /package/{components/neon → neon}/neon-toggle/css/index.tsx +0 -0
  161. /package/{components/neon → neon}/neon-toggle/css/neon-toggle.module.css +0 -0
  162. /package/{components/neon → neon}/neon-toggle/tailwind/index.tsx +0 -0
  163. /package/{components/overlays → overlays}/noise-overlay/index.tsx +0 -0
  164. /package/{components/overlays → overlays}/noise-overlay/noise-overlay.module.css +0 -0
  165. /package/{components/overlays → overlays}/scanlines/index.tsx +0 -0
  166. /package/{components/overlays → overlays}/scanlines/scanlines.module.css +0 -0
  167. /package/{components/overlays → overlays}/static-flicker/index.tsx +0 -0
  168. /package/{components/overlays → overlays}/static-flicker/static-flicker.module.css +0 -0
  169. /package/{components/overlays → overlays}/vignette/index.tsx +0 -0
  170. /package/{components/overlays → overlays}/vignette/vignette.module.css +0 -0
  171. /package/{components/text → text}/distortion-text/distortion-text.module.css +0 -0
  172. /package/{components/text → text}/distortion-text/index.tsx +0 -0
  173. /package/{components/text → text}/falling-text/falling-text.module.css +0 -0
  174. /package/{components/text → text}/falling-text/index.tsx +0 -0
  175. /package/{components/text → text}/flicker-text/flicker-text.module.css +0 -0
  176. /package/{components/text → text}/flicker-text/index.tsx +0 -0
  177. /package/{components/text → text}/glitch-text/glitch-text.module.css +0 -0
  178. /package/{components/text → text}/glitch-text/index.tsx +0 -0
@@ -0,0 +1,173 @@
1
+ .container {
2
+ display: block;
3
+ font-family: 'Courier New', 'Monaco', 'Consolas', monospace;
4
+ white-space: pre;
5
+ line-height: 1.2;
6
+ letter-spacing: 0;
7
+ overflow-x: auto;
8
+ }
9
+
10
+ /* Size variants */
11
+ .xs { font-size: 0.5rem; }
12
+ .sm { font-size: 0.65rem; }
13
+ .md { font-size: 0.8rem; }
14
+ .lg { font-size: 1rem; }
15
+ .xl { font-size: 1.25rem; }
16
+
17
+ /* Color variants */
18
+ .blood {
19
+ color: #ff0040;
20
+ text-shadow: 0 0 5px #ff004080;
21
+ }
22
+
23
+ .cyber {
24
+ color: #00ffff;
25
+ text-shadow: 0 0 5px #00ffff80;
26
+ }
27
+
28
+ .matrix {
29
+ color: #00ff00;
30
+ text-shadow: 0 0 5px #00ff0080;
31
+ }
32
+
33
+ .amber {
34
+ color: #ffaa00;
35
+ text-shadow: 0 0 5px #ffaa0080;
36
+ }
37
+
38
+ .ghost {
39
+ color: #666;
40
+ opacity: 0.7;
41
+ }
42
+
43
+ .gradient {
44
+ background: linear-gradient(180deg, #ff0040 0%, #00ffff 50%, #00ff00 100%);
45
+ -webkit-background-clip: text;
46
+ -webkit-text-fill-color: transparent;
47
+ background-clip: text;
48
+ }
49
+
50
+ /* Animation variants */
51
+ .typing {
52
+ overflow: hidden;
53
+ white-space: nowrap;
54
+ animation: typeIn 2s steps(40, end) forwards;
55
+ }
56
+
57
+ @keyframes typeIn {
58
+ from { max-width: 0; }
59
+ to { max-width: 100%; }
60
+ }
61
+
62
+ .reveal .line {
63
+ opacity: 0;
64
+ animation: lineReveal 0.5s forwards;
65
+ }
66
+
67
+ @keyframes lineReveal {
68
+ from { opacity: 0; transform: translateX(-10px); }
69
+ to { opacity: 1; transform: translateX(0); }
70
+ }
71
+
72
+ .glitch {
73
+ position: relative;
74
+ }
75
+
76
+ .glitch::before,
77
+ .glitch::after {
78
+ content: attr(data-text);
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ width: 100%;
83
+ height: 100%;
84
+ pointer-events: none;
85
+ }
86
+
87
+ .glitch::before {
88
+ color: #ff0040;
89
+ animation: asciiGlitch1 3s infinite;
90
+ clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%);
91
+ }
92
+
93
+ .glitch::after {
94
+ color: #00ffff;
95
+ animation: asciiGlitch2 3s infinite;
96
+ clip-path: polygon(0 55%, 100% 55%, 100% 100%, 0 100%);
97
+ }
98
+
99
+ @keyframes asciiGlitch1 {
100
+ 0%, 90%, 100% { transform: translate(0); opacity: 0; }
101
+ 92% { transform: translate(-2px, 0); opacity: 0.8; }
102
+ 94% { transform: translate(2px, 0); opacity: 0.8; }
103
+ }
104
+
105
+ @keyframes asciiGlitch2 {
106
+ 0%, 90%, 100% { transform: translate(0); opacity: 0; }
107
+ 93% { transform: translate(2px, 0); opacity: 0.8; }
108
+ 95% { transform: translate(-2px, 0); opacity: 0.8; }
109
+ }
110
+
111
+ /* Scanline effect */
112
+ .scanlines {
113
+ position: relative;
114
+ }
115
+
116
+ .scanlines::after {
117
+ content: '';
118
+ position: absolute;
119
+ top: 0;
120
+ left: 0;
121
+ right: 0;
122
+ bottom: 0;
123
+ background: repeating-linear-gradient(
124
+ 0deg,
125
+ transparent,
126
+ transparent 2px,
127
+ rgba(0, 0, 0, 0.1) 2px,
128
+ rgba(0, 0, 0, 0.1) 4px
129
+ );
130
+ pointer-events: none;
131
+ }
132
+
133
+ /* Border styles */
134
+ .bordered {
135
+ border: 1px solid currentColor;
136
+ padding: 1rem;
137
+ position: relative;
138
+ }
139
+
140
+ .bordered::before {
141
+ content: '┌' attr(data-title) '┐';
142
+ position: absolute;
143
+ top: -0.6em;
144
+ left: 1rem;
145
+ background: inherit;
146
+ padding: 0 0.5rem;
147
+ font-size: 0.8em;
148
+ }
149
+
150
+ /* Flicker effect */
151
+ .flicker {
152
+ animation: asciiFlicker 0.15s infinite;
153
+ }
154
+
155
+ @keyframes asciiFlicker {
156
+ 0%, 100% { opacity: 1; }
157
+ 50% { opacity: 0.8; }
158
+ }
159
+
160
+ /* Pulse effect */
161
+ .pulse {
162
+ animation: asciiPulse 2s ease-in-out infinite;
163
+ }
164
+
165
+ @keyframes asciiPulse {
166
+ 0%, 100% { opacity: 0.6; }
167
+ 50% { opacity: 1; }
168
+ }
169
+
170
+ /* Line container for reveal animation */
171
+ .line {
172
+ display: block;
173
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState } from 'react';
4
+ import styles from './ascii-art.module.css';
5
+
6
+ export interface AsciiArtProps extends HTMLAttributes<HTMLPreElement> {
7
+ /** ASCII art content (multi-line string) */
8
+ children: string;
9
+ /** Size variant */
10
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+ /** Color variant */
12
+ variant?: 'default' | 'blood' | 'cyber' | 'matrix' | 'amber' | 'ghost' | 'gradient';
13
+ /** Animation effect */
14
+ animation?: 'none' | 'typing' | 'reveal' | 'glitch' | 'flicker' | 'pulse';
15
+ /** Show scanlines overlay */
16
+ scanlines?: boolean;
17
+ /** Show border */
18
+ bordered?: boolean;
19
+ /** Border title */
20
+ title?: string;
21
+ /** Delay between lines for reveal animation (ms) */
22
+ revealDelay?: number;
23
+ /** Custom color */
24
+ color?: string;
25
+ }
26
+
27
+ export const AsciiArt = forwardRef<HTMLPreElement, AsciiArtProps>(
28
+ (
29
+ {
30
+ children,
31
+ size = 'md',
32
+ variant = 'default',
33
+ animation = 'none',
34
+ scanlines = false,
35
+ bordered = false,
36
+ title,
37
+ revealDelay = 100,
38
+ color,
39
+ className,
40
+ style,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const [revealedLines, setRevealedLines] = useState<number>(0);
46
+ const lines = children.split('\n');
47
+
48
+ useEffect(() => {
49
+ if (animation === 'reveal') {
50
+ setRevealedLines(0);
51
+ let lineIndex = 0;
52
+
53
+ const interval = setInterval(() => {
54
+ if (lineIndex < lines.length) {
55
+ setRevealedLines(lineIndex + 1);
56
+ lineIndex++;
57
+ } else {
58
+ clearInterval(interval);
59
+ }
60
+ }, revealDelay);
61
+
62
+ return () => clearInterval(interval);
63
+ }
64
+ }, [animation, lines.length, revealDelay, children]);
65
+
66
+ const containerClasses = [
67
+ styles.container,
68
+ styles[size],
69
+ variant !== 'default' && styles[variant],
70
+ animation !== 'none' && styles[animation],
71
+ scanlines && styles.scanlines,
72
+ bordered && styles.bordered,
73
+ className
74
+ ].filter(Boolean).join(' ');
75
+
76
+ const renderContent = () => {
77
+ if (animation === 'reveal') {
78
+ return lines.map((line, i) => (
79
+ <span
80
+ key={i}
81
+ className={styles.line}
82
+ style={{
83
+ opacity: i < revealedLines ? 1 : 0,
84
+ animationDelay: `${i * revealDelay}ms`,
85
+ transform: i < revealedLines ? 'translateX(0)' : 'translateX(-10px)',
86
+ transition: 'opacity 0.3s ease, transform 0.3s ease',
87
+ }}
88
+ >
89
+ {line}
90
+ {'\n'}
91
+ </span>
92
+ ));
93
+ }
94
+ return children;
95
+ };
96
+
97
+ return (
98
+ <pre
99
+ ref={ref}
100
+ className={containerClasses}
101
+ style={{
102
+ color: color,
103
+ ...style
104
+ }}
105
+ data-text={animation === 'glitch' ? children : undefined}
106
+ data-title={bordered ? title : undefined}
107
+ {...props}
108
+ >
109
+ {renderContent()}
110
+ </pre>
111
+ );
112
+ }
113
+ );
114
+
115
+ AsciiArt.displayName = 'AsciiArt';
116
+ export default AsciiArt;
@@ -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;