All Taste components
Animated heroes
Hero — with avatars and rating (motion)
The faces pop in one by one, the five stars spin into place, the email field glows once and the quotes rise in after.
avatar pops · star spins · email field glow · staggered quote cards
The booking page your regulars will actually use.
Slotwise gives your studio a simple page to book, pay a deposit and reschedule — and gives you your evenings back.
14 days of every feature · No card needed
“No more no-shows since we started taking deposits.”
“Clients book at midnight. I wake up to a full Saturday.”
“Setting it up took one coffee. Rescheduling takes one tap.”
import type { CSSProperties } from "react";
import { Star } from "lucide-react";
/* Add once to globals.css:
@keyframes rise-in { from { opacity: 0; transform: translateY(10px); } }
@keyframes star-in { from { opacity: 0; scale: .3; rotate: -60deg; } }
@keyframes focus-glow { 25%, 60% { border-color: var(--tst-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tst-primary) 22%, transparent); } }
@keyframes card-in { from { opacity: 0; transform: translateY(28px) rotate(var(--tilt, 0deg)); } }
*/
// Template rating and quotes — use your own, real reviews.
const rating = { score: "4.9", label: "from 2,000+ studio owners", faces: ["RK", "AS", "DM", "MI", "KB"] };
const quotes = [
{ quote: "No more no-shows since we started taking deposits.", name: "Riya K.", role: "Hair studio owner", initials: "RK" },
{ quote: "Clients book at midnight. I wake up to a full Saturday.", name: "Dev M.", role: "Barber", initials: "DM" },
{ quote: "Setting it up took one coffee. Rescheduling takes one tap.", name: "Meera I.", role: "Yoga teacher", initials: "MI" },
];
export function HeroSocialProofMotion({ action = "/signup" }: { action?: string }) {
return (
<section aria-labelledby="hero-title" className="bg-bg font-sans">
<div className="mx-auto flex max-w-5xl flex-col items-center gap-5 px-4 py-16 text-center sm:px-6 sm:py-24">
<div className="flex flex-wrap items-center justify-center gap-x-3.5 gap-y-2 text-[13.5px] text-fg-muted">
<div className="flex [&>*]:-ml-2 [&>*:first-child]:ml-0">
{rating.faces.map((f, i) => (
<span key={f} className="grid size-7 place-items-center rounded-full border-2 border-bg bg-primary-soft text-[11px] font-medium text-primary-soft-fg animate-pop motion-reduce:animate-fade" style={{ animationDelay: 100 + i * 70 + "ms" }}>{f}</span>
))}
</div>
<span role="img" aria-label={rating.score + " out of 5 stars"} className="flex gap-0.5 text-warning">
{Array.from({ length: 5 }, (_, i) => (
<Star key={i} className="size-[15px] fill-current animate-[star-in_.5s_cubic-bezier(.34,1.56,.64,1)_both] motion-reduce:animate-fade" style={{ animationDelay: 500 + i * 85 + "ms" }} />
))}
</span>
<span className="animate-fade [animation-delay:890ms]"><b className="font-semibold text-fg">{rating.score}</b> {rating.label}</span>
</div>
<h1 id="hero-title" className="max-w-3xl text-balance text-4xl font-semibold leading-[1.04] tracking-tight text-fg animate-rise [animation-delay:260ms] motion-reduce:animate-fade sm:text-5xl lg:text-6xl">
The booking page your regulars will actually use.
</h1>
<p className="max-w-xl text-pretty text-base leading-relaxed text-fg-muted animate-rise [animation-delay:350ms] motion-reduce:animate-fade sm:text-lg">
Slotwise gives your studio a simple page to book, pay a deposit and reschedule — and gives you your evenings back.
</p>
<form action={action} className="flex w-full max-w-md flex-col gap-1.5 rounded-lg border border-border-strong bg-bg p-1.5 [animation:rise-in_.75s_cubic-bezier(.2,.7,.1,1)_.5s_both,focus-glow_1.4s_cubic-bezier(.2,.7,.1,1)_1.5s_both] focus-within:border-primary focus-within:ring-4 focus-within:ring-ring/20 motion-reduce:animate-fade sm:flex-row">
<label htmlFor="hero-email" className="sr-only">Email address</label>
<input id="hero-email" name="email" type="email" required placeholder="you@studio.com" className="h-10 min-w-0 flex-1 bg-transparent px-2.5 text-[14.5px] text-fg outline-none placeholder:text-fg-subtle" />
<button type="submit" className="h-10 rounded bg-primary px-4 text-sm font-medium text-primary-fg transition hover:bg-primary-hover active:scale-[.97]">Start free</button>
</form>
<p className="text-[13px] text-fg-subtle animate-rise [animation-delay:620ms] motion-reduce:animate-fade">14 days of every feature · No card needed</p>
<div className="mt-8 grid w-full gap-3 text-left md:grid-cols-3">
{quotes.map((q, i) => (
<figure
key={q.name}
style={{ "--tilt": (i - 1) * 1.5 + "deg", animationDelay: 850 + i * 140 + "ms" } as CSSProperties}
className={(i > 0 ? "hidden md:grid" : "grid") + " gap-3.5 rounded-lg border border-border bg-surface p-[18px] animate-[card-in_.8s_cubic-bezier(.2,.7,.1,1)_both] motion-reduce:animate-fade"}
>
<blockquote className="text-sm leading-relaxed text-fg">“{q.quote}”</blockquote>
<figcaption className="flex items-center gap-2.5 text-[12.5px] text-fg-muted">
<span className="grid size-7 place-items-center rounded-full bg-primary-soft text-[11px] font-medium text-primary-soft-fg">{q.initials}</span>
<span><b className="block font-medium text-fg">{q.name}</b>{q.role}</span>
</figcaption>
</figure>
))}
</div>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.