Skip to content

Commit 09cb2e2

Browse files
authored
Merge pull request #1646 from vishalpatil-45/fix/testimonial-theme-readability
fix(ui): improve testimonial and mentorship section consistency
2 parents 1caf13e + e677330 commit 09cb2e2

6 files changed

Lines changed: 712 additions & 347 deletions

File tree

src/components/testimonials/TestimonialCard.tsx

Lines changed: 128 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useState } 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

@@ -11,7 +12,6 @@ interface TestimonialCardProps {
1112
date: string;
1213
avatar: string;
1314
gradient?: string;
14-
borderColor?: string;
1515
}
1616

1717
const TestimonialCard: React.FC<TestimonialCardProps> = ({
@@ -21,109 +21,146 @@ const TestimonialCard: React.FC<TestimonialCardProps> = ({
2121
date,
2222
avatar,
2323
gradient,
24-
borderColor,
2524
}) => {
26-
const { colorMode, isDark } = useSafeColorMode();
25+
const { isDark } = useSafeColorMode();
2726

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-
}
27+
// Map gradient prop to card visual variant
28+
const isAccent = gradient === "bg-purple-100";
29+
const isFeatured = gradient === "bg-pink-100";
3730

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-
};
31+
const getRole = () => {
32+
if (username === "VivienChen") return "Founder @ Toastie (BC Y24)";
33+
if (username === "DanielHan") return "Founder @ Unsloth AI (YC W24, BC Y24)";
34+
if (username === "EthanTrang") return "AI Engineer @ Relevance AI";
35+
return null;
5536
};
5637

57-
const [mounted, setMounted] = useState(false);
58-
useEffect(() => setMounted(true), []);
38+
const role = getRole();
39+
40+
// Card colors by variant
41+
const cardBg = isAccent
42+
? "linear-gradient(135deg, #4338ca 0%, #6d28d9 100%)"
43+
: isDark
44+
? "#0a0a0a"
45+
: "#ffffff";
46+
47+
const isInverted = isAccent || isDark;
48+
const textClr = isInverted ? "rgba(255,255,255,0.85)" : "rgba(17,24,39,0.78)";
49+
const nameClr = isInverted ? "#f1f5f9" : "#0f172a";
50+
const mutedClr = isAccent
51+
? "rgba(255,255,255,0.50)"
52+
: isDark
53+
? "rgba(148,163,184,0.55)"
54+
: "rgba(100,116,139,0.65)";
55+
const borderClr = isAccent
56+
? "rgba(255,255,255,0.10)"
57+
: isDark
58+
? "rgba(255,255,255,0.06)"
59+
: "rgba(0,0,0,0.07)";
60+
const quoteClr = isAccent
61+
? "rgba(255,255,255,0.12)"
62+
: isDark
63+
? "rgba(99,102,241,0.12)"
64+
: "rgba(99,102,241,0.08)";
5965

6066
return (
6167
<motion.div
62-
initial={mounted ? { opacity: 0, y: 20 } : false}
63-
animate={{ opacity: 1, y: 0 }}
64-
exit={{ opacity: 0, y: -20 }}
65-
whileHover={{ y: -8 }}
66-
transition={{ duration: 0.3 }}
67-
className={`group relative h-full w-full overflow-hidden rounded-3xl min-h-[550px] flex flex-col justify-between p-8`}
68-
style={getBackgroundStyle()}
68+
whileHover={{ scale: 1.035, y: -3 }}
69+
transition={{ type: "spring", stiffness: 400, damping: 25 }}
70+
className="relative flex flex-col justify-between overflow-hidden rounded-2xl p-5 flex-shrink-0 cursor-default select-none"
71+
style={{
72+
width: 320,
73+
minHeight: 180,
74+
background: cardBg,
75+
border: `1px solid ${borderClr}`,
76+
boxShadow: isDark
77+
? "0 2px 24px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.03)"
78+
: "0 2px 20px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)",
79+
}}
6980
>
70-
{/* Subtle glossy overlay */}
71-
<div
72-
className="absolute inset-0 rounded-3xl pointer-events-none"
73-
style={{
74-
background: `linear-gradient(135deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.2) 40%, transparent 70%)`,
75-
}}
76-
/>
77-
78-
{/* Soft shadow */}
79-
<div className="absolute inset-0 rounded-3xl pointer-events-none shadow-lg shadow-black/5" />
81+
{/* Grid pattern overlay for featured cards */}
82+
{isFeatured && (
83+
<div
84+
className="absolute inset-0 pointer-events-none"
85+
style={{
86+
backgroundImage:
87+
"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)",
88+
backgroundSize: "44px 48px",
89+
maskImage:
90+
"radial-gradient(ellipse 80% 50% at 50% 0%, #000 70%, transparent 110%)",
91+
WebkitMaskImage:
92+
"radial-gradient(ellipse 80% 50% at 50% 0%, #000 70%, transparent 110%)",
93+
}}
94+
/>
95+
)}
8096

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

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

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

0 commit comments

Comments
 (0)