Blocks

Testimonial 53

Testimonials
3 ColumnsText Align LeftVideo LightboxAvatar ImageCardsStar Rating
Testimonial 53

Code

relume-testimonial53.tsx
import { Card } from "@/components/ui/card";
import { StarFull, PlayCircle, ProgressActivity } from "relume-icons";
import { useState } from "react";
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import clsx from "clsx";

type ImageProps = {
  src: string;
  alt?: string;
};

type VideoProps = {
  url: string;
  image: ImageProps;
};

type TestimonialProps = {
  video: VideoProps;
  description: string;
  author: {
    name: string;
    position: string;
    avatar: ImageProps;
  };
};

type Props = {
  heading: string;
  description: string;
  testimonials: TestimonialProps[];
};

export type Testimonial53Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

export const Testimonial53 = (props: Testimonial53Props) => {
  const { heading, description, testimonials } = {
    ...Testimonial53Defaults,
    ...props,
  };
  const [isIframeLoaded, setIsIframeLoaded] = useState<boolean>(false);

  return (
    <section className="px-[5%] py-16 md:py-24 lg:py-28">
      <div className="container">
        <div className="mb-12 md:mb-18 lg:mb-20">
          <div className="mx-auto w-full max-w-lg text-center">
            <h2 className="mb-5 text-h2 font-bold md:mb-6">{heading}</h2>
            <p className="text-medium">{description}</p>
          </div>
        </div>
        <div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
          {testimonials.map((testimonial, index) => (
            <Card key={index}>
              <Dialog>
                <DialogTrigger asChild>
                  <button className="relative flex w-full items-center justify-center overflow-hidden">
                    <img
                      src={testimonial.video.image.src}
                      alt={testimonial.video.image.alt}
                      className="aspect-square w-full object-cover"
                    />
                    <span className="absolute inset-0 z-10 bg-neutral-darkest/40" />
                    <PlayCircle className="absolute z-20 size-20 text-white" />
                  </button>
                </DialogTrigger>
                <DialogContent>
                  {!isIframeLoaded && (
                    <ProgressActivity className="mx-auto size-16 animate-spin text-white" />
                  )}
                  <iframe
                    className={clsx(
                      "z-0 mx-auto aspect-video h-full w-full md:w-[738px] lg:w-[940px]",
                      {
                        visible: isIframeLoaded,
                        hidden: !isIframeLoaded,
                      },
                    )}
                    src={testimonial.video.url}
                    allow="autoplay; encrypted-media; picture-in-picture"
                    allowFullScreen
                    onLoad={() => setIsIframeLoaded(true)}
                  />
                </DialogContent>
              </Dialog>

              <div className="p-6 md:p-8">
                <div className="mb-5 flex items-center gap-1 md:mb-6">
                  <StarFull className="size-6 text-scheme-text" />
                  <StarFull className="size-6 text-scheme-text" />
                  <StarFull className="size-6 text-scheme-text" />
                  <StarFull className="size-6 text-scheme-text" />
                  <StarFull className="size-6 text-scheme-text" />
                </div>
                <p className="text-medium">"{testimonial.description}"</p>
                <div className="mt-5 md:mt-6">
                  <div className="flex items-center gap-4">
                    <div className="size-14 min-h-14 min-w-14 overflow-hidden rounded-full">
                      <img
                        src={testimonial.author.avatar.src}
                        alt={testimonial.author.avatar.alt}
                        className="size-full object-cover"
                      />
                    </div>
                    <div>
                      <p className="font-semibold">{testimonial.author.name}</p>
                      <p>{testimonial.author.position}</p>
                    </div>
                  </div>
                </div>
              </div>
            </Card>
          ))}
        </div>
      </div>
    </section>
  );
};

export const Testimonial53Defaults: Props = {
  heading: "Customer testimonials",
  description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",

  testimonials: [
    {
      video: {
        url: "https://www.youtube.com/embed/8DKLYsikxTs?si=Ch9W0KrDWWUiCMMW",
        image: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-video-thumbnail.svg",
          alt: "Relume placeholder image",
        },
      },
      description:
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare.",
      author: {
        name: "Name Surname",
        position: "Position, Company name",
        avatar: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg",
          alt: "Relume placeholder image",
        },
      },
    },
    {
      video: {
        url: "https://www.youtube.com/embed/8DKLYsikxTs?si=Ch9W0KrDWWUiCMMW",
        image: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-video-thumbnail.svg",
          alt: "Relume placeholder image",
        },
      },
      description:
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare.",
      author: {
        name: "Name Surname",
        position: "Position, Company name",
        avatar: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg",
          alt: "Relume placeholder image",
        },
      },
    },
    {
      video: {
        url: "https://www.youtube.com/embed/8DKLYsikxTs?si=Ch9W0KrDWWUiCMMW",
        image: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-video-thumbnail.svg",
          alt: "Relume placeholder image",
        },
      },
      description:
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare.",
      author: {
        name: "Name Surname",
        position: "Position, Company name",
        avatar: {
          src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg",
          alt: "Relume placeholder image",
        },
      },
    },
  ],
};

npm i @radix-ui/react-accordion @radix-ui/react-dialog @radix-ui/react-slot @radix-ui/react-tabs class-variance-authority@0.7.1 clsx@^2.1.1 motion@^12.15.0 relume-icons@1.3.0 tailwind-merge@^2.2.2