Todos os componentes

Testimonials 3

Testimonials 3 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, com licença MIT. Copia e cola no teu projeto.

SmoothUI MIT layout

O que é

Testimonials 3 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, com licença MIT. Copia e cola no teu projeto.

Como usar

Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.

Abrir no catálogo

Cinco cópias grátis por mês. Sem cartão.

Precisa de

npm i lucide-react motion

Licença

Este componente vem de SmoothUI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.

Testimonials 3

index.tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { getAvatarUrl, getTestimonials } from "@/lib/smoothui-data";
import { Star } from "lucide-react";
import { motion, useReducedMotion } from "motion/react";

const testimonials = getTestimonials(4);

export function TestimonialsStars() {
  const shouldReduceMotion = useReducedMotion();
  return (
    <section>
      <div className="py-24">
        <div className="container mx-auto w-full max-w-5xl px-6">
          <motion.div
            animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
            className="mb-12"
            initial={
              shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
            }
            transition={
              shouldReduceMotion
                ? { duration: 0 }
                : { duration: 0.6, ease: [0.22, 1, 0.36, 1] }
            }
          >
            <h2 className="font-semibold text-4xl text-foreground">
              Developer Reviews
            </h2>
            <p className="my-4 text-balance text-lg text-muted-foreground">
              See what the community is saying about SmoothUI. Real feedback
              from developers building amazing user experiences.
            </p>
          </motion.div>

          <motion.div
            animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1 }}
            className="grid 3xl:grid-cols-3 3xl:gap-12 gap-6 lg:grid-cols-2"
            initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}
            transition={
              shouldReduceMotion
                ? { duration: 0 }
                : { delay: 0.2, duration: 0.6, ease: [0.22, 1, 0.36, 1] }
            }
          >
            {testimonials.map((testimonial, index) => (
              <motion.div
                animate={
                  shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
                }
                className="group rounded-2xl border border-transparent px-4 py-3 duration-200 hover:border-border hover:bg-background/50"
                initial={
                  shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 30 }
                }
                key={testimonial.name}
                transition={
                  shouldReduceMotion
                    ? { duration: 0 }
                    : {
                        delay: index * 0.15,
                        duration: 0.5,
                        ease: [0.22, 1, 0.36, 1],
                      }
                }
                whileHover={
                  shouldReduceMotion
                    ? {}
                    : {
                        transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
                        y: -4,
                      }
                }
              >
                <motion.div
                  animate={
                    shouldReduceMotion
                      ? { opacity: 1 }
                      : { opacity: 1, scale: 1 }
                  }
                  className="flex gap-1"
                  initial={
                    shouldReduceMotion
                      ? { opacity: 1 }
                      : { opacity: 0, scale: 0.8 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.2,
                          duration: 0.4,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  {Array.from({ length: 5 }).map((_, i) => (
                    <motion.div
                      animate={
                        shouldReduceMotion
                          ? { opacity: 1 }
                          : { opacity: 1, scale: 1 }
                      }
                      initial={
                        shouldReduceMotion
                          ? { opacity: 1 }
                          : { opacity: 0, scale: 0 }
                      }
                      key={`${testimonial.name}-star-${i}`}
                      transition={
                        shouldReduceMotion
                          ? { duration: 0 }
                          : {
                              delay: index * 0.15 + 0.2 + i * 0.05,
                              duration: 0.3,
                              ease: [0.68, -0.55, 0.265, 1.55],
                            }
                      }
                    >
                      <Star
                        className={cn(
                          "size-4 transition-colors duration-200",
                          i < (testimonial.stars || 0)
                            ? "fill-accent stroke-accent"
                            : "fill-primary stroke-border"
                        )}
                      />
                    </motion.div>
                  ))}
                </motion.div>

                <motion.p
                  animate={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
                  }
                  className="my-4 text-foreground"
                  initial={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 10 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.4,
                          duration: 0.4,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  {testimonial.content}
                </motion.p>

                <motion.div
                  animate={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 1, x: 0 }
                  }
                  className="flex items-center gap-2"
                  initial={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 0, x: -10 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.5,
                          duration: 0.3,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  <Avatar className="size-6 border border-transparent shadow ring-1 ring-foreground/10">
                    <AvatarImage
                      alt={testimonial.name}
                      src={getAvatarUrl(testimonial.avatar, 48)}
                    />
                    <AvatarFallback>
                      {testimonial.name.charAt(0)}
                    </AvatarFallback>
                  </Avatar>
                  <div className="font-medium text-foreground text-sm">
                    {testimonial.name}
                  </div>
                  <span
                    aria-hidden="true"
                    className="size-1 rounded-full bg-foreground/25"
                  />
                  <span className="text-muted-foreground text-sm">
                    {testimonial.role}
                  </span>
                </motion.div>
              </motion.div>
            ))}
          </motion.div>
        </div>
      </div>
    </section>
  );
}

export default TestimonialsStars;

Componentes parecidos