All Taste components
Animated heroes
Hero — minimal editorial (motion)
The hairline rules draw across the page and each line of the huge headline slides up out of its own mask.
rules draw · masked line-by-line reveal · meta fades · arrow nudges on the CTA
Do the work.We'll handlethe money.
01 / Invoicing
Invoices, reminders and payouts for freelancers and small studios — quiet, quick and out of your way.
Start freeimport { ArrowRight } from "lucide-react";
/* Add once to globals.css:
@keyframes rule-draw { from { transform: scaleX(0); } }
@keyframes line-up { from { transform: translateY(135%); } }
@keyframes nudge { 0%, 55%, 100% { translate: 0 0; } 25% { translate: 4px 0; } }
*/
const meta = ["Fernbill — Invoicing", "For people who make things", "Est. 2026"];
const lines = [
{ text: "Do the work.", muted: false },
{ text: "We’ll handle", muted: true },
{ text: "the money.", muted: false },
];
const rule = "absolute inset-x-0 h-px origin-left animate-[rule-draw_1.2s_cubic-bezier(.2,.7,.1,1)_both] motion-reduce:animate-fade";
export function HeroEditorialMotion() {
return (
<section aria-labelledby="hero-title" className="bg-bg font-sans">
<div className="mx-auto max-w-6xl px-4 pb-16 pt-10 sm:px-6">
<div className="relative flex flex-wrap justify-between gap-x-6 gap-y-2 pb-3.5 text-[12.5px] text-fg-muted">
{meta.map((m, i) => <span key={m} className="animate-fade motion-reduce:animate-fade" style={{ animationDelay: 300 + i * 120 + "ms", animationDuration: ".8s" }}>{m}</span>)}
<span aria-hidden className={rule + " -bottom-px bg-fg [animation-delay:100ms]"} />
</div>
<h1 id="hero-title" className="my-10 text-[clamp(3.25rem,11vw,9.5rem)] font-medium leading-[0.9] tracking-[-0.06em] text-fg sm:my-14">
{lines.map((l, i) => (
// Each line rises out of its own mask; the padding keeps descenders from being clipped.
<span key={l.text} className="-mb-[.3em] -mt-[.1em] block overflow-hidden pb-[.3em] pt-[.1em]">
<span
className={"block animate-[line-up_1.1s_cubic-bezier(.16,1,.3,1)_both] motion-reduce:animate-fade " + (l.muted ? "text-fg-subtle" : "")}
style={{ animationDelay: 350 + i * 140 + "ms" }}
>
{l.text}
</span>
</span>
))}
</h1>
<div className="relative grid items-end gap-6 pt-4 sm:grid-cols-3">
<span aria-hidden className={rule + " -top-px bg-border [animation-delay:850ms]"} />
<span className="hidden text-[13px] text-fg-subtle animate-rise [animation-delay:890ms] motion-reduce:animate-fade sm:block">01 / Invoicing</span>
<p className="text-pretty text-[14.5px] leading-relaxed text-fg-muted animate-rise [animation-delay:980ms] motion-reduce:animate-fade">
Invoices, reminders and payouts for freelancers and small studios — quiet, quick and out of your way.
</p>
<a href="/signup" className="inline-flex w-fit items-center gap-2 border-b border-current pb-0.5 text-sm font-medium text-fg transition animate-rise [animation-delay:1070ms] hover:text-primary motion-reduce:animate-fade sm:justify-self-end">
Start free <ArrowRight className="size-4 animate-[nudge_2.6s_ease-in-out_2.2s_infinite] motion-reduce:animate-none" aria-hidden />
</a>
</div>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.