feat: choreograph hero devices on scroll

This commit is contained in:
2026-08-21 00:08:50 -04:00
parent ec89696c48
commit b52eff02e1
2 changed files with 100 additions and 22 deletions
+24 -22
View File
@@ -96,7 +96,7 @@ const accessFit = [
export default function HomePage() {
return (
<>
<section className="relative isolate min-h-[calc(100svh-4rem)] overflow-hidden px-4 pt-14 sm:px-6 sm:pt-20 lg:pt-24">
<section className="relative isolate min-h-[calc(100svh-4rem)] overflow-x-clip px-4 pt-14 sm:px-6 sm:pt-20 lg:pt-24">
<div className="bg-primary-light/55 absolute top-0 left-1/2 -z-10 h-[38rem] w-[68rem] max-w-[120vw] -translate-x-1/2 rounded-full blur-3xl" />
<div className="mx-auto max-w-7xl text-center">
<div className="animate-fade-in-up flex items-center justify-center gap-3">
@@ -127,29 +127,31 @@ export default function HomePage() {
</div>
</div>
<div className="relative mx-auto mt-12 h-[44rem] max-w-5xl sm:h-[58rem] lg:mt-8 lg:h-[64rem]">
<div className="absolute top-24 left-1/2 w-[17rem] -translate-x-[86%] -rotate-6 sm:top-28 sm:w-[22rem] lg:top-32 lg:w-[26rem]">
<ProductDevice
src="/screenshots/product-bezel-2026-08-20/summary-natural.webp"
darkSrc="/screenshots/product-bezel-2026-08-20/summary-natural-dark.webp"
alt="Medscribe visit summary in a natural titanium iPhone product bezel"
className="pitch-device-motion pitch-device-drift-left"
sizes="(min-width: 1024px) 416px, (min-width: 640px) 352px, 272px"
priority
/>
</div>
<div className="absolute top-0 left-1/2 z-10 w-[18rem] -translate-x-[14%] rotate-5 sm:w-[23rem] lg:w-[28rem]">
<ProductDevice
src="/screenshots/product-bezel-2026-08-20/home-black.webp"
darkSrc="/screenshots/product-bezel-2026-08-20/home-black-dark.webp"
alt="Medscribe home screen in a black titanium iPhone product bezel"
className="pitch-device-motion pitch-device-drift-right"
sizes="(min-width: 1024px) 448px, (min-width: 640px) 368px, 288px"
priority
/>
<div className="pitch-hero-stage relative mx-auto mt-12 h-[190svh] max-w-5xl lg:mt-8">
<div className="sticky top-16 h-[calc(100svh-4rem)]">
<div className="absolute top-24 left-1/2 w-[17rem] -translate-x-[86%] -rotate-6 sm:top-28 sm:w-[22rem] lg:top-32 lg:w-[26rem]">
<ProductDevice
src="/screenshots/product-bezel-2026-08-20/summary-natural.webp"
darkSrc="/screenshots/product-bezel-2026-08-20/summary-natural-dark.webp"
alt="Medscribe visit summary in a natural titanium iPhone product bezel"
className="pitch-hero-phone pitch-hero-phone-left"
sizes="(min-width: 1024px) 416px, (min-width: 640px) 352px, 272px"
priority
/>
</div>
<div className="absolute top-0 left-1/2 z-10 w-[18rem] -translate-x-[14%] rotate-5 sm:w-[23rem] lg:w-[28rem]">
<ProductDevice
src="/screenshots/product-bezel-2026-08-20/home-black.webp"
darkSrc="/screenshots/product-bezel-2026-08-20/home-black-dark.webp"
alt="Medscribe home screen in a black titanium iPhone product bezel"
className="pitch-hero-phone pitch-hero-phone-right"
sizes="(min-width: 1024px) 448px, (min-width: 640px) 368px, 288px"
priority
/>
</div>
</div>
</div>
<p className="text-muted-foreground relative z-20 mx-auto pb-8 text-center text-xs font-medium tracking-wide">
<p className="text-muted-foreground relative z-20 mx-auto mt-4 pb-8 text-center text-xs font-medium tracking-wide">
Illustrative profile · Medscribe interface
</p>
</section>
+76
View File
@@ -129,6 +129,61 @@
--pitch-exit-rotate: -1.25deg;
}
/* The opening pair gets its own scroll story: meet, settle, then peel apart. */
.pitch-hero-stage {
perspective: 1600px;
}
.pitch-hero-phone {
backface-visibility: hidden;
transform-origin: center;
transform-style: preserve-3d;
}
.pitch-hero-phone-left {
--hero-start-x: -4rem;
--hero-meet-x: 1.25rem;
--hero-exit-x: -5rem;
--hero-start-rotate: -6deg;
--hero-meet-rotate: 1.5deg;
--hero-exit-rotate: -5deg;
}
.pitch-hero-phone-right {
--hero-start-x: 4rem;
--hero-meet-x: -1rem;
--hero-exit-x: 5rem;
--hero-start-rotate: 6deg;
--hero-meet-rotate: -1.5deg;
--hero-exit-rotate: 5deg;
}
@keyframes pitch-hero-phone-scroll {
0% {
opacity: 0.38;
transform: translate3d(var(--hero-start-x), 4rem, -7rem) scale(0.86)
rotate(var(--hero-start-rotate));
}
30% {
opacity: 1;
transform: translate3d(0, 0.75rem, 0) scale(0.985) rotate(0deg);
}
58% {
opacity: 1;
transform: translate3d(var(--hero-meet-x), -2rem, 4rem) scale(1.04)
rotate(var(--hero-meet-rotate));
}
78% {
opacity: 1;
transform: translate3d(0, -3.5rem, 1rem) scale(1.015) rotate(0deg);
}
100% {
opacity: 0.76;
transform: translate3d(var(--hero-exit-x), -10rem, -4rem) scale(0.94)
rotate(var(--hero-exit-rotate));
}
}
@keyframes pitch-device-scroll {
0% {
opacity: 0.42;
@@ -172,6 +227,14 @@
}
}
@supports (animation-timeline: scroll()) {
.pitch-hero-phone {
animation: pitch-hero-phone-scroll linear both;
animation-timeline: scroll(root block);
animation-range: 0px 1500px;
}
}
@supports (animation-timeline: view()) {
.pitch-device-motion {
animation: pitch-device-scroll linear both;
@@ -194,6 +257,18 @@
/* Keep the product choreography visible on phones without crowding the copy. */
@media (max-width: 639px) {
.pitch-hero-phone-left {
--hero-start-x: -1.5rem;
--hero-meet-x: 0.5rem;
--hero-exit-x: -1.75rem;
}
.pitch-hero-phone-right {
--hero-start-x: 1.5rem;
--hero-meet-x: -0.5rem;
--hero-exit-x: 1.75rem;
}
.pitch-device-motion {
--pitch-enter-y: 4rem;
--pitch-exit-y: -3rem;
@@ -229,6 +304,7 @@
}
.pitch-device-motion,
.pitch-hero-phone,
.pitch-copy-motion,
.pitch-card-motion {
animation: none !important;