1+ 'use client' ;
2+
3+ import { useState } from 'react' ;
4+
5+ export default function FlareButton ( {
6+ text = '✨ Flare Button' ,
7+ onClick,
8+ disabled = false ,
9+ variant = 'primary'
10+ } ) {
11+ const [ isHovered , setIsHovered ] = useState ( false ) ;
12+ const [ isActive , setIsActive ] = useState ( false ) ;
13+ const [ showFlare , setShowFlare ] = useState ( false ) ;
14+
15+ const handleClick = ( ) => {
16+ if ( ! disabled ) {
17+ setShowFlare ( true ) ;
18+ if ( onClick ) {
19+ onClick ( ) ;
20+ }
21+ setTimeout ( ( ) => {
22+ setShowFlare ( false ) ;
23+ } , 1500 ) ;
24+ }
25+ } ;
26+
27+ // Variant configurations
28+ const variants = {
29+ primary : {
30+ gradient : 'from-indigo-500 via-purple-500 to-purple-600' ,
31+ hoverGradient : 'hover:from-indigo-600 hover:via-purple-600 hover:to-purple-700' ,
32+ flareColors : [ '#667eea' , '#764ba2' , '#a855f7' , '#c084fc' , '#e9d5ff' ]
33+ } ,
34+ secondary : {
35+ gradient : 'from-pink-400 via-pink-500 to-red-500' ,
36+ hoverGradient : 'hover:from-pink-500 hover:via-pink-600 hover:to-red-600' ,
37+ flareColors : [ '#f093fb' , '#f5576c' , '#ff6b9d' , '#fda4af' , '#fbcfe8' ]
38+ } ,
39+ gradient : {
40+ gradient : 'from-pink-400 via-yellow-300 to-yellow-400' ,
41+ hoverGradient : 'hover:from-pink-500 hover:via-yellow-400 hover:to-yellow-500' ,
42+ flareColors : [ '#fa709a' , '#fee140' , '#fbbf24' , '#fde047' , '#fef3c7' ]
43+ }
44+ } ;
45+
46+ const currentVariant = variants [ variant ] ;
47+
48+ // Generate flare particles (shooting stars effect)
49+ const flareParticles = Array . from ( { length : 12 } , ( _ , i ) => {
50+ const angle = ( i * 30 ) - 15 ;
51+ const distance = 80 + Math . random ( ) * 120 ;
52+ const size = Math . random ( ) * 8 + 4 ;
53+
54+ return {
55+ id : i ,
56+ angle,
57+ distance,
58+ size,
59+ color : currentVariant . flareColors [ i % currentVariant . flareColors . length ] ,
60+ delay : Math . random ( ) * 0.1 ,
61+ duration : 0.8 + Math . random ( ) * 0.4
62+ } ;
63+ } ) ;
64+
65+ // Generate sparkles around button
66+ const sparkles = Array . from ( { length : 8 } , ( _ , i ) => {
67+ const angle = ( i * 45 ) ;
68+ return {
69+ id : i ,
70+ angle,
71+ color : currentVariant . flareColors [ i % currentVariant . flareColors . length ] ,
72+ delay : i * 0.05
73+ } ;
74+ } ) ;
75+
76+ return (
77+ < div className = "relative inline-block" >
78+ < button
79+ onClick = { handleClick }
80+ onMouseEnter = { ( ) => ! disabled && setIsHovered ( true ) }
81+ onMouseLeave = { ( ) => {
82+ setIsHovered ( false ) ;
83+ setIsActive ( false ) ;
84+ } }
85+ onMouseDown = { ( ) => ! disabled && setIsActive ( true ) }
86+ onMouseUp = { ( ) => setIsActive ( false ) }
87+ disabled = { disabled }
88+ className = { `
89+ relative px-12 py-4 font-semibold text-white uppercase tracking-wider
90+ rounded-full border-none cursor-pointer overflow-visible
91+ transition-all duration-400 ease-out
92+ focus:outline-none focus:ring-4 focus:ring-purple-300
93+ bg-gradient-to-r ${ currentVariant . gradient } ${ currentVariant . hoverGradient }
94+ ${ isHovered && ! disabled ? '-translate-y-1' : 'translate-y-0' }
95+ ${ isActive && ! disabled ? 'scale-95' : isHovered && ! disabled ? 'scale-105' : 'scale-100' }
96+ ${ disabled ? 'opacity-50 cursor-not-allowed' : '' }
97+ ` }
98+ style = { {
99+ fontFamily : '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif'
100+ } }
101+ aria-label = { text }
102+ tabIndex = { 0 }
103+ >
104+ { /* Button Content */ }
105+ < span className = "relative z-10 flex items-center justify-center gap-3" >
106+ < span
107+ className = { `
108+ inline-block transition-transform duration-300
109+ ${ isHovered && ! disabled ? '-translate-x-1' : 'translate-x-0' }
110+ ` }
111+ >
112+ { text }
113+ </ span >
114+ < span
115+ className = { `
116+ inline-flex items-center justify-center transition-all duration-300
117+ ${ isHovered && ! disabled ? 'opacity-100 translate-x-0' : 'opacity-0 -translate-x-2' }
118+ ` }
119+ >
120+ < svg width = "20" height = "20" viewBox = "0 0 20 20" fill = "none" >
121+ < path
122+ d = "M7.5 15L12.5 10L7.5 5"
123+ stroke = "currentColor"
124+ strokeWidth = "2"
125+ strokeLinecap = "round"
126+ strokeLinejoin = "round"
127+ />
128+ </ svg >
129+ </ span >
130+ </ span >
131+
132+ { /* Ripple Effect */ }
133+ { isActive && ! disabled && (
134+ < div
135+ className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-0 h-0 rounded-full bg-white/50 pointer-events-none animate-ripple"
136+ />
137+ ) }
138+ </ button >
139+
140+ { /* FLARE EFFECT - Shooting Particles */ }
141+ { showFlare && (
142+ < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" >
143+ { flareParticles . map ( particle => (
144+ < div
145+ key = { particle . id }
146+ className = "absolute animate-flare-shoot"
147+ style = { {
148+ left : '0' ,
149+ top : '0' ,
150+ width : `${ particle . size } px` ,
151+ height : `${ particle . size } px` ,
152+ backgroundColor : particle . color ,
153+ borderRadius : '50%' ,
154+ filter : `blur(${ particle . size * 0.3 } px)` ,
155+ '--flare-angle' : `${ particle . angle } deg` ,
156+ '--flare-distance' : `${ particle . distance } px` ,
157+ animationDelay : `${ particle . delay } s` ,
158+ animationDuration : `${ particle . duration } s`
159+ } }
160+ />
161+ ) ) }
162+ </ div >
163+ ) }
164+
165+ { /* FLARE EFFECT - Expanding Ring */ }
166+ { showFlare && (
167+ < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" >
168+ < div className = "animate-flare-ring"
169+ style = { {
170+ border : `4px solid ${ currentVariant . flareColors [ 0 ] } ` ,
171+ borderRadius : '50%' ,
172+ opacity : 0 ,
173+ filter : 'blur(2px)'
174+ } }
175+ />
176+ </ div >
177+ ) }
178+
179+ { /* FLARE EFFECT - Sparkles */ }
180+ { showFlare && (
181+ < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" >
182+ { sparkles . map ( sparkle => (
183+ < div
184+ key = { sparkle . id }
185+ className = "absolute animate-sparkle"
186+ style = { {
187+ left : '0' ,
188+ top : '0' ,
189+ width : '4px' ,
190+ height : '16px' ,
191+ backgroundColor : sparkle . color ,
192+ borderRadius : '50%' ,
193+ filter : 'blur(1px)' ,
194+ '--sparkle-angle' : `${ sparkle . angle } deg` ,
195+ animationDelay : `${ sparkle . delay } s`
196+ } }
197+ />
198+ ) ) }
199+ </ div >
200+ ) }
201+
202+ { /* FLARE EFFECT - Center Burst */ }
203+ { showFlare && (
204+ < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" >
205+ < div
206+ className = "animate-burst"
207+ style = { {
208+ width : '30px' ,
209+ height : '30px' ,
210+ background : `radial-gradient(circle, ${ currentVariant . flareColors [ 0 ] } , ${ currentVariant . flareColors [ 2 ] } , transparent)` ,
211+ borderRadius : '50%' ,
212+ filter : 'blur(6px)'
213+ } }
214+ />
215+ </ div >
216+ ) }
217+
218+ < style jsx > { `
219+ @keyframes ripple {
220+ 0% {
221+ width: 0;
222+ height: 0;
223+ opacity: 0.5;
224+ }
225+ 100% {
226+ width: 200px;
227+ height: 200px;
228+ opacity: 0;
229+ }
230+ }
231+
232+ @keyframes flare-shoot {
233+ 0% {
234+ transform: rotate(var(--flare-angle)) translateX(0) scale(1);
235+ opacity: 1;
236+ }
237+ 70% {
238+ opacity: 1;
239+ }
240+ 100% {
241+ transform: rotate(var(--flare-angle)) translateX(var(--flare-distance)) scale(0.3);
242+ opacity: 0;
243+ }
244+ }
245+
246+ @keyframes flare-ring {
247+ 0% {
248+ width: 0;
249+ height: 0;
250+ opacity: 1;
251+ }
252+ 100% {
253+ width: 300px;
254+ height: 300px;
255+ opacity: 0;
256+ }
257+ }
258+
259+ @keyframes sparkle {
260+ 0% {
261+ transform: rotate(var(--sparkle-angle)) translateX(0) scale(1);
262+ opacity: 1;
263+ }
264+ 50% {
265+ transform: rotate(var(--sparkle-angle)) translateX(60px) scale(1.5);
266+ opacity: 0.8;
267+ }
268+ 100% {
269+ transform: rotate(var(--sparkle-angle)) translateX(100px) scale(0);
270+ opacity: 0;
271+ }
272+ }
273+
274+ @keyframes burst {
275+ 0% {
276+ transform: scale(1);
277+ opacity: 1;
278+ }
279+ 50% {
280+ transform: scale(4);
281+ opacity: 0.8;
282+ }
283+ 100% {
284+ transform: scale(6);
285+ opacity: 0;
286+ }
287+ }
288+
289+ .animate-ripple {
290+ animation: ripple 0.6s ease-out forwards;
291+ }
292+
293+ .animate-flare-shoot {
294+ animation: flare-shoot 1s ease-out forwards;
295+ }
296+
297+ .animate-flare-ring {
298+ animation: flare-ring 1s ease-out forwards;
299+ }
300+
301+ .animate-sparkle {
302+ animation: sparkle 0.8s ease-out forwards;
303+ }
304+
305+ .animate-burst {
306+ animation: burst 0.6s ease-out forwards;
307+ }
308+ ` } </ style >
309+ </ div >
310+ ) ;
311+ }
0 commit comments