@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,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
+ }
@@ -0,0 +1,153 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState, useCallback, useRef } from 'react';
4
+ import styles from './char-glitch.module.css';
5
+
6
+ export interface CharGlitchProps extends HTMLAttributes<HTMLSpanElement> {
7
+ /** Text to display */
8
+ children: string;
9
+ /** Glitch intensity */
10
+ intensity?: 'subtle' | 'medium' | 'intense';
11
+ /** Visual variant */
12
+ variant?: 'blood' | 'cyber' | 'matrix' | 'corrupt';
13
+ /** Trigger mode */
14
+ mode?: 'random' | 'hover' | 'continuous' | 'wave';
15
+ /** Interval between random glitches in ms */
16
+ interval?: number;
17
+ /** Characters to use for scramble effect */
18
+ glitchChars?: string;
19
+ /** Enable scramble reveal effect */
20
+ scramble?: boolean;
21
+ }
22
+
23
+ const GLITCH_CHARS = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
24
+
25
+ export const CharGlitch = forwardRef<HTMLSpanElement, CharGlitchProps>(
26
+ (
27
+ {
28
+ children,
29
+ intensity = 'medium',
30
+ variant = 'blood',
31
+ mode = 'random',
32
+ interval = 100,
33
+ glitchChars = GLITCH_CHARS,
34
+ scramble = false,
35
+ className,
36
+ ...props
37
+ },
38
+ ref
39
+ ) => {
40
+ const [glitchingIndices, setGlitchingIndices] = useState<Set<number>>(new Set());
41
+ const [scrambledChars, setScrambledChars] = useState<string[]>([]);
42
+ const [revealed, setRevealed] = useState<boolean[]>([]);
43
+ const intervalRef = useRef<NodeJS.Timeout>();
44
+
45
+ const triggerGlitch = useCallback((index: number) => {
46
+ setGlitchingIndices(prev => new Set(prev).add(index));
47
+ setTimeout(() => {
48
+ setGlitchingIndices(prev => {
49
+ const next = new Set(prev);
50
+ next.delete(index);
51
+ return next;
52
+ });
53
+ }, intensity === 'subtle' ? 300 : intensity === 'intense' ? 80 : 150);
54
+ }, [intensity]);
55
+
56
+ useEffect(() => {
57
+ if (scramble) {
58
+ setScrambledChars(children.split('').map(() =>
59
+ glitchChars[Math.floor(Math.random() * glitchChars.length)]
60
+ ));
61
+ setRevealed(new Array(children.length).fill(false));
62
+
63
+ let index = 0;
64
+ const revealInterval = setInterval(() => {
65
+ if (index < children.length) {
66
+ setRevealed(prev => {
67
+ const next = [...prev];
68
+ next[index] = true;
69
+ return next;
70
+ });
71
+ index++;
72
+ } else {
73
+ clearInterval(revealInterval);
74
+ }
75
+ }, 50);
76
+
77
+ return () => clearInterval(revealInterval);
78
+ }
79
+ }, [children, scramble, glitchChars]);
80
+
81
+ useEffect(() => {
82
+ if (mode === 'random' && !scramble) {
83
+ intervalRef.current = setInterval(() => {
84
+ const randomIndex = Math.floor(Math.random() * children.length);
85
+ triggerGlitch(randomIndex);
86
+ }, interval);
87
+
88
+ return () => {
89
+ if (intervalRef.current) clearInterval(intervalRef.current);
90
+ };
91
+ }
92
+
93
+ if (mode === 'continuous' && !scramble) {
94
+ children.split('').forEach((_, i) => {
95
+ setTimeout(() => triggerGlitch(i), i * 50);
96
+ });
97
+
98
+ intervalRef.current = setInterval(() => {
99
+ children.split('').forEach((_, i) => {
100
+ setTimeout(() => triggerGlitch(i), i * 50);
101
+ });
102
+ }, interval * children.length);
103
+
104
+ return () => {
105
+ if (intervalRef.current) clearInterval(intervalRef.current);
106
+ };
107
+ }
108
+
109
+ if (mode === 'wave' && !scramble) {
110
+ let waveIndex = 0;
111
+ intervalRef.current = setInterval(() => {
112
+ triggerGlitch(waveIndex % children.length);
113
+ waveIndex++;
114
+ }, interval);
115
+
116
+ return () => {
117
+ if (intervalRef.current) clearInterval(intervalRef.current);
118
+ };
119
+ }
120
+ }, [mode, interval, children, triggerGlitch, scramble]);
121
+
122
+ const containerClasses = [
123
+ styles.container,
124
+ styles[intensity],
125
+ styles[variant],
126
+ mode === 'hover' && styles.hover,
127
+ scramble && styles.scramble,
128
+ className
129
+ ].filter(Boolean).join(' ');
130
+
131
+ return (
132
+ <span ref={ref} className={containerClasses} {...props}>
133
+ {children.split('').map((char, i) => {
134
+ const isGlitching = glitchingIndices.has(i);
135
+ const displayChar = scramble && !revealed[i] ? scrambledChars[i] : char;
136
+
137
+ return (
138
+ <span
139
+ key={i}
140
+ className={`${styles.char} ${isGlitching ? styles.glitching : ''} ${scramble && !revealed[i] ? styles.scrambling : ''}`}
141
+ data-char={displayChar}
142
+ >
143
+ {displayChar}
144
+ </span>
145
+ );
146
+ })}
147
+ </span>
148
+ );
149
+ }
150
+ );
151
+
152
+ CharGlitch.displayName = 'CharGlitch';
153
+ export default CharGlitch;
@@ -0,0 +1,126 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useEffect, useState, useCallback, useRef } from 'react';
4
+
5
+ export interface CharGlitchProps extends HTMLAttributes<HTMLSpanElement> {
6
+ children: string;
7
+ intensity?: 'subtle' | 'medium' | 'intense';
8
+ variant?: 'blood' | 'cyber' | 'matrix' | 'corrupt';
9
+ mode?: 'random' | 'hover' | 'continuous' | 'wave';
10
+ interval?: number;
11
+ glitchChars?: string;
12
+ scramble?: boolean;
13
+ }
14
+
15
+ const GLITCH_CHARS = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
16
+
17
+ const variantColors = {
18
+ blood: { primary: 'text-rose-500', shadow: 'drop-shadow-[0_0_2px_#ff0040]' },
19
+ cyber: { primary: 'text-cyan-400', shadow: 'drop-shadow-[0_0_2px_#00ffff]' },
20
+ matrix: { primary: 'text-green-400', shadow: 'drop-shadow-[0_0_2px_#00ff00]' },
21
+ corrupt: { primary: 'text-white', shadow: 'drop-shadow-[0_0_2px_#ffffff]' },
22
+ };
23
+
24
+ export const CharGlitch = forwardRef<HTMLSpanElement, CharGlitchProps>(
25
+ (
26
+ {
27
+ children,
28
+ intensity = 'medium',
29
+ variant = 'blood',
30
+ mode = 'random',
31
+ interval = 100,
32
+ glitchChars = GLITCH_CHARS,
33
+ scramble = false,
34
+ className = '',
35
+ ...props
36
+ },
37
+ ref
38
+ ) => {
39
+ const [glitchingIndices, setGlitchingIndices] = useState<Set<number>>(new Set());
40
+ const [scrambledChars, setScrambledChars] = useState<string[]>([]);
41
+ const [revealed, setRevealed] = useState<boolean[]>([]);
42
+ const intervalRef = useRef<NodeJS.Timeout>();
43
+
44
+ const durations = { subtle: 300, medium: 150, intense: 80 };
45
+
46
+ const triggerGlitch = useCallback((index: number) => {
47
+ setGlitchingIndices(prev => new Set(prev).add(index));
48
+ setTimeout(() => {
49
+ setGlitchingIndices(prev => {
50
+ const next = new Set(prev);
51
+ next.delete(index);
52
+ return next;
53
+ });
54
+ }, durations[intensity]);
55
+ }, [intensity]);
56
+
57
+ useEffect(() => {
58
+ if (scramble) {
59
+ setScrambledChars(children.split('').map(() =>
60
+ glitchChars[Math.floor(Math.random() * glitchChars.length)]
61
+ ));
62
+ setRevealed(new Array(children.length).fill(false));
63
+
64
+ let index = 0;
65
+ const revealInterval = setInterval(() => {
66
+ if (index < children.length) {
67
+ setRevealed(prev => {
68
+ const next = [...prev];
69
+ next[index] = true;
70
+ return next;
71
+ });
72
+ index++;
73
+ } else {
74
+ clearInterval(revealInterval);
75
+ }
76
+ }, 50);
77
+
78
+ return () => clearInterval(revealInterval);
79
+ }
80
+ }, [children, scramble, glitchChars]);
81
+
82
+ useEffect(() => {
83
+ if (mode === 'random' && !scramble) {
84
+ intervalRef.current = setInterval(() => {
85
+ const randomIndex = Math.floor(Math.random() * children.length);
86
+ triggerGlitch(randomIndex);
87
+ }, interval);
88
+ return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
89
+ }
90
+
91
+ if (mode === 'wave' && !scramble) {
92
+ let waveIndex = 0;
93
+ intervalRef.current = setInterval(() => {
94
+ triggerGlitch(waveIndex % children.length);
95
+ waveIndex++;
96
+ }, interval);
97
+ return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
98
+ }
99
+ }, [mode, interval, children, triggerGlitch, scramble]);
100
+
101
+ return (
102
+ <span ref={ref} className={`inline-block ${className}`} {...props}>
103
+ {children.split('').map((char, i) => {
104
+ const isGlitching = glitchingIndices.has(i);
105
+ const displayChar = scramble && !revealed[i] ? scrambledChars[i] : char;
106
+ const { primary, shadow } = variantColors[variant];
107
+
108
+ return (
109
+ <span
110
+ key={i}
111
+ className={`inline-block relative transition-transform duration-100 ${
112
+ isGlitching ? `${primary} ${shadow} animate-pulse scale-110` : ''
113
+ } ${mode === 'hover' ? 'hover:animate-pulse hover:scale-110' : ''}`}
114
+ data-char={displayChar}
115
+ >
116
+ {displayChar}
117
+ </span>
118
+ );
119
+ })}
120
+ </span>
121
+ );
122
+ }
123
+ );
124
+
125
+ CharGlitch.displayName = 'CharGlitch';
126
+ export default CharGlitch;
@@ -0,0 +1,179 @@
1
+ .container {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ gap: 1rem;
6
+ font-family: 'Syne', 'Inter', sans-serif;
7
+ font-weight: 800;
8
+ }
9
+
10
+ .block {
11
+ display: flex;
12
+ flex-direction: column;
13
+ align-items: center;
14
+ position: relative;
15
+ }
16
+
17
+ .value {
18
+ font-size: clamp(3rem, 12vw, 8rem);
19
+ line-height: 1;
20
+ letter-spacing: -0.02em;
21
+ position: relative;
22
+ background: linear-gradient(180deg, #fafafa 0%, #888 100%);
23
+ -webkit-background-clip: text;
24
+ -webkit-text-fill-color: transparent;
25
+ background-clip: text;
26
+ }
27
+
28
+ /* 3D shadow effect */
29
+ .value::before {
30
+ content: attr(data-value);
31
+ position: absolute;
32
+ left: 3px;
33
+ top: 3px;
34
+ z-index: -1;
35
+ background: linear-gradient(180deg, var(--accent-color, #ff0040) 0%, transparent 100%);
36
+ -webkit-background-clip: text;
37
+ -webkit-text-fill-color: transparent;
38
+ background-clip: text;
39
+ opacity: 0.5;
40
+ }
41
+
42
+ .label {
43
+ font-size: 0.6rem;
44
+ letter-spacing: 0.3em;
45
+ color: #444;
46
+ margin-top: 0.5rem;
47
+ text-transform: uppercase;
48
+ font-weight: 400;
49
+ }
50
+
51
+ .separator {
52
+ font-size: clamp(2rem, 8vw, 5rem);
53
+ font-weight: 800;
54
+ color: #333;
55
+ animation: pulseSep 1s ease-in-out infinite;
56
+ align-self: flex-start;
57
+ margin-top: 0.5rem;
58
+ }
59
+
60
+ @keyframes pulseSep {
61
+ 0%, 100% { opacity: 1; }
62
+ 50% { opacity: 0.2; }
63
+ }
64
+
65
+ /* Size variants */
66
+ .sm .value { font-size: clamp(1.5rem, 6vw, 3rem); }
67
+ .sm .separator { font-size: clamp(1rem, 4vw, 2rem); }
68
+ .sm .label { font-size: 0.5rem; }
69
+
70
+ .lg .value { font-size: clamp(5rem, 18vw, 12rem); }
71
+ .lg .separator { font-size: clamp(3rem, 10vw, 7rem); }
72
+ .lg .label { font-size: 0.7rem; }
73
+
74
+ /* Variant: minimal */
75
+ .minimal .value {
76
+ background: none;
77
+ -webkit-text-fill-color: currentColor;
78
+ color: #fafafa;
79
+ }
80
+
81
+ .minimal .value::before {
82
+ display: none;
83
+ }
84
+
85
+ /* Variant: neon */
86
+ .neon .value {
87
+ background: none;
88
+ -webkit-text-fill-color: currentColor;
89
+ color: var(--accent-color, #ff0040);
90
+ text-shadow:
91
+ 0 0 10px var(--accent-color, #ff0040),
92
+ 0 0 20px var(--accent-color, #ff0040),
93
+ 0 0 40px var(--accent-color, #ff0040);
94
+ }
95
+
96
+ .neon .value::before {
97
+ display: none;
98
+ }
99
+
100
+ .neon .separator {
101
+ color: var(--accent-color, #ff0040);
102
+ text-shadow: 0 0 10px var(--accent-color, #ff0040);
103
+ }
104
+
105
+ /* Variant: brutal */
106
+ .brutal .value {
107
+ background: var(--accent-color, #ff0040);
108
+ -webkit-text-fill-color: #0a0a0a;
109
+ padding: 0 0.25em;
110
+ }
111
+
112
+ .brutal .value::before {
113
+ display: none;
114
+ }
115
+
116
+ /* Variant: glitch */
117
+ .glitch .value::after {
118
+ content: attr(data-value);
119
+ position: absolute;
120
+ left: 0;
121
+ top: 0;
122
+ z-index: -2;
123
+ background: linear-gradient(180deg, #00ffff 0%, transparent 100%);
124
+ -webkit-background-clip: text;
125
+ -webkit-text-fill-color: transparent;
126
+ background-clip: text;
127
+ opacity: 0;
128
+ animation: glitchOffset 3s infinite;
129
+ }
130
+
131
+ @keyframes glitchOffset {
132
+ 0%, 90%, 100% { opacity: 0; transform: translate(0); }
133
+ 92% { opacity: 0.8; transform: translate(-3px, 0); }
134
+ 94% { opacity: 0.8; transform: translate(3px, 0); }
135
+ }
136
+
137
+ /* Flip animation on change */
138
+ .flip .value {
139
+ perspective: 500px;
140
+ }
141
+
142
+ .flip .value.changing {
143
+ animation: flipValue 0.3s ease-in-out;
144
+ }
145
+
146
+ @keyframes flipValue {
147
+ 0% { transform: rotateX(0deg); }
148
+ 50% { transform: rotateX(-90deg); }
149
+ 100% { transform: rotateX(0deg); }
150
+ }
151
+
152
+ /* Urgency states */
153
+ .urgent .value {
154
+ animation: urgentPulse 0.5s ease-in-out infinite;
155
+ }
156
+
157
+ @keyframes urgentPulse {
158
+ 0%, 100% { transform: scale(1); }
159
+ 50% { transform: scale(1.02); }
160
+ }
161
+
162
+ .urgent .separator {
163
+ color: var(--accent-color, #ff0040);
164
+ animation: pulseSep 0.3s ease-in-out infinite;
165
+ }
166
+
167
+ /* Hide labels option */
168
+ .hideLabels .label {
169
+ display: none;
170
+ }
171
+
172
+ /* Compact mode */
173
+ .compact {
174
+ gap: 0.5rem;
175
+ }
176
+
177
+ .compact .separator {
178
+ margin: 0 0.25rem;
179
+ }