7 Commits

Author SHA1 Message Date
fe728c129f Bob AI: Add a 3D flip animation to testimonial cards on hover. When 2026-02-27 07:55:54 +00:00
58c0e016b7 Merge version_5 into main
Merge version_5 into main
2026-02-27 07:54:13 +00:00
0f84616d2d Bob AI: Add decorative red hearts around the testimonials section (a 2026-02-27 07:53:33 +00:00
9efac29bb7 Merge version_4 into main
Merge version_4 into main
2026-02-27 07:52:11 +00:00
e0760e8bd0 Bob AI: make theme feel more comfortable and soft 2026-02-27 07:51:31 +00:00
6edd5ea507 Merge version_3 into main
Merge version_3 into main
2026-02-27 07:50:23 +00:00
c168e80ffe Bob AI: Replace the current NavbarStyleFullscreen with a different n 2026-02-27 07:49:42 +00:00
3 changed files with 112 additions and 51 deletions

View File

@@ -9,6 +9,7 @@ import TestimonialCardOne from '@/components/sections/testimonial/TestimonialCar
import FaqSplitMedia from '@/components/sections/faq/FaqSplitMedia';
import ContactCenter from '@/components/sections/contact/ContactCenter';
import FooterSimple from '@/components/sections/footer/FooterSimple';
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
export default function LandingPage() {
return (
@@ -25,7 +26,7 @@ export default function LandingPage() {
headingFontWeight="semibold"
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
<NavbarStyleApple
navItems={[
{ name: "Home", id: "#hero" },
{ name: "About", id: "#about" },
@@ -34,8 +35,6 @@ export default function LandingPage() {
{ name: "Contact", id: "#contact" },
]}
brandName="Sweet Delights"
bottomLeftText="Baked with Love"
bottomRightText="hello@sweetdelights.com"
/>
</div>
<div id="hero" data-section="hero">

View File

@@ -3,22 +3,22 @@
/* --vw is set by ThemeProvider */
/* --background: #f7f6f7;;
--card: #ffffff;;
--foreground: #0c1325;;
--primary-cta: #0798ff;;
--secondary-cta: #ffffff;;
--accent: #93c7ff;;
--background-accent: #a8cde8;; */
--card: #faf9fb;;
--foreground: #2d3e52;;
--primary-cta: #6b9fd9;;
--secondary-cta: #f5f3f7;;
--accent: #b8d9f0;;
--background-accent: #d4e4f0;; */
--background: #f7f6f7;;
--card: #ffffff;;
--foreground: #0c1325;;
--primary-cta: #0798ff;;
--background: #faf8fb;;
--card: #faf9fb;;
--foreground: #2d3e52;;
--primary-cta: #6b9fd9;;
--primary-cta-text: #f7f6f7;;
--secondary-cta: #ffffff;;
--secondary-cta: #f5f3f7;;
--secondary-cta-text: #0c1325;;
--accent: #93c7ff;;
--background-accent: #a8cde8;;
--accent: #b8d9f0;;
--background-accent: #d4e4f0;;
/* text sizing - set by ThemeProvider */
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);

View File

@@ -1,10 +1,10 @@
"use client";
import { memo } from "react";
import { memo, useState } from "react";
import CardStack from "@/components/cardStack/CardStack";
import MediaContent from "@/components/shared/MediaContent";
import { cls } from "@/lib/utils";
import { Star } from "lucide-react";
import { Star, Heart } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, ButtonAnimationType, CardAnimationTypeWith3D, GridVariant, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
@@ -18,6 +18,7 @@ type Testimonial = {
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
backText?: string;
};
interface TestimonialCardOneProps {
@@ -69,6 +70,7 @@ interface TestimonialCardProps {
nameClassName?: string;
roleClassName?: string;
companyClassName?: string;
backTextClassName?: string;
}
const TestimonialCard = memo(({
@@ -80,41 +82,84 @@ const TestimonialCard = memo(({
nameClassName = "",
roleClassName = "",
companyClassName = "",
backTextClassName = "",
}: TestimonialCardProps) => {
return (
<div className={cls("relative h-full rounded-theme-capped overflow-hidden group", cardClassName)}>
<MediaContent
imageSrc={testimonial.imageSrc}
videoSrc={testimonial.videoSrc}
imageAlt={testimonial.imageAlt || testimonial.name}
videoAriaLabel={testimonial.videoAriaLabel || testimonial.name}
imageClassName={cls("relative z-1 w-full h-full object-cover!", imageClassName)}
/>
const [isFlipped, setIsFlipped] = useState(false);
<div className={cls("!absolute z-1 bottom-6 left-6 right-6 card backdrop-blur-xs p-6 flex flex-col gap-3 rounded-theme-capped", overlayClassName)}>
<div className={cls("relative z-1 flex gap-1", ratingClassName)}>
{Array.from({ length: 5 }).map((_, index) => (
<Star
key={index}
className={cls(
"h-5 w-auto text-accent",
index < testimonial.rating ? "fill-accent" : "fill-transparent"
)}
strokeWidth={1.5}
/>
))}
return (
<div
className={cls("relative h-full rounded-theme-capped overflow-hidden group cursor-pointer", cardClassName)}
style={{
perspective: "1000px",
}}
onMouseEnter={() => setIsFlipped(true)}
onMouseLeave={() => setIsFlipped(false)}
>
<div
style={{
transformStyle: "preserve-3d",
transform: isFlipped ? "rotateY(180deg)" : "rotateY(0deg)",
transition: "transform 0.6s ease-in-out",
width: "100%",
height: "100%",
}}
>
{/* Front side */}
<div
style={{
backfaceVisibility: "hidden",
WebkitBackfaceVisibility: "hidden",
}}
className="w-full h-full"
>
<MediaContent
imageSrc={testimonial.imageSrc}
videoSrc={testimonial.videoSrc}
imageAlt={testimonial.imageAlt || testimonial.name}
videoAriaLabel={testimonial.videoAriaLabel || testimonial.name}
imageClassName={cls("relative z-1 w-full h-full object-cover!", imageClassName)}
/>
<div className={cls("!absolute z-1 bottom-6 left-6 right-6 card backdrop-blur-xs p-6 flex flex-col gap-3 rounded-theme-capped", overlayClassName)}>
<div className={cls("relative z-1 flex gap-1", ratingClassName)}>
{Array.from({ length: 5 }).map((_, index) => (
<Star
key={index}
className={cls(
"h-5 w-auto text-accent",
index < testimonial.rating ? "fill-accent" : "fill-transparent"
)}
strokeWidth={1.5}
/>
))}
</div>
<h3 className={cls("relative z-1 text-2xl font-medium text-foreground leading-[1.1] mt-1", nameClassName)}>
{testimonial.name}
</h3>
<div className="relative z-1 flex flex-col gap-1">
<p className={cls("text-base text-foreground leading-[1.1]", roleClassName)}>
{testimonial.role}
</p>
<p className={cls("text-base text-foreground leading-[1.1]", companyClassName)}>
{testimonial.company}
</p>
</div>
</div>
</div>
<h3 className={cls("relative z-1 text-2xl font-medium text-foreground leading-[1.1] mt-1", nameClassName)}>
{testimonial.name}
</h3>
<div className="relative z-1 flex flex-col gap-1">
<p className={cls("text-base text-foreground leading-[1.1]", roleClassName)}>
{testimonial.role}
</p>
<p className={cls("text-base text-foreground leading-[1.1]", companyClassName)}>
{testimonial.company}
{/* Back side */}
<div
style={{
backfaceVisibility: "hidden",
WebkitBackfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
className={cls("absolute inset-0 w-full h-full card backdrop-blur-xs p-6 flex flex-col gap-3 rounded-theme-capped justify-center items-center", backTextClassName)}
>
<p className="text-base text-foreground leading-relaxed text-center">
{testimonial.backText || "Hover to see more"}
</p>
</div>
</div>
@@ -164,7 +209,22 @@ const TestimonialCardOne = ({
textBoxButtonTextClassName = "",
}: TestimonialCardOneProps) => {
return (
<CardStack
<div className="relative">
{/* Decorative hearts around section */}
<div className="absolute -top-8 left-1/4 text-red-500 opacity-60 animate-pulse">
<Heart size={24} fill="currentColor" />
</div>
<div className="absolute -top-6 right-1/4 text-red-500 opacity-50 animate-pulse" style={{ animationDelay: "0.5s" }}>
<Heart size={20} fill="currentColor" />
</div>
<div className="absolute -bottom-8 left-1/3 text-red-500 opacity-60 animate-pulse" style={{ animationDelay: "1s" }}>
<Heart size={22} fill="currentColor" />
</div>
<div className="absolute -bottom-6 right-1/3 text-red-500 opacity-50 animate-pulse" style={{ animationDelay: "0.3s" }}>
<Heart size={18} fill="currentColor" />
</div>
<CardStack
mode={carouselMode}
gridVariant={gridVariant}
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
@@ -208,9 +268,11 @@ const TestimonialCardOne = ({
nameClassName={nameClassName}
roleClassName={roleClassName}
companyClassName={companyClassName}
backTextClassName=""
/>
))}
</CardStack>
</CardStack>
</div>
);
};