An interiors studio's site in a phone frame. The links fold into a menu that grows out of the pill, and "Book a call" shares the pill with it once caught; the hero copy gets wider padding through `data-[placement=hero]:`.
registry/demos/handoff-nav-studio-demo.tsx
"use client";
import * as React from "react";
import { HandoffNav, HandoffNavCta, HandoffNavProvider } from "@/components/ui/handoff-nav";
const photo = (id: string) =>
`https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=720&h=900&q=80`;
const work = [
{ id: "1638193399915-e22226710f2b", title: "Rua das Flores", kind: "Apartment, 92 m²", year: "2026" },
{ id: "1630232782797-7b48bf7125e4", title: "Casa Alfama", kind: "Kitchen and dining", year: "2025" },
{ id: "1633944095397-878622ebc01c", title: "Quinta Norte", kind: "Guest suite", year: "2025" },
{ id: "1633429083812-2f9c1ef8738d", title: "Atelier Graça", kind: "Reading room", year: "2024" },
];
const services = ["Whole homes", "Kitchens and joinery", "Lighting plans", "Furniture sourcing"];
export function HandoffNavStudioDemo() {
const scrollRef = React.useRef<HTMLDivElement>(null);
return (
// The studio's site at phone width: the links fold into the menu, and the caught button
// shares the pill with it.
<div className="flex w-full justify-center">
<div className="w-full max-w-[390px] overflow-hidden rounded-[2rem] border border-stone-300 bg-[#f6f3ee] shadow-[0_30px_60px_-30px_rgb(28_25_23/0.45)] dark:border-stone-700 dark:bg-stone-950">
<div ref={scrollRef} className="relative h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain">
<HandoffNavProvider>
<HandoffNav
containerRef={scrollRef}
surfaceClassName="bg-[#f6f3ee]/85 dark:bg-stone-900/85"
logo={
<a
href="#"
className="rounded-sm font-serif text-[17px] tracking-tight text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-stone-900 dark:text-stone-100 dark:focus-visible:outline-white"
>
Atelier Mora
</a>
}
links={[
{ label: "Work", href: "#work" },
{ label: "Studio", href: "#studio" },
{ label: "Journal", href: "#journal" },
{ label: "Contact", href: "#contact" },
]}
action={{ label: "hello@ateliermora.pt", href: "#contact" }}
/>
<main className="px-5 pb-12">
<section className="pt-8">
<p className="text-[13px] text-stone-500">Interiors · Lisbon</p>
<h1 className="mt-4 font-serif text-[40px]/[1.02] tracking-[-0.02em] text-stone-900 dark:text-stone-100">
Rooms that hold <em className="text-stone-500">still.</em>
</h1>
<p className="mt-5 text-[15px]/6 text-stone-600 dark:text-stone-400">
We design homes around the light they already have, and finish them with things made a few
streets away.
</p>
<div className="mt-7 flex items-center gap-4">
<HandoffNavCta
href="#contact"
className="bg-stone-900 hover:bg-stone-700 data-[placement=hero]:px-6 dark:bg-stone-100 dark:text-stone-900 dark:hover:bg-stone-300"
>
Book a call
</HandoffNavCta>
<a href="#work" className="text-sm text-stone-700 underline underline-offset-4 dark:text-stone-300">
See the work
</a>
</div>
</section>
<section id="work" className="mt-12 space-y-8">
{work.map((item) => (
<figure key={item.title}>
<div className="aspect-[4/5] overflow-hidden rounded-xl bg-stone-200 dark:bg-stone-800">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={photo(item.id)} alt={`${item.title}, ${item.kind}`} className="size-full object-cover" />
</div>
<figcaption className="mt-3 flex items-baseline justify-between text-[13px]">
<span className="font-serif text-[17px] text-stone-900 dark:text-stone-100">{item.title}</span>
<span className="text-stone-500">
{item.kind} · {item.year}
</span>
</figcaption>
</figure>
))}
</section>
<section id="studio" className="mt-14 border-t border-stone-900/10 pt-8 dark:border-white/10">
<h2 className="font-serif text-2xl tracking-tight text-stone-900 dark:text-stone-100">What we take on</h2>
<ul className="mt-4 divide-y divide-stone-900/10 dark:divide-white/10">
{services.map((service) => (
<li key={service} className="py-3 text-[15px] text-stone-700 dark:text-stone-300">
{service}
</li>
))}
</ul>
</section>
<footer id="contact" className="mt-14 text-[13px]/6 text-stone-500">
Rua do Benformoso 41, Lisbon
<br />
Two new homes a season. Next opening: March.
</footer>
</main>
</HandoffNavProvider>
</div>
</div>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/handoff-nav#handoff-nav-studio-demo