All Taste components
Animated heroes
Hero — centered big type (motion)
The two-line headline rises in word by word out of a blur, then a brand underline draws under the second line.
word-by-word rise · underline draw · pulsing status dot · staggered ticks
Invoicing for freelancersInvoices out.
Invoices out.
Money in.
Fernbill sends the invoice, chases it politely and tells you the moment you're paid — so you can get back to the work.
- No card needed
- 5 free invoices a month
- Cancel any time
import { ArrowRight, Check } from "lucide-react";
/* Add once to globals.css:
@keyframes word-rise { from { opacity: 0; transform: translateY(.42em) rotate(2deg); filter: blur(8px); } }
@keyframes underline-draw { from { transform: scaleX(0); } }
@keyframes status-ping { 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--tst-primary) 6%, transparent); } }
*/
const hero = {
status: "Invoicing for freelancers",
title: ["Invoices out.", "Money in."],
lead: "Fernbill sends the invoice, chases it politely and tells you the moment you’re paid — so you can get back to the work.",
primary: { label: "Start free", href: "/signup" },
secondary: { label: "Watch a 2-min tour", href: "/tour" },
reassurances: ["No card needed", "5 free invoices a month", "Cancel any time"],
};
/** Splits a line into words that rise in one after another, starting at word number "start". */
function Words({ text, start = 0 }: { text: string; start?: number }) {
return text.split(" ").map((w, i) => (
<span key={i}>
{i > 0 ? " " : null}
<span
className="inline-block animate-[word-rise_.8s_cubic-bezier(.2,.7,.1,1)_both] motion-reduce:animate-fade"
style={{ animationDelay: 120 + (start + i) * 90 + "ms" }}
>
{w}
</span>
</span>
));
}
export function HeroCenteredTypeMotion() {
return (
<section aria-labelledby="hero-title" className="relative isolate overflow-hidden bg-bg font-sans">
<div aria-hidden className="absolute left-1/2 top-[-40%] -z-10 aspect-square w-[70%] -translate-x-1/2 rounded-full bg-[radial-gradient(closest-side,color-mix(in_srgb,var(--tst-primary)_14%,transparent),transparent)]" />
<div className="mx-auto flex max-w-5xl flex-col items-center px-4 py-16 text-center sm:px-6 sm:py-24">
<p className="inline-flex items-center gap-2 text-[13px] font-medium text-fg-muted animate-fade">
<span aria-hidden className="size-[7px] rounded-full bg-primary shadow-[0_0_0_4px_color-mix(in_srgb,var(--tst-primary)_20%,transparent)] animate-[status-ping_2.6s_ease-in-out_1.2s_infinite] motion-reduce:animate-none" />
{hero.status}
</p>
<h1 id="hero-title" className="mt-6 text-balance text-5xl font-semibold leading-[0.96] tracking-[-0.055em] text-fg sm:text-7xl lg:text-8xl">
<Words text={hero.title[0]} />
<br />
<span className="relative text-primary">
<Words text={hero.title[1]} start={2} />
<span aria-hidden className="absolute -z-10 bottom-[.1em] -left-[.02em] right-[.02em] h-[.16em] origin-left rounded-full bg-primary/20 animate-[underline-draw_.9s_cubic-bezier(.2,.7,.1,1)_.95s_both] motion-reduce:animate-fade" />
</span>
</h1>
<p className="mt-6 max-w-xl text-pretty text-base leading-relaxed text-fg-muted sm:text-lg animate-rise [animation-delay:530ms] motion-reduce:animate-fade">{hero.lead}</p>
<div className="mt-8 flex flex-wrap justify-center gap-3 animate-rise [animation-delay:620ms] motion-reduce:animate-fade">
<a href={hero.primary.href} className="inline-flex h-11 items-center gap-1.5 rounded bg-primary px-5 text-[15px] font-medium text-primary-fg transition hover:bg-primary-hover active:scale-[.97]">
{hero.primary.label} <ArrowRight className="size-4" aria-hidden />
</a>
<a href={hero.secondary.href} className="inline-flex h-11 items-center rounded border border-border-strong bg-bg px-5 text-[15px] font-medium text-fg transition hover:bg-surface active:scale-[.97]">
{hero.secondary.label}
</a>
</div>
<ul className="mt-6 flex flex-wrap justify-center gap-x-6 gap-y-2 text-[13.5px] text-fg-muted">
{hero.reassurances.map((r, i) => (
<li key={r} className="inline-flex items-center gap-1.5 animate-rise motion-reduce:animate-fade" style={{ animationDelay: 710 + i * 90 + "ms" }}>
<Check className="size-4 text-primary" aria-hidden />{r}
</li>
))}
</ul>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.