Retour au catalogue

Customer Stories Grid

Grille de success stories : cards avec logo, metrique cle et quote courte.

customer-storiessimple Both Responsive a11y
minimalcorporatesaasuniversalgrid
Theme
"use client";

import { motion } from "framer-motion";
import { TrendingUp, Quote } from "lucide-react";

interface Story {
  companyName: string;
  companyInitial?: string;
  companyColor?: string;
  metric: string;
  metricLabel: string;
  quote: string;
}

interface CustomerStoriesGridProps {
  title?: string;
  subtitle?: string;
  badge?: string;
  stories?: Story[];
}

const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];

export default function CustomerStoriesGrid({
  title = "Nos clients temoignent",
  subtitle = "",
  badge = "",
  stories = [],
}: CustomerStoriesGridProps) {
  return (
    <section className="py-20 lg:py-28 px-6" style={{ background: "var(--color-background)" }}>
      <div className="mx-auto max-w-5xl">
        <motion.div
          initial={{ opacity: 0, y: 16 }}
          whileInView={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, ease }}
          viewport={{ once: true }}
          className="text-center mb-14"
        >
          {badge && (
            <span
              className="inline-block mb-4 px-3 py-1 text-xs font-medium tracking-wider uppercase rounded-full"
              style={{ color: "var(--color-accent)", border: "1px solid var(--color-border)" }}
            >
              {badge}
            </span>
          )}
          <h2 className="text-3xl md:text-4xl font-bold" style={{ color: "var(--color-foreground)" }}>
            {title}
          </h2>
          {subtitle && (
            <p className="mt-3 text-base max-w-xl mx-auto" style={{ color: "var(--color-foreground-muted)" }}>
              {subtitle}
            </p>
          )}
        </motion.div>

        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          {stories.map((story, i) => (
            <motion.div
              key={i}
              initial={{ opacity: 0, y: 24 }}
              whileInView={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.5, ease, delay: i * 0.08 }}
              viewport={{ once: true }}
              className="rounded-xl p-6 flex flex-col"
              style={{ background: "var(--color-background-card)", border: "1px solid var(--color-border)" }}
            >
              <div className="flex items-center gap-3 mb-5">
                <div
                  className="flex items-center justify-center w-10 h-10 rounded-lg text-sm font-bold"
                  style={{
                    background: story.companyColor || "var(--color-accent)",
                    color: "#fff",
                  }}
                >
                  {story.companyInitial || story.companyName[0]}
                </div>
                <span className="text-sm font-semibold" style={{ color: "var(--color-foreground)" }}>
                  {story.companyName}
                </span>
              </div>

              <div className="flex items-center gap-2 mb-4">
                <TrendingUp size={16} style={{ color: "var(--color-accent)" }} />
                <span className="text-2xl font-bold" style={{ color: "var(--color-accent)" }}>
                  {story.metric}
                </span>
                <span className="text-xs" style={{ color: "var(--color-foreground-muted)" }}>
                  {story.metricLabel}
                </span>
              </div>

              <div className="flex-1">
                <Quote size={16} className="mb-2" style={{ color: "var(--color-foreground-light)" }} />
                <p
                  className="text-sm leading-relaxed italic"
                  style={{ color: "var(--color-foreground-muted)" }}
                >
                  {story.quote}
                </p>
              </div>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  );
}

Avis

Customer Stories Grid — React Customer-stories Section — Incubator