Component
Mobile Carousel Hero
Full-section hero with a 3D coverflow carousel of app screens inside a device frame. Screens auto-advance on a timer, pause on hover, and can be clicked to jump to any position. Supports dark and light themes. Responsive — peek cards visible on all screen sizes.
Preview
This component uses Framer Motion animations and requires real images — open the interactive preview to see it in action.
Open interactive preview →How it works
The carousel uses an unbounded tick integer as its state rather than a circular index. Each 3D slot is keyed by tick + offset — a stable key that never jumps, so Framer Motion always transitions each card exactly one position at a time, producing true coverflow depth without the cross-screen sweep artifact.
The center slot (offset 0) is rendered in the 3D layer at opacity: 0 so the handoff between the 3D layer and the device viewport is seamless — same spring, same physics. Depth is communicated via brightness() filter (never opacity) so cards remain fully opaque and don't bleed through each other.
Coverflow depth tiers
| Offset | X position | rotateY | Brightness | Blur |
|---|---|---|---|---|
| 0 (active) | 0 — inside device | 0° | 100% (full) | 0px |
| ±1 | ±190px | ±52° | 62% | 1px |
| ±2 | ±350px | ±64° | 36% | 3px |
| ±3 | ±490px | ±72° | 22% | 6px |
Sanity fields
Available as mobileCarouselHero in the Page builder. The Screens field uses a grid layout in Studio — drag-select multiple portrait images at once for bulk upload. Portrait ratio (9:19) works best.
| Prop | Type | Default | Description |
|---|---|---|---|
| headline | string | — | Main headline. Rendered in Cal Sans 600. Required. |
| eyebrow | string | — | Small uppercase label above the headline in Electric blue. |
| subhead | text | — | Supporting copy below the headline. |
| screens | image[] | — | App screen images. Min 1. Bulk upload via drag-and-drop in Studio. Stores dimensions + LQIP automatically. |
| theme | string | "dark" | "dark" or "light". Controls section background, text colors, and device frame. |
Interaction model
- Auto-advances every 4.5 s when idle
- Hovering a peek card pauses the timer; mouse-leave resumes it
- Clicking a peek card advances it to center and pauses the timer
- Dot indicators show active screen; click any dot for shortest-path navigation
- Respects prefers-reduced-motion — skips spring animation, uses simple fade
Usage
import { MobileCarouselHero } from '@/components/sections/mobile-carousel-hero'
<MobileCarouselHero
block={{
_type: 'mobileCarouselHero',
headline: 'Your photography, always in your pocket',
eyebrow: 'Mobile-First Experience',
subhead: 'Browse collections, save favourites, share the work.',
theme: 'dark',
screens: [
{ _type: 'image', asset: { _id: '…', url: '…', metadata: { lqip: '…', dimensions: { width: 600, height: 1300, aspectRatio: 0.46 } } } },
// add as many screens as needed
],
}}
/>// Via Sanity BlockRenderer — no extra code needed.
// Add to a Page document in Studio:
// Sections → Add item → Mobile Carousel Hero