Skip to content
All Taste components

Testimonials

Testimonials — three cards with headlines

Three customer cards, each led by a short headline drawn from the quote, under a left-aligned heading.

3 cards · headline per quote · person footer · link to more stories

Customer stories

Small businesses, paid on time

Read all stories

“Reminders do the chasing”

I used to send awkward follow-up emails every Friday. Now Tallybook nudges clients for me, politely, and I haven’t written one since March.

Grace WhitfieldCopywriter

“Our books match the bank”

Payments land already matched to the right invoice. Our accountant spends an afternoon on us now instead of a week.

Omar HaddadCo-owner, Cedar & Salt Café

“Quotes become invoices”

The quote a client approves on Monday turns into the invoice on Friday. Nothing to retype, nothing missed.

Julia BrandtBrandt Landscaping
import { ArrowRight } from "lucide-react";

const stories = [
  { headline: "Reminders do the chasing", quote: "I used to send awkward follow-up emails every Friday. Now Tallybook nudges clients for me, politely, and I haven’t written one since March.", name: "Grace Whitfield", role: "Copywriter" },
  { headline: "Our books match the bank", quote: "Payments land already matched to the right invoice. Our accountant spends an afternoon on us now instead of a week.", name: "Omar Haddad", role: "Co-owner, Cedar & Salt Café" },
  { headline: "Quotes become invoices", quote: "The quote a client approves on Monday turns into the invoice on Friday. Nothing to retype, nothing missed.", name: "Julia Brandt", role: "Brandt Landscaping" },
];

const initials = (name: string) => name.split(" ").map((w) => w[0]).join("").slice(0, 2);

export function TestimonialsThreeCards() {
  return (
    <section aria-labelledby="stories-title" className="bg-surface px-5 py-16 font-sans sm:px-10 sm:py-20">
      <div className="mx-auto max-w-6xl">
        <div className="flex flex-wrap items-end justify-between gap-4">
          <div>
            <p className="text-[13px] font-medium text-primary">Customer stories</p>
            <h2 id="stories-title" className="mt-3 text-3xl font-semibold tracking-tight text-fg sm:text-4xl">Small businesses, paid on time</h2>
          </div>
          <a href="/customers" className="inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline">
            Read all stories <ArrowRight className="size-4" aria-hidden />
          </a>
        </div>

        <div className="mt-10 grid gap-4 md:grid-cols-3">
          {stories.map((s) => (
            <figure key={s.name} className="flex flex-col justify-between gap-7 rounded-xl border border-border bg-bg p-7">
              <div className="grid gap-3">
                <h3 className="text-lg font-semibold tracking-tight text-fg">“{s.headline}”</h3>
                <blockquote className="text-[14.5px] leading-relaxed text-fg-muted"><p>{s.quote}</p></blockquote>
              </div>
              <figcaption className="flex items-center gap-3 border-t border-border pt-5">
                <span className="grid size-9 place-items-center rounded-full bg-primary-soft text-[13px] font-medium text-primary-soft-fg" aria-hidden>{initials(s.name)}</span>
                <div>
                  <p className="text-sm font-semibold text-fg">{s.name}</p>
                  <p className="text-[13px] text-fg-muted">{s.role}</p>
                </div>
              </figcaption>
            </figure>
          ))}
        </div>
      </div>
    </section>
  );
}

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