Testimonial Card Wall

Masonry grid of testimonial cards with avatars, star ratings, and quotes.

Preview

Source

tsx
"use client";

import { Star } from "lucide-react";
import { testimonials } from "@/lib/placeholders";

function StarRating({ rating }: { rating: number }) {
  return (
    <div className="flex gap-0.5">
      {Array.from({ length: 5 }).map((_, i) => (
        <Star
          key={i}
          className={`w-4 h-4 ${
            i < rating
              ? "fill-amber-400 text-amber-400"
              : "fill-muted text-muted"
          }`}
        />
      ))}
    </div>
  );
}

export default function TestimonialCardWall() {
  return (
    <section className="py-20 px-4 sm:px-6 bg-background">
      <div className="max-w-6xl mx-auto">
        {/* Header */}
        <div className="text-center mb-14">
          <span className="inline-block text-xs font-semibold uppercase tracking-widest text-primary bg-primary/10 border border-primary/20 rounded-full px-4 py-1.5 mb-4">
            Testimonials
          </span>
          <h2 className="text-3xl sm:text-4xl lg:text-5xl font-extrabold tracking-tight text-foreground mb-4">
            Loved by teams everywhere
          </h2>
          <p className="text-lg text-muted-foreground max-w-xl mx-auto">
            Don't take our word for it — here's what our clients have to say.
          </p>
        </div>

        {/* Masonry grid */}
        <div className="columns-1 sm:columns-2 lg:columns-3 gap-6 space-y-0">
          {testimonials.map((t) => (
            <div
              key={t.id}
              className="break-inside-avoid mb-6 bg-card border border-border rounded-2xl p-6 shadow-sm hover:shadow-md transition-shadow duration-300"
            >
              <StarRating rating={t.rating} />
              <blockquote className="mt-4 text-foreground leading-relaxed text-sm sm:text-base">
                "{t.text}"
              </blockquote>
              <div className="mt-5 flex items-center gap-3">
                <img
                  src={t.avatar}
                  alt={t.name}
                  className="w-10 h-10 rounded-full object-cover ring-2 ring-border"
                />
                <div>
                  <p className="font-semibold text-sm text-foreground">{t.name}</p>
                  <p className="text-xs text-muted-foreground">{t.role}</p>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}
Claude Code Instructions

CLI Install

npx innovations add card-wall

Where to use it

Place this section after your features or case studies section on a landing page. In Astro: import TestimonialCardWall from '../components/innovations/testimonials/card-wall'; <TestimonialCardWall client:load /> (or client:visible for below-the-fold lazy loading) In Next.js: import TestimonialCardWall from '@/components/innovations/testimonials/card-wall'; // Add below the features section in your page component Pass custom testimonials via the optional 'testimonials' prop, or edit the imported data in src/lib/placeholders.ts.