Retour au catalogue

Customer Stories Featured

Une histoire client featured : grande image, quote longue, resultats chiffres et auteur.

customer-storiesmedium Both Responsive a11y
elegantcorporatesaasuniversalsplit
Theme
"use client";

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

interface Metric {
  label: string;
  value: string;
}

interface CustomerStoriesFeaturedProps {
  badge?: string;
  companyName?: string;
  companyLogo?: string;
  quote?: string;
  authorName?: string;
  authorRole?: string;
  imageSrc?: string;
  metrics?: Metric[];
}

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

export default function CustomerStoriesFeatured({
  badge = "",
  companyName = "",
  companyLogo = "",
  quote = "",
  authorName = "",
  authorRole = "",
  imageSrc = "",
  metrics = [],
}: CustomerStoriesFeaturedProps) {
  return (
    <section className="py-20 lg:py-28 px-6" style={{ background: "var(--color-background)" }}>
      <div className="mx-auto max-w-5xl">
        {badge && (
          <motion.span
            initial={{ opacity: 0 }}
            whileInView={{ opacity: 1 }}
            viewport={{ once: true }}
            className="inline-block mb-8 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}
          </motion.span>
        )}

        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
          {/* Image */}
          <motion.div
            initial={{ opacity: 0, x: -24 }}
            whileInView={{ opacity: 1, x: 0 }}
            transition={{ duration: 0.6, ease }}
            viewport={{ once: true }}
            className="aspect-[4/3] rounded-2xl overflow-hidden flex items-center justify-center"
            style={{
              background: imageSrc ? undefined : "var(--color-background-alt)",
              border: imageSrc ? undefined : "1px dashed var(--color-border)",
            }}
          >
            {imageSrc ? (
              <img src={imageSrc} alt={companyName} className="w-full h-full object-cover" />
            ) : (
              <span className="text-sm" style={{ color: "var(--color-foreground-light)" }}>
                Image client
              </span>
            )}
          </motion.div>

          {/* Content */}
          <motion.div
            initial={{ opacity: 0, x: 24 }}
            whileInView={{ opacity: 1, x: 0 }}
            transition={{ duration: 0.6, ease, delay: 0.1 }}
            viewport={{ once: true }}
          >
            {companyLogo ? (
              <img src={companyLogo} alt={companyName} className="h-8 mb-6 object-contain" />
            ) : companyName ? (
              <span
                className="inline-block mb-6 text-lg font-bold tracking-tight"
                style={{ color: "var(--color-foreground)" }}
              >
                {companyName}
              </span>
            ) : null}

            <blockquote className="relative">
              <Quote
                size={32}
                className="mb-4"
                style={{ color: "var(--color-accent)", opacity: 0.3 }}
              />
              <p
                className="text-lg md:text-xl leading-relaxed"
                style={{ color: "var(--color-foreground)", fontFamily: "var(--font-serif)", fontStyle: "italic" }}
              >
                {quote}
              </p>
            </blockquote>

            {(authorName || authorRole) && (
              <div className="mt-6">
                {authorName && (
                  <p className="text-sm font-semibold" style={{ color: "var(--color-foreground)" }}>
                    {authorName}
                  </p>
                )}
                {authorRole && (
                  <p className="text-xs mt-0.5" style={{ color: "var(--color-foreground-muted)" }}>
                    {authorRole}
                  </p>
                )}
              </div>
            )}

            {metrics.length > 0 && (
              <div
                className="mt-8 pt-8 grid grid-cols-2 sm:grid-cols-3 gap-6"
                style={{ borderTop: "1px solid var(--color-border)" }}
              >
                {metrics.map((metric, i) => (
                  <div key={i}>
                    <div className="flex items-center gap-1.5">
                      <TrendingUp size={14} style={{ color: "var(--color-accent)" }} />
                      <span
                        className="text-2xl font-bold"
                        style={{ color: "var(--color-accent)" }}
                      >
                        {metric.value}
                      </span>
                    </div>
                    <p className="mt-1 text-xs" style={{ color: "var(--color-foreground-muted)" }}>
                      {metric.label}
                    </p>
                  </div>
                ))}
              </div>
            )}
          </motion.div>
        </div>
      </div>
    </section>
  );
}

Avis

Customer Stories Featured — React Customer-stories Section — Incubator