@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,108 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes } from 'react';
4
+
5
+ export const RUNES = {
6
+ fehu: 'ᚠ', uruz: 'ᚢ', thurisaz: 'ᚦ', ansuz: 'ᚨ', raidho: 'ᚱ', kenaz: 'ᚲ',
7
+ gebo: 'ᚷ', wunjo: 'ᚹ', hagalaz: 'ᚺ', nauthiz: 'ᚾ', isaz: 'ᛁ', jera: 'ᛃ',
8
+ eihwaz: 'ᛇ', perthro: 'ᛈ', algiz: 'ᛉ', sowilo: 'ᛊ', tiwaz: 'ᛏ', berkano: 'ᛒ',
9
+ ehwaz: 'ᛖ', mannaz: 'ᛗ', laguz: 'ᛚ', ingwaz: 'ᛝ', dagaz: 'ᛞ', othala: 'ᛟ',
10
+ };
11
+
12
+ export interface RuneSymbolsProps extends HTMLAttributes<HTMLDivElement> {
13
+ runes?: (keyof typeof RUNES | string)[];
14
+ count?: number;
15
+ variant?: 'gold' | 'blood' | 'bone' | 'iron' | 'cyan';
16
+ animation?: 'glow' | 'floating' | 'pulsing' | 'flickering' | 'none';
17
+ direction?: 'horizontal' | 'vertical';
18
+ size?: 'sm' | 'md' | 'lg' | 'xl';
19
+ scattered?: boolean;
20
+ }
21
+
22
+ const variantStyles = {
23
+ gold: 'text-amber-600',
24
+ blood: 'text-red-800',
25
+ bone: 'text-stone-400',
26
+ iron: 'text-zinc-500',
27
+ cyan: 'text-cyan-400',
28
+ };
29
+
30
+ const sizeClasses = {
31
+ sm: 'text-base gap-3',
32
+ md: 'text-3xl gap-6',
33
+ lg: 'text-5xl gap-8',
34
+ xl: 'text-6xl gap-12',
35
+ };
36
+
37
+ const getRandomRunes = (count: number): string[] => {
38
+ const runeValues = Object.values(RUNES);
39
+ return Array.from({ length: count }, () => runeValues[Math.floor(Math.random() * runeValues.length)]);
40
+ };
41
+
42
+ export const RuneSymbols = forwardRef<HTMLDivElement, RuneSymbolsProps>(
43
+ ({ runes, count = 6, variant = 'gold', animation = 'glow', direction = 'horizontal', size = 'md', scattered = false, className = '', style, ...props }, ref) => {
44
+ const resolvedRunes = runes ? runes.map(r => RUNES[r as keyof typeof RUNES] || r) : getRandomRunes(count);
45
+
46
+ const animationClass = {
47
+ glow: 'animate-[runeGlow_4s_ease-in-out_infinite]',
48
+ floating: 'animate-[runeFloat_6s_ease-in-out_infinite]',
49
+ pulsing: 'animate-[runePulse_2s_ease-in-out_infinite]',
50
+ flickering: 'animate-[runeFlicker_0.5s_steps(2)_infinite]',
51
+ none: '',
52
+ }[animation];
53
+
54
+ return (
55
+ <div
56
+ ref={ref}
57
+ className={`
58
+ flex ${sizeClasses[size]}
59
+ ${direction === 'vertical' ? 'flex-col' : ''}
60
+ ${scattered ? 'relative w-full h-full' : ''}
61
+ ${className}
62
+ `}
63
+ style={style}
64
+ {...props}
65
+ >
66
+ <style>{`
67
+ @keyframes runeGlow {
68
+ 0%, 100% { opacity: 0.4; text-shadow: none; }
69
+ 50% { opacity: 0.8; text-shadow: 0 0 15px currentColor; }
70
+ }
71
+ @keyframes runeFloat {
72
+ 0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.4; }
73
+ 25% { transform: translateY(-10px) rotate(5deg); opacity: 0.7; }
74
+ 50% { transform: translateY(0) rotate(0deg); opacity: 0.5; }
75
+ 75% { transform: translateY(10px) rotate(-5deg); opacity: 0.7; }
76
+ }
77
+ @keyframes runePulse {
78
+ 0%, 100% { transform: scale(1); opacity: 0.5; }
79
+ 50% { transform: scale(1.15); opacity: 1; text-shadow: 0 0 25px currentColor; }
80
+ }
81
+ @keyframes runeFlicker {
82
+ 0% { opacity: 0.3; } 25% { opacity: 0.8; } 50% { opacity: 0.5; } 75% { opacity: 0.9; } 100% { opacity: 0.3; }
83
+ }
84
+ `}</style>
85
+ {resolvedRunes.map((rune, i) => (
86
+ <span
87
+ key={i}
88
+ className={`
89
+ ${variantStyles[variant]} opacity-60 transition-all duration-400
90
+ hover:opacity-100 hover:scale-110 hover:[text-shadow:0_0_20px_currentColor]
91
+ ${animationClass}
92
+ ${scattered ? 'absolute' : ''}
93
+ `}
94
+ style={{
95
+ animationDelay: `${i * 0.5}s`,
96
+ ...(scattered ? { left: `${Math.random() * 80 + 10}%`, top: `${Math.random() * 80 + 10}%` } : {}),
97
+ }}
98
+ >
99
+ {rune}
100
+ </span>
101
+ ))}
102
+ </div>
103
+ );
104
+ }
105
+ );
106
+
107
+ RuneSymbols.displayName = 'RuneSymbols';
108
+ export default RuneSymbols;
@@ -0,0 +1,144 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useMemo } from 'react';
4
+ import styles from './sheet-music.module.css';
5
+
6
+ export const MUSIC_SYMBOLS = {
7
+ quarterNote: '♩',
8
+ eighthNote: '♪',
9
+ beamedNotes: '♫',
10
+ sixteenthNotes: '♬',
11
+ trebleClef: '𝄞',
12
+ bassClef: '𝄢',
13
+ sharp: '♯',
14
+ flat: '♭',
15
+ natural: '♮',
16
+ fermata: '𝄐',
17
+ coda: '𝄌',
18
+ segno: '𝄋',
19
+ };
20
+
21
+ export interface NoteConfig {
22
+ symbol: keyof typeof MUSIC_SYMBOLS | string;
23
+ position: { top: string; left: string };
24
+ size?: 'sm' | 'md' | 'lg' | 'xl';
25
+ delay?: number;
26
+ }
27
+
28
+ export interface SheetMusicProps extends HTMLAttributes<HTMLDivElement> {
29
+ /** Positioning mode */
30
+ mode?: 'overlay' | 'inline';
31
+ /** Animation style */
32
+ animation?: 'drift' | 'falling' | 'swirling' | 'rising' | 'none';
33
+ /** Color variant */
34
+ variant?: 'ash' | 'silver' | 'gold' | 'blood' | 'ivory';
35
+ /** Note density */
36
+ density?: 'sparse' | 'normal' | 'dense';
37
+ /** Number of random notes */
38
+ count?: number;
39
+ /** Custom note configurations */
40
+ notes?: NoteConfig[];
41
+ /** Show decorative staff lines */
42
+ showStaff?: boolean;
43
+ /** Symbols to use for random generation */
44
+ symbols?: (keyof typeof MUSIC_SYMBOLS)[];
45
+ }
46
+
47
+ const generateRandomNotes = (
48
+ count: number,
49
+ symbols: (keyof typeof MUSIC_SYMBOLS)[]
50
+ ): NoteConfig[] => {
51
+ const sizes: ('sm' | 'md' | 'lg' | 'xl')[] = ['sm', 'md', 'lg', 'xl'];
52
+ return Array.from({ length: count }, (_, i) => ({
53
+ symbol: symbols[Math.floor(Math.random() * symbols.length)],
54
+ position: {
55
+ top: `${Math.random() * 80 + 10}%`,
56
+ left: `${Math.random() * 90 + 5}%`,
57
+ },
58
+ size: sizes[Math.floor(Math.random() * sizes.length)],
59
+ delay: i * (Math.random() * 3 + 1),
60
+ }));
61
+ };
62
+
63
+ const sizeClasses = {
64
+ sm: styles.note4,
65
+ md: styles.note1,
66
+ lg: styles.note2,
67
+ xl: styles.note5,
68
+ };
69
+
70
+ export const SheetMusic = forwardRef<HTMLDivElement, SheetMusicProps>(
71
+ (
72
+ {
73
+ mode = 'overlay',
74
+ animation = 'drift',
75
+ variant = 'ash',
76
+ density = 'normal',
77
+ count = 8,
78
+ notes: customNotes,
79
+ showStaff = false,
80
+ symbols = ['quarterNote', 'eighthNote', 'beamedNotes', 'sixteenthNotes', 'trebleClef', 'bassClef'],
81
+ className,
82
+ ...props
83
+ },
84
+ ref
85
+ ) => {
86
+ const notes = useMemo(() =>
87
+ customNotes || generateRandomNotes(count, symbols),
88
+ [customNotes, count, symbols]
89
+ );
90
+
91
+ const animationClass = {
92
+ drift: '',
93
+ falling: styles.falling,
94
+ swirling: styles.swirling,
95
+ rising: styles.rising,
96
+ none: '',
97
+ }[animation];
98
+
99
+ return (
100
+ <div
101
+ ref={ref}
102
+ className={`
103
+ ${styles.container}
104
+ ${mode === 'inline' ? styles.inline : ''}
105
+ ${styles[variant]}
106
+ ${styles[density]}
107
+ ${animationClass}
108
+ ${className || ''}
109
+ `}
110
+ {...props}
111
+ >
112
+ {showStaff && (
113
+ <>
114
+ <div className={`${styles.staff} ${styles.staffTop}`}>
115
+ {[...Array(5)].map((_, i) => <div key={i} className={styles.staffLine} />)}
116
+ </div>
117
+ <div className={`${styles.staff} ${styles.staffBottom}`}>
118
+ {[...Array(5)].map((_, i) => <div key={i} className={styles.staffLine} />)}
119
+ </div>
120
+ </>
121
+ )}
122
+
123
+ {notes.map((note, i) => {
124
+ const symbol = MUSIC_SYMBOLS[note.symbol as keyof typeof MUSIC_SYMBOLS] || note.symbol;
125
+ return (
126
+ <span
127
+ key={i}
128
+ className={`${styles.note} ${sizeClasses[note.size || 'md']}`}
129
+ style={{
130
+ ...note.position,
131
+ animationDelay: `${note.delay || i * 2}s`,
132
+ }}
133
+ >
134
+ {symbol}
135
+ </span>
136
+ );
137
+ })}
138
+ </div>
139
+ );
140
+ }
141
+ );
142
+
143
+ SheetMusic.displayName = 'SheetMusic';
144
+ export default SheetMusic;
@@ -0,0 +1,152 @@
1
+ .container {
2
+ --note-color: #2a2a2a;
3
+
4
+ position: fixed;
5
+ inset: 0;
6
+ pointer-events: none;
7
+ overflow: hidden;
8
+ z-index: 1;
9
+ }
10
+
11
+ .inline {
12
+ position: relative;
13
+ width: 100%;
14
+ height: 100%;
15
+ }
16
+
17
+ .note {
18
+ position: absolute;
19
+ color: var(--note-color);
20
+ opacity: 0.3;
21
+ animation: noteDrift 20s linear infinite;
22
+ }
23
+
24
+ /* Note variations */
25
+ .note1 { font-size: 1.5rem; }
26
+ .note2 { font-size: 2rem; }
27
+ .note3 { font-size: 1.8rem; }
28
+ .note4 { font-size: 1.2rem; }
29
+ .note5 { font-size: 3rem; }
30
+ .note6 { font-size: 2.5rem; }
31
+
32
+ @keyframes noteDrift {
33
+ 0%, 100% {
34
+ transform: translateY(0) rotate(0deg);
35
+ opacity: 0.3;
36
+ }
37
+ 25% {
38
+ transform: translateY(-20px) rotate(5deg);
39
+ opacity: 0.5;
40
+ }
41
+ 50% {
42
+ transform: translateY(0) rotate(0deg);
43
+ opacity: 0.3;
44
+ }
45
+ 75% {
46
+ transform: translateY(20px) rotate(-5deg);
47
+ opacity: 0.5;
48
+ }
49
+ }
50
+
51
+ /* Falling animation */
52
+ .falling .note {
53
+ animation: noteFall 15s linear infinite;
54
+ }
55
+
56
+ @keyframes noteFall {
57
+ 0% {
58
+ transform: translateY(-100%) rotate(0deg);
59
+ opacity: 0;
60
+ }
61
+ 10% {
62
+ opacity: 0.4;
63
+ }
64
+ 90% {
65
+ opacity: 0.4;
66
+ }
67
+ 100% {
68
+ transform: translateY(100vh) rotate(360deg);
69
+ opacity: 0;
70
+ }
71
+ }
72
+
73
+ /* Swirling animation */
74
+ .swirling .note {
75
+ animation: noteSwirl 25s ease-in-out infinite;
76
+ }
77
+
78
+ @keyframes noteSwirl {
79
+ 0%, 100% {
80
+ transform: translate(0, 0) rotate(0deg) scale(1);
81
+ opacity: 0.2;
82
+ }
83
+ 25% {
84
+ transform: translate(30px, -40px) rotate(90deg) scale(1.1);
85
+ opacity: 0.5;
86
+ }
87
+ 50% {
88
+ transform: translate(0, 0) rotate(180deg) scale(1);
89
+ opacity: 0.3;
90
+ }
91
+ 75% {
92
+ transform: translate(-30px, 40px) rotate(270deg) scale(0.9);
93
+ opacity: 0.5;
94
+ }
95
+ }
96
+
97
+ /* Rising animation */
98
+ .rising .note {
99
+ animation: noteRise 20s ease-in-out infinite;
100
+ }
101
+
102
+ @keyframes noteRise {
103
+ 0% {
104
+ transform: translateY(100vh) scale(0.5);
105
+ opacity: 0;
106
+ }
107
+ 20% {
108
+ opacity: 0.4;
109
+ }
110
+ 80% {
111
+ opacity: 0.4;
112
+ }
113
+ 100% {
114
+ transform: translateY(-100%) scale(1.5);
115
+ opacity: 0;
116
+ }
117
+ }
118
+
119
+ /* Staff lines */
120
+ .staff {
121
+ position: absolute;
122
+ width: 100%;
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: 8px;
126
+ opacity: 0.1;
127
+ }
128
+
129
+ .staffLine {
130
+ height: 1px;
131
+ background: var(--note-color);
132
+ }
133
+
134
+ .staffTop {
135
+ top: 15%;
136
+ }
137
+
138
+ .staffBottom {
139
+ bottom: 15%;
140
+ }
141
+
142
+ /* Variants */
143
+ .ash { --note-color: #2a2a2a; }
144
+ .silver { --note-color: #888888; }
145
+ .gold { --note-color: #c9a227; }
146
+ .blood { --note-color: #6b1010; }
147
+ .ivory { --note-color: #e8e8e8; }
148
+
149
+ /* Density */
150
+ .sparse .note { animation-duration: 30s; }
151
+ .normal .note { animation-duration: 20s; }
152
+ .dense .note { animation-duration: 12s; }
@@ -0,0 +1,111 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, HTMLAttributes, useMemo } from 'react';
4
+
5
+ export const MUSIC_SYMBOLS = {
6
+ quarterNote: '♩', eighthNote: '♪', beamedNotes: '♫', sixteenthNotes: '♬',
7
+ trebleClef: '𝄞', bassClef: '𝄢', sharp: '♯', flat: '♭', natural: '♮',
8
+ fermata: '𝄐', coda: '𝄌', segno: '𝄋',
9
+ };
10
+
11
+ export interface NoteConfig {
12
+ symbol: keyof typeof MUSIC_SYMBOLS | string;
13
+ position: { top: string; left: string };
14
+ size?: 'sm' | 'md' | 'lg' | 'xl';
15
+ delay?: number;
16
+ }
17
+
18
+ export interface SheetMusicProps extends HTMLAttributes<HTMLDivElement> {
19
+ mode?: 'overlay' | 'inline';
20
+ animation?: 'drift' | 'falling' | 'swirling' | 'rising' | 'none';
21
+ variant?: 'ash' | 'silver' | 'gold' | 'blood' | 'ivory';
22
+ density?: 'sparse' | 'normal' | 'dense';
23
+ count?: number;
24
+ notes?: NoteConfig[];
25
+ showStaff?: boolean;
26
+ symbols?: (keyof typeof MUSIC_SYMBOLS)[];
27
+ }
28
+
29
+ const variantColors = {
30
+ ash: 'text-zinc-800',
31
+ silver: 'text-gray-400',
32
+ gold: 'text-amber-600',
33
+ blood: 'text-red-900',
34
+ ivory: 'text-stone-200',
35
+ };
36
+
37
+ const sizeClasses = { sm: 'text-lg', md: 'text-2xl', lg: 'text-3xl', xl: 'text-5xl' };
38
+ const densityDurations = { sparse: '30s', normal: '20s', dense: '12s' };
39
+
40
+ const generateRandomNotes = (count: number, symbols: (keyof typeof MUSIC_SYMBOLS)[]): NoteConfig[] => {
41
+ const sizes: ('sm' | 'md' | 'lg' | 'xl')[] = ['sm', 'md', 'lg', 'xl'];
42
+ return Array.from({ length: count }, (_, i) => ({
43
+ symbol: symbols[Math.floor(Math.random() * symbols.length)],
44
+ position: { top: `${Math.random() * 80 + 10}%`, left: `${Math.random() * 90 + 5}%` },
45
+ size: sizes[Math.floor(Math.random() * sizes.length)],
46
+ delay: i * (Math.random() * 3 + 1),
47
+ }));
48
+ };
49
+
50
+ export const SheetMusic = forwardRef<HTMLDivElement, SheetMusicProps>(
51
+ ({ mode = 'overlay', animation = 'drift', variant = 'ash', density = 'normal', count = 8, notes: customNotes, showStaff = false, symbols = ['quarterNote', 'eighthNote', 'beamedNotes', 'sixteenthNotes', 'trebleClef', 'bassClef'], className = '', ...props }, ref) => {
52
+ const notes = useMemo(() => customNotes || generateRandomNotes(count, symbols), [customNotes, count, symbols]);
53
+ const duration = densityDurations[density];
54
+
55
+ const animationKeyframes = {
56
+ drift: `@keyframes noteDrift { 0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.3; } 25% { transform: translateY(-20px) rotate(5deg); opacity: 0.5; } 50% { transform: translateY(0) rotate(0deg); opacity: 0.3; } 75% { transform: translateY(20px) rotate(-5deg); opacity: 0.5; } }`,
57
+ falling: `@keyframes noteFall { 0% { transform: translateY(-100%) rotate(0deg); opacity: 0; } 10%, 90% { opacity: 0.4; } 100% { transform: translateY(100vh) rotate(360deg); opacity: 0; } }`,
58
+ swirling: `@keyframes noteSwirl { 0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); opacity: 0.2; } 25% { transform: translate(30px, -40px) rotate(90deg) scale(1.1); opacity: 0.5; } 50% { transform: translate(0, 0) rotate(180deg) scale(1); opacity: 0.3; } 75% { transform: translate(-30px, 40px) rotate(270deg) scale(0.9); opacity: 0.5; } }`,
59
+ rising: `@keyframes noteRise { 0% { transform: translateY(100vh) scale(0.5); opacity: 0; } 20%, 80% { opacity: 0.4; } 100% { transform: translateY(-100%) scale(1.5); opacity: 0; } }`,
60
+ none: '',
61
+ };
62
+
63
+ const animationNames = { drift: 'noteDrift', falling: 'noteFall', swirling: 'noteSwirl', rising: 'noteRise', none: '' };
64
+
65
+ return (
66
+ <div
67
+ ref={ref}
68
+ className={`
69
+ pointer-events-none overflow-hidden z-[1]
70
+ ${mode === 'overlay' ? 'fixed inset-0' : 'relative w-full h-full'}
71
+ ${variantColors[variant]}
72
+ ${className}
73
+ `}
74
+ {...props}
75
+ >
76
+ <style>{animationKeyframes[animation]}</style>
77
+
78
+ {showStaff && (
79
+ <>
80
+ <div className="absolute w-full top-[15%] flex flex-col gap-2 opacity-10">
81
+ {[...Array(5)].map((_, i) => <div key={i} className="h-px bg-current" />)}
82
+ </div>
83
+ <div className="absolute w-full bottom-[15%] flex flex-col gap-2 opacity-10">
84
+ {[...Array(5)].map((_, i) => <div key={i} className="h-px bg-current" />)}
85
+ </div>
86
+ </>
87
+ )}
88
+
89
+ {notes.map((note, i) => {
90
+ const symbol = MUSIC_SYMBOLS[note.symbol as keyof typeof MUSIC_SYMBOLS] || note.symbol;
91
+ return (
92
+ <span
93
+ key={i}
94
+ className={`absolute opacity-30 ${sizeClasses[note.size || 'md']}`}
95
+ style={{
96
+ ...note.position,
97
+ animation: animation !== 'none' ? `${animationNames[animation]} ${duration} linear infinite` : undefined,
98
+ animationDelay: `${note.delay || i * 2}s`,
99
+ }}
100
+ >
101
+ {symbol}
102
+ </span>
103
+ );
104
+ })}
105
+ </div>
106
+ );
107
+ }
108
+ );
109
+
110
+ SheetMusic.displayName = 'SheetMusic';
111
+ export default SheetMusic;
@@ -0,0 +1,93 @@
1
+ .container {
2
+ --scroll-accent: #00f0ff;
3
+ --scroll-bg: rgba(0, 0, 0, 0.3);
4
+ --panel-gap: 2rem;
5
+
6
+ width: 100%;
7
+ overflow-x: auto;
8
+ overflow-y: hidden;
9
+ scroll-snap-type: x mandatory;
10
+ scroll-behavior: smooth;
11
+ -webkit-overflow-scrolling: touch;
12
+ scrollbar-width: thin;
13
+ scrollbar-color: var(--scroll-accent) var(--scroll-bg);
14
+ }
15
+
16
+ .container::-webkit-scrollbar {
17
+ height: 4px;
18
+ }
19
+
20
+ .container::-webkit-scrollbar-track {
21
+ background: var(--scroll-bg);
22
+ }
23
+
24
+ .container::-webkit-scrollbar-thumb {
25
+ background: var(--scroll-accent);
26
+ border-radius: 2px;
27
+ }
28
+
29
+ .track {
30
+ display: flex;
31
+ gap: var(--panel-gap);
32
+ padding: 1rem 0;
33
+ width: max-content;
34
+ }
35
+
36
+ .panel {
37
+ flex: 0 0 auto;
38
+ scroll-snap-align: start;
39
+ scroll-snap-stop: normal;
40
+ }
41
+
42
+ /* Variants */
43
+ .cyan { --scroll-accent: #00f0ff; }
44
+ .green { --scroll-accent: #00ff88; }
45
+ .amber { --scroll-accent: #ffaa00; }
46
+ .blood { --scroll-accent: #8b1a1a; }
47
+
48
+ /* Panel sizes */
49
+ .panelFull {
50
+ width: 100vw;
51
+ }
52
+
53
+ .panelLarge {
54
+ width: 80vw;
55
+ }
56
+
57
+ .panelMedium {
58
+ width: 60vw;
59
+ }
60
+
61
+ .panelSmall {
62
+ width: 40vw;
63
+ }
64
+
65
+ /* Fade edges */
66
+ .fadeEdges {
67
+ mask-image: linear-gradient(90deg, transparent 0%, black 5%, black 95%, transparent 100%);
68
+ -webkit-mask-image: linear-gradient(90deg, transparent 0%, black 5%, black 95%, transparent 100%);
69
+ }
70
+
71
+ /* Indicators */
72
+ .indicators {
73
+ display: flex;
74
+ justify-content: center;
75
+ gap: 0.5rem;
76
+ margin-top: 1rem;
77
+ }
78
+
79
+ .indicator {
80
+ width: 8px;
81
+ height: 8px;
82
+ border-radius: 50%;
83
+ background: var(--scroll-bg);
84
+ border: 1px solid var(--scroll-accent);
85
+ cursor: pointer;
86
+ transition: all 0.3s ease;
87
+ }
88
+
89
+ .indicator:hover,
90
+ .indicatorActive {
91
+ background: var(--scroll-accent);
92
+ box-shadow: 0 0 10px var(--scroll-accent);
93
+ }