Skip to content
All Taste components

Testimonials

Testimonials — tabbed by industry

Tabs for each kind of customer that switch between a quote from that industry and two of its results.

4 industry tabs · quote + person · results panel in brand tint · switching panels · stacks on small screens

Built for your kind of business

Hear it from someone like you

“Patients confirm or move their own appointments from the reminder. The front desk spends mornings on people in the room, not on the phone.”

Dr. Amara OseiPractice lead, Fernhill Dental
missed appointments
−42%
of phone calls saved daily
3 hrs
Read the full story

“Every chair has its own calendar and clients pick the stylist they want. Saturdays are booked out by Wednesday.”

Leah MorenoOwner, Loft Hair Studio
chairs booked on weekends
94%
to set up the team
11 min
Read the full story

“Class packs, waitlists and reminders all in one place. When someone cancels, the next person on the waitlist gets the spot automatically.”

Ines DuarteFounder, Core & Coast Pilates
empty spots after cancellations
0
class-pack renewals
2×
Read the full story

“Customers book a visit window from my website and the address lands in my calendar. I quote on site and the deposit is already paid.”

Callum ReidReid Plumbing & Heating
jobs with a deposit up front
9 / 10
of admin saved weekly
5 hrs
Read the full story
"use client";
import { useRef, useState } from "react";
import { ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";

const industries = [
  { id: "clinics", label: "Clinics", quote: "Patients confirm or move their own appointments from the reminder. The front desk spends mornings on people in the room, not on the phone.", name: "Dr. Amara Osei", role: "Practice lead, Fernhill Dental", results: [{ value: "−42%", label: "missed appointments" }, { value: "3 hrs", label: "of phone calls saved daily" }] },
  { id: "salons", label: "Salons", quote: "Every chair has its own calendar and clients pick the stylist they want. Saturdays are booked out by Wednesday.", name: "Leah Moreno", role: "Owner, Loft Hair Studio", results: [{ value: "94%", label: "chairs booked on weekends" }, { value: "11 min", label: "to set up the team" }] },
  { id: "studios", label: "Studios", quote: "Class packs, waitlists and reminders all in one place. When someone cancels, the next person on the waitlist gets the spot automatically.", name: "Ines Duarte", role: "Founder, Core & Coast Pilates", results: [{ value: "0", label: "empty spots after cancellations" }, { value: "2×", label: "class-pack renewals" }] },
  { id: "trades", label: "Trades", quote: "Customers book a visit window from my website and the address lands in my calendar. I quote on site and the deposit is already paid.", name: "Callum Reid", role: "Reid Plumbing & Heating", results: [{ value: "9 / 10", label: "jobs with a deposit up front" }, { value: "5 hrs", label: "of admin saved weekly" }] },
];

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

export function TestimonialsByIndustry() {
  const [active, setActive] = useState(0);
  const refs = useRef<(HTMLButtonElement | null)[]>([]);

  const onKeyDown = (e: React.KeyboardEvent) => {
    const last = industries.length - 1;
    const keys: Record<string, number> = {
      ArrowRight: active === last ? 0 : active + 1,
      ArrowLeft: active === 0 ? last : active - 1,
      Home: 0,
      End: last,
    };
    const next = keys[e.key];
    if (next === undefined) return;
    e.preventDefault();
    setActive(next);
    refs.current[next]?.focus();
  };

  return (
    <section aria-labelledby="industry-title" className="bg-bg px-5 py-12 font-sans sm:px-10 sm:py-20">
      <div className="mx-auto grid max-w-6xl gap-7">
        <div className="mx-auto grid max-w-2xl justify-items-center gap-3.5 text-center">
          <p className="text-[13px] font-medium text-primary">Built for your kind of business</p>
          <h2 id="industry-title" className="text-balance text-3xl font-semibold tracking-tight text-fg sm:text-4xl">Hear it from someone like you</h2>
        </div>

        <div role="tablist" aria-label="Industry" className="flex flex-wrap justify-center gap-1.5">
          {industries.map((d, i) => {
            const selected = active === i;
            return (
              <button
                key={d.id}
                ref={(el) => { refs.current[i] = el; }}
                id={"industry-tab-" + d.id}
                type="button"
                role="tab"
                aria-selected={selected}
                aria-controls={"industry-panel-" + d.id}
                tabIndex={selected ? 0 : -1}
                onClick={() => setActive(i)}
                onKeyDown={onKeyDown}
                className={cn(
                  "h-8 rounded-full border px-3.5 text-[13px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
                  selected ? "border-fg bg-fg text-bg" : "border-border bg-bg text-fg-muted hover:border-border-strong hover:text-fg",
                )}
              >
                {d.label}
              </button>
            );
          })}
        </div>

        {industries.map((d, i) => (
          <div key={d.id} id={"industry-panel-" + d.id} role="tabpanel" aria-labelledby={"industry-tab-" + d.id} hidden={active !== i} tabIndex={0}>
            <div className="grid overflow-hidden rounded-xl border border-border bg-bg md:grid-cols-[1.6fr_1fr]">
              <figure className="grid content-between gap-7 p-7 md:p-10">
                <blockquote className="text-pretty text-xl font-medium leading-snug tracking-tight text-fg sm:text-[25px]">
                  <p>“{d.quote}”</p>
                </blockquote>
                <figcaption className="flex items-center gap-3">
                  <span className="grid size-9 place-items-center rounded-full bg-primary-soft text-[13px] font-medium text-primary-soft-fg" aria-hidden>{initials(d.name)}</span>
                  <div>
                    <p className="text-sm font-semibold text-fg">{d.name}</p>
                    <p className="text-[13px] text-fg-muted">{d.role}</p>
                  </div>
                </figcaption>
              </figure>
              <div className="grid content-between gap-6 bg-primary-soft p-7 text-primary-soft-fg md:p-10">
                <dl className="grid gap-[18px]">
                  {d.results.map((r) => (
                    <div key={r.label}>
                      <dt className="text-[13px] opacity-85">{r.label}</dt>
                      <dd className="mt-0.5 text-4xl font-semibold tabular-nums tracking-tight">{r.value}</dd>
                    </div>
                  ))}
                </dl>
                <a href={"/customers/" + d.id} className="inline-flex items-center gap-1.5 text-sm font-medium hover:underline">
                  Read the full story <ArrowRight className="size-4" aria-hidden />
                </a>
              </div>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

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