Hero Section
Product hero with blur-in headline, CTAs and a dashboard mockup that tilts into place.
The operating system for modern revenue teams
Pipeline, forecasting and customer insight in one beautifully fast workspace.
$48.2k+12.4%
2,431+12.4%
64.8%-1.2%
components/ui/hero-section.tsx
"use client";
import { motion } from "motion/react";
import { ArrowRight, BarChart3, Home, Inbox, Play, Settings, Users } from "lucide-react";
const ease = [0.22, 1, 0.36, 1] as const;
function DashboardMockup() {
return (
<div className="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-[0_30px_80px_-20px_rgba(9,9,11,0.25)]">
<div className="flex items-center gap-1.5 border-b border-zinc-100 px-4 py-3">
<span className="size-2.5 rounded-full bg-zinc-200" />
<span className="size-2.5 rounded-full bg-zinc-200" />
<span className="size-2.5 rounded-full bg-zinc-200" />
<span className="ml-3 h-5 w-48 rounded-md bg-zinc-100" />
</div>
<div className="flex">
<aside className="hidden w-44 shrink-0 space-y-1 border-r border-zinc-100 p-3 sm:block">
{[Home, BarChart3, Users, Inbox, Settings].map((Icon, i) => (
<div key={i} className={`flex items-center gap-2 rounded-lg px-2.5 py-2 text-xs ${i === 1 ? "bg-zinc-900 text-white" : "text-zinc-500"}`}>
<Icon className="size-3.5" />
<span className={`h-2 rounded ${i === 1 ? "w-14 bg-white/60" : "w-16 bg-zinc-200"}`} />
</div>
))}
</aside>
<div className="flex-1 space-y-4 p-5">
<div className="grid grid-cols-3 gap-3">
{["$48.2k", "2,431", "64.8%"].map((v, i) => (
<div key={v} className="rounded-xl border border-zinc-100 p-3 text-left">
<span className="block h-2 w-12 rounded bg-zinc-200" />
<span className="mt-2 block text-sm font-semibold text-zinc-900 sm:text-lg">{v}</span>
<span className={`text-[10px] font-medium ${i === 2 ? "text-rose-500" : "text-emerald-600"}`}>{i === 2 ? "-1.2%" : "+12.4%"}</span>
</div>
))}
</div>
<div className="rounded-xl border border-zinc-100 p-4">
<svg viewBox="0 0 400 120" className="h-28 w-full" preserveAspectRatio="none">
<defs>
<linearGradient id="hero-area" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stopColor="#2563eb" stopOpacity="0.25" />
<stop offset="100%" stopColor="#2563eb" stopOpacity="0" />
</linearGradient>
</defs>
<motion.path
d="M0 95 C40 90 60 70 100 72 S160 40 200 48 S270 20 310 30 S370 10 400 8 L400 120 L0 120 Z"
fill="url(#hero-area)"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.4, duration: 0.8 }}
/>
<motion.path
d="M0 95 C40 90 60 70 100 72 S160 40 200 48 S270 20 310 30 S370 10 400 8"
fill="none"
stroke="#2563eb"
strokeWidth="2.5"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ delay: 0.8, duration: 1.6, ease: "easeInOut" }}
/>
</svg>
</div>
</div>
</div>
</div>
);
}
export function HeroSection() {
return (
<section className="relative isolate w-full overflow-hidden px-6 pb-16 pt-20 text-center">
<div
aria-hidden
className="absolute inset-0 -z-10 [background-image:radial-gradient(#e4e4e7_1px,transparent_1px)] [background-size:22px_22px] [mask-image:radial-gradient(ellipse_at_top,black_20%,transparent_70%)]"
/>
<motion.a
href="#"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease }}
className="group inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white py-1 pl-1 pr-3 text-xs font-medium text-zinc-600 shadow-sm"
>
<span className="rounded-full bg-blue-600 px-2 py-0.5 text-white">New</span>
Workflows 2.0 is here
<ArrowRight className="size-3 transition-transform group-hover:translate-x-0.5" />
</motion.a>
<motion.h1
initial={{ opacity: 0, y: 20, filter: "blur(8px)" }}
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
transition={{ duration: 0.8, delay: 0.1, ease }}
className="mx-auto mt-6 max-w-3xl text-balance text-5xl font-semibold tracking-tight text-zinc-900 sm:text-6xl"
>
The operating system for modern revenue teams
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.25, ease }}
className="mx-auto mt-5 max-w-xl text-lg text-zinc-500"
>
Pipeline, forecasting and customer insight in one beautifully fast workspace.
</motion.p>
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4, ease }}
className="mt-8 flex flex-wrap items-center justify-center gap-3"
>
<button className="group inline-flex items-center gap-2 rounded-full bg-zinc-900 px-6 py-3 text-sm font-medium text-white transition hover:-translate-y-0.5 hover:bg-zinc-800">
Start free trial
<ArrowRight className="size-4 transition-transform group-hover:translate-x-0.5" />
</button>
<button className="inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white px-6 py-3 text-sm font-medium text-zinc-900 transition hover:bg-zinc-50">
<Play className="size-3.5 fill-current" /> Watch demo
</button>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 60, rotateX: 18 }}
animate={{ opacity: 1, y: 0, rotateX: 0 }}
transition={{ duration: 1.1, delay: 0.5, ease }}
style={{ transformPerspective: 1400 }}
className="mx-auto mt-16 max-w-4xl"
>
<DashboardMockup />
</motion.div>
</section>
);
}
export default function Demo() {
return <HeroSection />;
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/hero-section.tsxcomponents/ui/hero-section.tsx"use client"; import { motion } from "motion/react"; import { ArrowRight, BarChart3, Home, Inbox, Play, Settings, Users } from "lucide-react"; const ease = [0.22, 1, 0.36, 1] as const; function DashboardMockup() { return ( <div className="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-[0_30px_80px_-20px_rgba(9,9,11,0.25)]"> <div className="flex items-center gap-1.5 border-b border-zinc-100 px-4 py-3"> <span className="size-2.5 rounded-full bg-zinc-200" /> <span className="size-2.5 rounded-full bg-zinc-200" /> <span className="size-2.5 rounded-full bg-zinc-200" /> <span className="ml-3 h-5 w-48 rounded-md bg-zinc-100" /> </div> <div className="flex"> <aside className="hidden w-44 shrink-0 space-y-1 border-r border-zinc-100 p-3 sm:block"> {[Home, BarChart3, Users, Inbox, Settings].map((Icon, i) => ( <div key={i} className={`flex items-center gap-2 rounded-lg px-2.5 py-2 text-xs ${i === 1 ? "bg-zinc-900 text-white" : "text-zinc-500"}`}> <Icon className="size-3.5" /> <span className={`h-2 rounded ${i === 1 ? "w-14 bg-white/60" : "w-16 bg-zinc-200"}`} /> </div> ))} </aside> <div className="flex-1 space-y-4 p-5"> <div className="grid grid-cols-3 gap-3"> {["$48.2k", "2,431", "64.8%"].map((v, i) => ( <div key={v} className="rounded-xl border border-zinc-100 p-3 text-left"> <span className="block h-2 w-12 rounded bg-zinc-200" /> <span className="mt-2 block text-sm font-semibold text-zinc-900 sm:text-lg">{v}</span> <span className={`text-[10px] font-medium ${i === 2 ? "text-rose-500" : "text-emerald-600"}`}>{i === 2 ? "-1.2%" : "+12.4%"}</span> </div> ))} </div> <div className="rounded-xl border border-zinc-100 p-4"> <svg viewBox="0 0 400 120" className="h-28 w-full" preserveAspectRatio="none"> <defs> <linearGradient id="hero-area" x1="0" x2="0" y1="0" y2="1"> <stop offset="0%" stopColor="#2563eb" stopOpacity="0.25" /> <stop offset="100%" stopColor="#2563eb" stopOpacity="0" /> </linearGradient> </defs> <motion.path d="M0 95 C40 90 60 70 100 72 S160 40 200 48 S270 20 310 30 S370 10 400 8 L400 120 L0 120 Z" fill="url(#hero-area)" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 1.4, duration: 0.8 }} /> <motion.path d="M0 95 C40 90 60 70 100 72 S160 40 200 48 S270 20 310 30 S370 10 400 8" fill="none" stroke="#2563eb" strokeWidth="2.5" initial={{ pathLength: 0 }} animate={{ pathLength: 1 }} transition={{ delay: 0.8, duration: 1.6, ease: "easeInOut" }} /> </svg> </div> </div> </div> </div> ); } export function HeroSection() { return ( <section className="relative isolate w-full overflow-hidden px-6 pb-16 pt-20 text-center"> <div aria-hidden className="absolute inset-0 -z-10 [background-image:radial-gradient(#e4e4e7_1px,transparent_1px)] [background-size:22px_22px] [mask-image:radial-gradient(ellipse_at_top,black_20%,transparent_70%)]" /> <motion.a href="#" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, ease }} className="group inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white py-1 pl-1 pr-3 text-xs font-medium text-zinc-600 shadow-sm" > <span className="rounded-full bg-blue-600 px-2 py-0.5 text-white">New</span> Workflows 2.0 is here <ArrowRight className="size-3 transition-transform group-hover:translate-x-0.5" /> </motion.a> <motion.h1 initial={{ opacity: 0, y: 20, filter: "blur(8px)" }} animate={{ opacity: 1, y: 0, filter: "blur(0px)" }} transition={{ duration: 0.8, delay: 0.1, ease }} className="mx-auto mt-6 max-w-3xl text-balance text-5xl font-semibold tracking-tight text-zinc-900 sm:text-6xl" > The operating system for modern revenue teams </motion.h1> <motion.p initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, delay: 0.25, ease }} className="mx-auto mt-5 max-w-xl text-lg text-zinc-500" > Pipeline, forecasting and customer insight in one beautifully fast workspace. </motion.p> <motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, delay: 0.4, ease }} className="mt-8 flex flex-wrap items-center justify-center gap-3" > <button className="group inline-flex items-center gap-2 rounded-full bg-zinc-900 px-6 py-3 text-sm font-medium text-white transition hover:-translate-y-0.5 hover:bg-zinc-800"> Start free trial <ArrowRight className="size-4 transition-transform group-hover:translate-x-0.5" /> </button> <button className="inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white px-6 py-3 text-sm font-medium text-zinc-900 transition hover:bg-zinc-50"> <Play className="size-3.5 fill-current" /> Watch demo </button> </motion.div> <motion.div initial={{ opacity: 0, y: 60, rotateX: 18 }} animate={{ opacity: 1, y: 0, rotateX: 0 }} transition={{ duration: 1.1, delay: 0.5, ease }} style={{ transformPerspective: 1400 }} className="mx-auto mt-16 max-w-4xl" > <DashboardMockup /> </motion.div> </section> ); } export default function Demo() { return <HeroSection />; }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.