An AI inbox's landing page. Scroll the page: the bar docks into a pill, and "Get Morrow" flies up into it as the hero passes under. Scroll back up and it flies home.
registry/demos/handoff-nav-demo.tsx
"use client";
import * as React from "react";
import { HandoffNav, HandoffNavCta, HandoffNavProvider } from "@/components/ui/handoff-nav";
const links = [
{ label: "Product", href: "#product" },
{ label: "Pricing", href: "#pricing" },
{ label: "Changelog", href: "#changelog" },
{ label: "Docs", href: "#docs" },
];
const threads = [
{
from: "Priya Raman",
subject: "Q3 board deck: one more pass?",
preview: "Happy to tighten the retention slide before Thursday. I moved the cohort chart up and…",
time: "6:12",
},
{
from: "Lisbon offsite",
subject: "Flights are booked, hotel needs a headcount",
preview: "The venue holds rooms until Friday noon. Can you confirm 14 or should we keep 16?",
time: "5:48",
},
{
from: "Tomás at Harbor",
subject: "Renewal terms for 2027",
preview: "Attaching the redline. The only open item is the uptime credit in section 4.2.",
time: "Yesterday",
},
{
from: "Mei Chen",
subject: "Coffee next week?",
preview: "I'm in town Tuesday to Thursday. Would love to hear how the launch went.",
time: "Yesterday",
},
];
const steps = [
{
title: "It reads overnight",
body: "Every new thread is read once, with the history behind it: what you promised, who's waiting, what changed.",
},
{
title: "It drafts in your voice",
body: "Replies sound like the last hundred you sent. Short where you're short, warm where you're warm.",
},
{
title: "You send before coffee",
body: "Open a thread, change a word if you want, press send. Most mornings take under ten minutes.",
},
];
function Mark() {
return (
<svg viewBox="0 0 24 24" aria-hidden className="size-6">
<circle cx="12" cy="12" r="10" className="fill-zinc-950 dark:fill-white" />
<path d="M7 13.5a5 5 0 0 0 10 0" className="fill-none stroke-white stroke-2 dark:stroke-zinc-950" />
<circle cx="12" cy="8.5" r="1.6" className="fill-violet-400" />
</svg>
);
}
export function HandoffNavDemo() {
const scrollRef = React.useRef<HTMLDivElement>(null);
return (
<div className="relative w-full overflow-hidden rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950">
<div ref={scrollRef} className="@container relative h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain">
<HandoffNavProvider>
<HandoffNav
containerRef={scrollRef}
className="[--handoff-nav-width:64rem]"
logo={
<a
href="#"
className="flex items-center gap-2 rounded-md text-[15px] font-semibold tracking-tight text-zinc-950 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-zinc-950 dark:text-white dark:focus-visible:outline-white"
>
<Mark />
Morrow
</a>
}
links={links}
action={{ label: "Log in", href: "#login" }}
/>
<main className="mx-auto max-w-5xl px-4 pb-16 @xl:px-6">
<section className="flex flex-col items-center pt-10 text-center @xl:pt-14">
<a
href="#changelog"
className="inline-flex items-center gap-2 rounded-full border border-zinc-950/10 bg-white py-1 pr-3 pl-1 text-[13px] text-zinc-700 shadow-sm dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-300"
>
<span className="rounded-full bg-violet-600 px-2 py-0.5 text-[11px] font-semibold text-white">New</span>
Drafts now match your sign-off
<span aria-hidden>→</span>
</a>
<h1 className="mt-6 max-w-2xl text-4xl/[1.05] font-semibold tracking-[-0.035em] text-balance text-zinc-950 @xl:text-6xl/[1.02] dark:text-white">
Your inbox, answered{" "}
<span className="text-zinc-400 dark:text-zinc-500">before you open it.</span>
</h1>
<p className="mt-5 max-w-md text-[15px]/6 text-pretty text-zinc-600 dark:text-zinc-400">
Morrow reads every thread overnight and leaves a reply drafted in your voice. You read it,
change a word, and send.
</p>
<div className="mt-8 flex items-center gap-5">
<a
href="#product"
className="text-sm font-medium text-zinc-700 underline-offset-4 hover:underline dark:text-zinc-300"
>
See a sample morning
</a>
<HandoffNavCta href="#signup">Get Morrow</HandoffNavCta>
</div>
</section>
<section id="product" className="mt-14">
<div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white shadow-[0_24px_60px_-30px_rgb(9_9_11/0.35)] dark:border-white/10 dark:bg-zinc-900">
<div className="flex items-center justify-between border-b border-zinc-950/[0.06] px-4 py-3 dark:border-white/[0.06]">
<p className="text-[13px] font-medium text-zinc-900 dark:text-zinc-100">Inbox</p>
<p className="text-[12px] text-zinc-500">4 drafts ready · 7:02 am</p>
</div>
<ul className="divide-y divide-zinc-950/[0.06] dark:divide-white/[0.06]">
{threads.map((thread) => (
<li key={thread.subject} className="flex items-start gap-3 px-4 py-3.5">
<span className="mt-0.5 grid size-8 shrink-0 place-items-center rounded-full bg-zinc-100 text-[12px] font-semibold text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">
{thread.from
.split(" ")
.map((word) => word[0])
.slice(0, 2)
.join("")}
</span>
<div className="min-w-0 flex-1 text-left">
<div className="flex items-baseline justify-between gap-3">
<p className="truncate text-[13px] font-medium text-zinc-900 dark:text-zinc-100">
{thread.from}
</p>
<p className="shrink-0 text-[11px] text-zinc-400 tabular-nums">{thread.time}</p>
</div>
<p className="truncate text-[13px] text-zinc-700 dark:text-zinc-300">{thread.subject}</p>
<p className="truncate text-[12px] text-zinc-500">{thread.preview}</p>
</div>
<span className="mt-1 hidden shrink-0 rounded-full bg-violet-50 px-2 py-0.5 text-[11px] font-medium text-violet-700 @xl:inline dark:bg-violet-500/15 dark:text-violet-300">
Draft ready
</span>
</li>
))}
</ul>
</div>
</section>
<section className="mt-20">
<h2 className="max-w-md text-2xl/tight font-semibold tracking-tight text-zinc-950 dark:text-white">
How a morning goes
</h2>
<ol className="mt-6 grid gap-px overflow-hidden rounded-2xl border border-zinc-950/10 bg-zinc-950/10 @2xl:grid-cols-3 dark:border-white/10 dark:bg-white/10">
{steps.map((step, index) => (
<li key={step.title} className="bg-white p-5 dark:bg-zinc-900">
<p className="text-[12px] text-zinc-400 tabular-nums">{index + 1} of 3</p>
<p className="mt-6 text-[15px] font-medium text-zinc-900 dark:text-zinc-100">{step.title}</p>
<p className="mt-1.5 text-[13px]/5 text-zinc-500 dark:text-zinc-400">{step.body}</p>
</li>
))}
</ol>
</section>
<section className="mt-20 grid gap-8 @2xl:grid-cols-[1fr_1.2fr] @2xl:items-end">
<h2 className="text-2xl/tight font-semibold tracking-tight text-zinc-950 dark:text-white">
Nothing sends without you
</h2>
<p className="text-[15px]/6 text-zinc-600 dark:text-zinc-400">
Drafts wait in the thread until you press send. Morrow never replies on its own, never
forwards, and never reads folders you haven't shared with it.
</p>
</section>
<footer className="mt-24 flex items-center justify-between border-t border-zinc-950/10 pt-6 text-[13px] text-zinc-500 dark:border-white/10">
<span>© 2026 Morrow Labs</span>
<span>Privacy · Terms</span>
</footer>
</main>
</HandoffNavProvider>
</div>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/handoff-nav#handoff-nav-demo