@@ -96,7 +96,17 @@ function CompactSearchBar({
9696 remaining : number
9797} ) {
9898 return (
99- < div className = "bg-[#09090b]/95 backdrop-blur-xl border-b border-white/5 sticky top-16 z-40" >
99+ < motion . div
100+ className = "bg-[#09090b]/95 backdrop-blur-xl border-b border-white/5 sticky top-16 z-40"
101+ animate = { loading ? {
102+ boxShadow : [
103+ '0 0 0 rgba(99, 102, 241, 0)' ,
104+ '0 0 30px rgba(99, 102, 241, 0.3)' ,
105+ '0 0 0 rgba(99, 102, 241, 0)' ,
106+ ]
107+ } : { } }
108+ transition = { { duration : 1.5 , repeat : Infinity } }
109+ >
100110 < div className = "max-w-4xl mx-auto px-6 py-4" >
101111 < div className = "flex items-center gap-4" >
102112 { /* Back button */ }
@@ -110,18 +120,38 @@ function CompactSearchBar({
110120
111121 { /* Search input */ }
112122 < form onSubmit = { ( e ) => { e . preventDefault ( ) ; onSearch ( ) ; } } className = "flex-1 flex items-center gap-3" >
113- < div className = "flex-1 relative" >
123+ < motion . div
124+ className = "flex-1 relative"
125+ animate = { loading ? {
126+ scale : [ 1 , 1.01 , 1 ] ,
127+ } : { } }
128+ transition = { { duration : 0.8 , repeat : Infinity } }
129+ >
114130 < div className = "absolute left-4 top-1/2 -translate-y-1/2 text-zinc-500" >
115- < SearchIcon />
131+ { loading ? (
132+ < motion . div
133+ animate = { { rotate : 360 } }
134+ transition = { { duration : 1 , repeat : Infinity , ease : "linear" } }
135+ >
136+ < SearchIcon />
137+ </ motion . div >
138+ ) : (
139+ < SearchIcon />
140+ ) }
116141 </ div >
117142 < input
118143 type = "text"
119144 value = { query }
120145 onChange = { ( e ) => onQueryChange ( e . target . value ) }
121146 placeholder = "Search again..."
122- className = "w-full bg-zinc-900/80 border border-zinc-800 rounded-xl pl-12 pr-4 py-3 text-white placeholder:text-zinc-500 focus:outline-none focus:border-zinc-700 transition-colors"
147+ className = { cn (
148+ "w-full bg-zinc-900/80 border rounded-xl pl-12 pr-4 py-3 text-white placeholder:text-zinc-500 focus:outline-none transition-all duration-300" ,
149+ loading
150+ ? "border-indigo-500/50 shadow-lg shadow-indigo-500/20"
151+ : "border-zinc-800 focus:border-zinc-700"
152+ ) }
123153 />
124- </ div >
154+ </ motion . div >
125155 < Button
126156 type = "submit"
127157 disabled = { ! query . trim ( ) || loading || remaining <= 0 }
@@ -141,34 +171,90 @@ function CompactSearchBar({
141171 </ form >
142172 </ div >
143173 </ div >
144- </ div >
174+ </ motion . div >
145175 )
146176}
147177
148- // ============ RESULT CARD with staggered blur-to-focus animation ============
178+ // ============ SKELETON LOADING CARD ============
179+ function SkeletonCard ( { index } : { index : number } ) {
180+ return (
181+ < motion . div
182+ initial = { { opacity : 0 , y : 30 } }
183+ animate = { { opacity : 1 , y : 0 } }
184+ transition = { { duration : 0.3 , delay : index * 0.1 } }
185+ >
186+ < Card className = "bg-[#111113] border-white/5 overflow-hidden" >
187+ < div className = "px-5 py-4 border-b border-white/5" >
188+ < div className = "flex items-start justify-between" >
189+ < div className = "space-y-2" >
190+ < div className = "h-5 w-48 bg-zinc-800 rounded animate-pulse" />
191+ < div className = "h-4 w-32 bg-zinc-800/60 rounded animate-pulse" />
192+ </ div >
193+ < div className = "text-right space-y-1" >
194+ < div className = "h-8 w-16 bg-zinc-800 rounded animate-pulse" />
195+ < div className = "h-3 w-12 bg-zinc-800/60 rounded animate-pulse" />
196+ </ div >
197+ </ div >
198+ </ div >
199+ < div className = "p-4 space-y-2 bg-[#0d0d0f]" >
200+ < div className = "h-4 w-full bg-zinc-800/40 rounded animate-pulse" />
201+ < div className = "h-4 w-5/6 bg-zinc-800/40 rounded animate-pulse" />
202+ < div className = "h-4 w-4/6 bg-zinc-800/40 rounded animate-pulse" />
203+ < div className = "h-4 w-full bg-zinc-800/40 rounded animate-pulse" />
204+ < div className = "h-4 w-3/4 bg-zinc-800/40 rounded animate-pulse" />
205+ </ div >
206+ </ Card >
207+ </ motion . div >
208+ )
209+ }
210+
211+ // ============ RESULT CARD with DRAMATIC staggered animation ============
149212const resultCardVariants = {
150213 hidden : {
151214 opacity : 0 ,
152- y : 20 ,
153- filter : 'blur(10px)' ,
154- scale : 0.98
215+ y : 60 ,
216+ scale : 0.9 ,
217+ filter : 'blur(20px)' ,
218+ rotateX : 15 ,
155219 } ,
156220 visible : ( index : number ) => ( {
157221 opacity : 1 ,
158222 y : 0 ,
159- filter : 'blur(0px)' ,
160223 scale : 1 ,
224+ filter : 'blur(0px)' ,
225+ rotateX : 0 ,
161226 transition : {
162- duration : 0.4 ,
163- delay : index * 0.075 ,
164- ease : [ 0.16 , 1 , 0.3 , 1 ] , // ease-out-expo
227+ type : 'spring' ,
228+ damping : 25 ,
229+ stiffness : 200 ,
230+ delay : index * 0.12 ,
165231 }
166232 } ) ,
167233 exit : {
168234 opacity : 0 ,
169- y : - 10 ,
170- filter : 'blur(5px)' ,
171- transition : { duration : 0.2 }
235+ y : - 30 ,
236+ scale : 0.95 ,
237+ filter : 'blur(10px)' ,
238+ transition : { duration : 0.25 }
239+ }
240+ }
241+
242+ // Hover animation for cards
243+ const cardHoverVariants = {
244+ rest : {
245+ scale : 1 ,
246+ y : 0 ,
247+ boxShadow : '0 0 0 rgba(59, 130, 246, 0)' ,
248+ } ,
249+ hover : {
250+ scale : 1.02 ,
251+ y : - 4 ,
252+ boxShadow : '0 20px 40px rgba(0, 0, 0, 0.4), 0 0 30px rgba(59, 130, 246, 0.1)' ,
253+ transition : {
254+ type : 'spring' ,
255+ damping : 20 ,
256+ stiffness : 300 ,
257+ }
172258 }
173259}
174260
@@ -181,10 +267,16 @@ function ResultCard({ result, index }: { result: SearchResult; index: number })
181267 exit = "exit"
182268 custom = { index }
183269 layout
270+ style = { { perspective : 1000 } }
184271 >
185- < Card
186- className = "bg-[#111113] border-white/5 overflow-hidden hover:border-white/10 transition-colors duration-200 hover:shadow-lg hover:shadow-black/20"
272+ < motion . div
273+ variants = { cardHoverVariants }
274+ initial = "rest"
275+ whileHover = "hover"
187276 >
277+ < Card
278+ className = "bg-[#111113] border-white/5 overflow-hidden hover:border-blue-500/30 transition-colors duration-300 cursor-pointer"
279+ >
188280 < div className = "px-5 py-4 border-b border-white/5 flex items-start justify-between" >
189281 < div >
190282 < div className = "flex items-center gap-3" >
@@ -211,7 +303,8 @@ function ResultCard({ result, index }: { result: SearchResult; index: number })
211303 >
212304 { result . code }
213305 </ SyntaxHighlighter >
214- </ Card >
306+ </ Card >
307+ </ motion . div >
215308 </ motion . div >
216309 )
217310}
@@ -376,14 +469,12 @@ export function LandingPage() {
376469 ) }
377470 </ div >
378471
379- { /* Loading State */ }
472+ { /* Loading State - Skeleton Cards */ }
380473 { loading && (
381- < div className = "flex flex-col items-center justify-center py-20" >
382- < div className = "relative" >
383- < div className = "w-12 h-12 border-4 border-zinc-700 border-t-blue-500 rounded-full animate-spin" />
384- </ div >
385- < p className = "mt-4 text-zinc-400 text-sm" > Searching codebase...</ p >
386- < p className = "text-zinc-600 text-xs mt-1" > This may take a few seconds for first search</ p >
474+ < div className = "space-y-4" >
475+ { [ 0 , 1 , 2 , 3 ] . map ( ( i ) => (
476+ < SkeletonCard key = { i } index = { i } />
477+ ) ) }
387478 </ div >
388479 ) }
389480
0 commit comments