feat: choreograph hero devices on scroll
This commit is contained in:
@@ -96,7 +96,7 @@ const accessFit = [
|
|||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
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="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="mx-auto max-w-7xl text-center">
|
||||||
<div className="animate-fade-in-up flex items-center justify-center gap-3">
|
<div className="animate-fade-in-up flex items-center justify-center gap-3">
|
||||||
@@ -127,29 +127,31 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</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="pitch-hero-stage relative mx-auto mt-12 h-[190svh] max-w-5xl lg:mt-8">
|
||||||
<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]">
|
<div className="sticky top-16 h-[calc(100svh-4rem)]">
|
||||||
<ProductDevice
|
<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]">
|
||||||
src="/screenshots/product-bezel-2026-08-20/summary-natural.webp"
|
<ProductDevice
|
||||||
darkSrc="/screenshots/product-bezel-2026-08-20/summary-natural-dark.webp"
|
src="/screenshots/product-bezel-2026-08-20/summary-natural.webp"
|
||||||
alt="Medscribe visit summary in a natural titanium iPhone product bezel"
|
darkSrc="/screenshots/product-bezel-2026-08-20/summary-natural-dark.webp"
|
||||||
className="pitch-device-motion pitch-device-drift-left"
|
alt="Medscribe visit summary in a natural titanium iPhone product bezel"
|
||||||
sizes="(min-width: 1024px) 416px, (min-width: 640px) 352px, 272px"
|
className="pitch-hero-phone pitch-hero-phone-left"
|
||||||
priority
|
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]">
|
</div>
|
||||||
<ProductDevice
|
<div className="absolute top-0 left-1/2 z-10 w-[18rem] -translate-x-[14%] rotate-5 sm:w-[23rem] lg:w-[28rem]">
|
||||||
src="/screenshots/product-bezel-2026-08-20/home-black.webp"
|
<ProductDevice
|
||||||
darkSrc="/screenshots/product-bezel-2026-08-20/home-black-dark.webp"
|
src="/screenshots/product-bezel-2026-08-20/home-black.webp"
|
||||||
alt="Medscribe home screen in a black titanium iPhone product bezel"
|
darkSrc="/screenshots/product-bezel-2026-08-20/home-black-dark.webp"
|
||||||
className="pitch-device-motion pitch-device-drift-right"
|
alt="Medscribe home screen in a black titanium iPhone product bezel"
|
||||||
sizes="(min-width: 1024px) 448px, (min-width: 640px) 368px, 288px"
|
className="pitch-hero-phone pitch-hero-phone-right"
|
||||||
priority
|
sizes="(min-width: 1024px) 448px, (min-width: 640px) 368px, 288px"
|
||||||
/>
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</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
|
Illustrative profile · Medscribe interface
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -129,6 +129,61 @@
|
|||||||
--pitch-exit-rotate: -1.25deg;
|
--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 {
|
@keyframes pitch-device-scroll {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0.42;
|
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()) {
|
@supports (animation-timeline: view()) {
|
||||||
.pitch-device-motion {
|
.pitch-device-motion {
|
||||||
animation: pitch-device-scroll linear both;
|
animation: pitch-device-scroll linear both;
|
||||||
@@ -194,6 +257,18 @@
|
|||||||
|
|
||||||
/* Keep the product choreography visible on phones without crowding the copy. */
|
/* Keep the product choreography visible on phones without crowding the copy. */
|
||||||
@media (max-width: 639px) {
|
@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-device-motion {
|
||||||
--pitch-enter-y: 4rem;
|
--pitch-enter-y: 4rem;
|
||||||
--pitch-exit-y: -3rem;
|
--pitch-exit-y: -3rem;
|
||||||
@@ -229,6 +304,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pitch-device-motion,
|
.pitch-device-motion,
|
||||||
|
.pitch-hero-phone,
|
||||||
.pitch-copy-motion,
|
.pitch-copy-motion,
|
||||||
.pitch-card-motion {
|
.pitch-card-motion {
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user