Skip to content

Commit 32920b0

Browse files
fix(ui): improve testimonial and mentorship section consistency
1 parent 58e615d commit 32920b0

6 files changed

Lines changed: 708 additions & 329 deletions

File tree

src/components/testimonials/TestimonialCard.tsx

Lines changed: 129 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react";
22
import { motion } from "framer-motion";
3+
import { Quote } from "lucide-react";
34
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
45
import { useSafeColorMode } from "../../utils/useSafeColorMode";
56

@@ -23,95 +24,145 @@ const TestimonialCard: React.FC<TestimonialCardProps> = ({
2324
gradient,
2425
borderColor,
2526
}) => {
26-
const { colorMode, isDark } = useSafeColorMode();
27+
const { isDark } = useSafeColorMode();
2728

28-
const getBackgroundStyle = () => {
29-
let colorStop = "";
30-
if (gradient === "bg-pink-100") {
31-
colorStop = "rgba(244, 194, 214, 0.35)"; // Pink
32-
} else if (gradient === "bg-purple-100") {
33-
colorStop = "rgba(191, 190, 255, 0.35)"; // Blue/Lavender
34-
} else {
35-
colorStop = "rgba(165, 243, 252, 0.35)"; // Cyan
36-
}
29+
// Map gradient prop to card visual variant
30+
const isAccent = gradient === "bg-purple-100";
31+
const isFeatured = gradient === "bg-pink-100";
3732

38-
return {
39-
background: `
40-
radial-gradient(
41-
ellipse 600px 500px at 10% 85%,
42-
${colorStop} 0%,
43-
rgba(255, 255, 255, 0) 70%
44-
),
45-
linear-gradient(
46-
135deg,
47-
rgba(255, 255, 255, 0.95) 0%,
48-
rgba(255, 255, 255, 0.9) 100%
49-
)
50-
`,
51-
backdropFilter: "blur(4px)",
52-
WebkitBackdropFilter: "blur(4px)",
53-
border: "1px solid rgba(200, 200, 220, 0.4)",
54-
};
33+
const getRole = () => {
34+
if (username === "VivienChen") return "Founder @ Toastie (BC Y24)";
35+
if (username === "DanielHan") return "Founder @ Unsloth AI (YC W24, BC Y24)";
36+
if (username === "EthanTrang") return "AI Engineer @ Relevance AI";
37+
return null;
5538
};
5639

40+
const role = getRole();
41+
42+
// Card colors by variant
43+
const cardBg = isAccent
44+
? "linear-gradient(135deg, #4338ca 0%, #6d28d9 100%)"
45+
: isDark
46+
? "#0a0a0a"
47+
: "#ffffff";
48+
49+
const isInverted = isAccent || isDark;
50+
const textClr = isInverted ? "rgba(255,255,255,0.85)" : "rgba(17,24,39,0.78)";
51+
const nameClr = isInverted ? "#f1f5f9" : "#0f172a";
52+
const mutedClr = isAccent
53+
? "rgba(255,255,255,0.50)"
54+
: isDark
55+
? "rgba(148,163,184,0.55)"
56+
: "rgba(100,116,139,0.65)";
57+
const borderClr = isAccent
58+
? "rgba(255,255,255,0.10)"
59+
: isDark
60+
? "rgba(255,255,255,0.06)"
61+
: "rgba(0,0,0,0.07)";
62+
const quoteClr = isAccent
63+
? "rgba(255,255,255,0.12)"
64+
: isDark
65+
? "rgba(99,102,241,0.12)"
66+
: "rgba(99,102,241,0.08)";
67+
5768
return (
5869
<motion.div
59-
initial={{ opacity: 0, y: 20 }}
60-
animate={{ opacity: 1, y: 0 }}
61-
exit={{ opacity: 0, y: -20 }}
62-
whileHover={{ y: -8 }}
63-
transition={{ duration: 0.3 }}
64-
className={`group relative h-full w-full overflow-hidden rounded-3xl min-h-[550px] flex flex-col justify-between p-8`}
65-
style={getBackgroundStyle()}
70+
whileHover={{ scale: 1.035, y: -3 }}
71+
transition={{ type: "spring", stiffness: 400, damping: 25 }}
72+
className="relative flex flex-col justify-between overflow-hidden rounded-2xl p-5 flex-shrink-0 cursor-default select-none"
73+
style={{
74+
width: 320,
75+
minHeight: 180,
76+
background: cardBg,
77+
border: `1px solid ${borderClr}`,
78+
boxShadow: isDark
79+
? "0 2px 24px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.03)"
80+
: "0 2px 20px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)",
81+
}}
6682
>
67-
{/* Subtle glossy overlay */}
68-
<div
69-
className="absolute inset-0 rounded-3xl pointer-events-none"
70-
style={{
71-
background: `linear-gradient(135deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.2) 40%, transparent 70%)`,
72-
}}
73-
/>
74-
75-
{/* Soft shadow */}
76-
<div className="absolute inset-0 rounded-3xl pointer-events-none shadow-lg shadow-black/5" />
83+
{/* Grid pattern overlay for featured cards */}
84+
{isFeatured && (
85+
<div
86+
className="absolute inset-0 pointer-events-none"
87+
style={{
88+
backgroundImage:
89+
"linear-gradient(to right, rgba(79,79,79,0.14) 1px, transparent 1px), linear-gradient(to bottom, rgba(79,79,79,0.14) 1px, transparent 1px)",
90+
backgroundSize: "44px 48px",
91+
maskImage:
92+
"radial-gradient(ellipse 80% 50% at 50% 0%, #000 70%, transparent 110%)",
93+
WebkitMaskImage:
94+
"radial-gradient(ellipse 80% 50% at 50% 0%, #000 70%, transparent 110%)",
95+
}}
96+
/>
97+
)}
7798

78-
<div className="relative z-10 flex flex-col h-full">
79-
{/* Testimonial Quote - Top Section */}
80-
<div className="mb-12">
81-
<p className="text-2xl leading-relaxed font-semibold text-gray-900 tracking-tight">
82-
"{content.replace(/#\w+/g, '').trim()}"
83-
</p>
84-
</div>
85-
86-
{/* Avatar and Info - Bottom Section */}
87-
<div className="mt-auto">
88-
<div className="flex items-center gap-6">
89-
{/* Large Avatar with White Background */}
90-
<Avatar className="h-20 w-20 overflow-hidden rounded-full border-3 border-white shadow-md flex-shrink-0 bg-white">
91-
<AvatarImage src={avatar} className="h-full w-full object-cover scale-125" />
92-
<AvatarFallback className="text-white font-bold text-lg bg-gradient-to-br from-purple-400 to-pink-400">
93-
{name.charAt(0)}
94-
</AvatarFallback>
95-
</Avatar>
99+
{/* Quote icon top-right */}
100+
<div className="absolute top-5 right-5 pointer-events-none">
101+
<Quote
102+
size={28}
103+
style={{ color: quoteClr }}
104+
className="rotate-180"
105+
/>
106+
</div>
96107

97-
<div className="flex-1">
98-
<h3 className="text-xl font-bold text-gray-900 mb-1 tracking-tight">
99-
{name}
100-
</h3>
101-
{username !== "AryanGupta" && username !== "DonaldAnyamba" && (
102-
<p className="text-sm text-gray-700 font-medium mb-3">
103-
{username === "VivienChen" ? "Founder @ Toastie (BC Y24)" :
104-
username === "DanielHan" ? "Founder @ Unsloth AI (YC W24, BC Y24)" :
105-
"AI Engineer @ Relevance AI"}
106-
</p>
107-
)}
108+
{/* Quote content */}
109+
<p
110+
className="relative z-10 text-[13px] leading-[1.65] flex-1 pr-8"
111+
style={{
112+
color: textClr,
113+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
114+
}}
115+
>
116+
&ldquo;{content.replace(/#\w+/g, "").trim()}&rdquo;
117+
</p>
108118

109-
<div className="flex items-center gap-3 text-xs text-gray-700">
110-
<span className="font-medium">{date}</span>
111-
</div>
112-
</div>
113-
</div>
119+
{/* Author row */}
120+
<div
121+
className="relative z-10 flex items-center justify-between mt-3 pt-3"
122+
style={{ borderTop: `1px solid ${borderClr}` }}
123+
>
124+
<div className="min-w-0 flex-1 mr-3">
125+
<h3
126+
className="font-semibold text-[14px] leading-tight truncate"
127+
style={{
128+
color: nameClr,
129+
fontFamily: "'Inter', -apple-system, sans-serif",
130+
}}
131+
>
132+
{name}
133+
</h3>
134+
{role && (
135+
<p
136+
className="text-[12px] mt-0.5 truncate"
137+
style={{ color: mutedClr }}
138+
>
139+
{role}
140+
</p>
141+
)}
142+
<span
143+
className="text-[11px] mt-0.5 inline-block"
144+
style={{ color: mutedClr }}
145+
>
146+
{date}
147+
</span>
114148
</div>
149+
<Avatar
150+
className="h-9 w-9 rounded-lg overflow-hidden flex-shrink-0"
151+
style={{ border: `2px solid ${borderClr}` }}
152+
>
153+
<AvatarImage
154+
src={avatar}
155+
className="h-full w-full object-cover"
156+
/>
157+
<AvatarFallback
158+
className="text-white font-semibold text-sm rounded-xl"
159+
style={{
160+
background: "linear-gradient(135deg, #6366f1, #a855f7)",
161+
}}
162+
>
163+
{name.charAt(0)}
164+
</AvatarFallback>
165+
</Avatar>
115166
</div>
116167
</motion.div>
117168
);

0 commit comments

Comments
 (0)