Skip to content

Commit e00c3b2

Browse files
committed
feat(frontend): DRAMATIC animation upgrade - skeleton cards, glow effects, spring physics
Animation improvements: - Skeleton loading cards instead of boring spinner - Search bar glows and pulses when loading (indigo glow) - Search icon rotates during search - Result cards now have DRAMATIC entrance: - y: 60px (was 20px) - more noticeable slide - blur: 20px (was 10px) - more dramatic blur - scale: 0.9 (was 0.98) - more noticeable pop - rotateX: 15deg - subtle 3D tilt effect - Spring physics (damping: 25, stiffness: 200) - Stagger delay: 120ms (was 75ms) - Hover effect with lift and blue glow shadow - Border turns blue on hover This is the 'premium feel' we promised. Not subtle - NOTICEABLE.
1 parent 66e6b10 commit e00c3b2

1 file changed

Lines changed: 118 additions & 27 deletions

File tree

frontend/src/pages/LandingPage.tsx

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ============
149212
const 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

Comments
 (0)