Skip to content
All Taste components

Testimonials

Testimonial — quote beside a portrait

One customer’s quote set beside a tall portrait, with two results from their story and a link to read it.

portrait with name tag · large quote · 2 result figures · company mark · stacks on small screens

“Saturday pre-orders used to arrive by text, email and phone. Now customers pick a pickup slot themselves, and I bake for exactly the orders I have.”

Orders taken online
9 in 10
Admin saved each week
6 hrs
SaltmarshRead Nadia’s story
import { ArrowRight } from "lucide-react";

const story = {
  quote: "Saturday pre-orders used to arrive by text, email and phone. Now customers pick a pickup slot themselves, and I bake for exactly the orders I have.",
  name: "Nadia Ferreira",
  role: "Owner, Saltmarsh Bakery",
  since: "Customer since 2023",
  company: "Saltmarsh",
  stats: [
    { label: "Orders taken online", value: "9 in 10" },
    { label: "Admin saved each week", value: "6 hrs" },
  ],
  href: "/customers/saltmarsh",
};

export function TestimonialPortrait() {
  return (
    <section aria-label="Customer story" className="bg-bg px-5 py-12 font-sans sm:px-10 sm:py-20">
      <div className="mx-auto grid max-w-6xl items-center gap-9 md:grid-cols-[0.85fr_1.15fr] md:gap-16">
        {/* Swap the gradient for <img src="..." alt="Portrait of Nadia Ferreira" className="size-full object-cover" /> */}
        <div role="img" aria-label={"Portrait of " + story.name}
          className="relative aspect-[4/5] max-h-[520px] w-full overflow-hidden rounded-xl bg-gradient-to-br from-brand-300 via-brand-600 to-brand-900 shadow-2xl">
          <div className="absolute inset-x-4 bottom-4 flex flex-wrap items-center justify-between gap-2 rounded-lg bg-neutral-950/60 px-3.5 py-2.5 text-[13px] text-neutral-50 backdrop-blur">
            <div>
              <p className="text-sm font-semibold">{story.name}</p>
              <p className="text-neutral-50/75">{story.role}</p>
            </div>
            <span className="text-neutral-50/75">{story.since}</span>
          </div>
        </div>

        <figure className="grid justify-items-start gap-6">
          <svg viewBox="0 0 40 40" className="size-[38px] text-primary" fill="currentColor" aria-hidden>
            <path d="M6 30V20.5C6 13.6 9.6 9.4 16.4 8l1.2 3.4c-3.6 1.1-5.4 3.4-5.6 6.6H17V30H6Zm17 0V20.5c0-6.9 3.6-11.1 10.4-12.5l1.2 3.4c-3.6 1.1-5.4 3.4-5.6 6.6H34V30H23Z" />
          </svg>
          <blockquote className="text-pretty text-[22px] font-medium leading-snug tracking-tight text-fg sm:text-3xl">
            <p>“{story.quote}”</p>
          </blockquote>
          <dl className="grid w-full grid-cols-2 gap-3">
            {story.stats.map((s) => (
              <div key={s.label} className="rounded-lg bg-surface px-4 py-3.5">
                <dt className="text-[13px] text-fg-muted">{s.label}</dt>
                <dd className="mt-1 text-[26px] font-semibold tabular-nums tracking-tight text-fg">{s.value}</dd>
              </div>
            ))}
          </dl>
          <figcaption className="flex w-full flex-wrap items-center gap-x-6 gap-y-3 border-t border-border pt-5">
            <span className="font-serif text-lg font-semibold italic text-fg">{story.company}</span>
            <a href={story.href} className="ml-auto inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline">
              Read Nadia’s story <ArrowRight className="size-4" aria-hidden />
            </a>
          </figcaption>
        </figure>
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.