About this animation
An expandable booking carousel with a shared image transition and configurable items, typefaces, surface color, and text color.
Why this one
The artwork, card, and price surface share one reversible expansion progress instead of cutting between two screens. The price pill grows into the cream detail surface while its title and price travel into place. Scrolling then collapses the illustrated hero, keeping the vehicle name visible in the detail header. The booking button samples its color continuously from adjacent cards.
How it works
onLayoutmeasures the host container. Cards occupy 62% of its width and 48% of its height.- A continuous shared carousel position drives perspective, scale, artwork parallax, neighbor shading, and button color.
- Gesture Handler races a horizontal pan against a tap. Press feedback has a separate shared value.
- One expansion progress drives the card bounds, hero, cream surface, title size, price size, and staggered detail content. A 650 ms
(0.65, 0, 0.35, 1)curve runs forward or backward from the current position. - A native scroll view drives the hero collapse and half-speed image parallax on the UI thread. The expanded viewport reserves its full height and the panel moves with a transform, so scrolling never resizes the scroll view or triggers React renders. A counter-transform keeps text movement one-to-one with the native scroll offset; fixed bottom padding keeps the content reachable while the panel extends below the host.
- A downward drag on the hero reverses expansion only when the detail scroll is at the top. The hero detector does not cover the scrollable body. The back button remains available at any scroll offset.
- Ref methods let the host orchestrate a demo without placing autoplay logic in the component.
Install
npx expo install expo-image react-native-gesture-handler react-native-reanimated react-native-workletsWrap your app in GestureHandlerRootView. Configure Reanimated and Worklets using the instructions for your Expo SDK. No new native modules or remote assets are needed in this project's Expo Go setup.
Usage
import { CardDetailExpand } from './card-detail-expand';
<CardDetailExpand
items={[
{
id: 'coastal',
name: 'Coastal Camper',
image: require('./assets/coastal.jpg'),
price: 96,
color: '#E8783C',
description: ['A cozy base for your next trip.', 'Fresh linens and a stocked kitchen included.'],
},
]}
onBook={(item) => startCheckout(item.id)}
/>const ref = useRef<CardDetailExpandRef>(null);
<CardDetailExpand
ref={ref}
items={campers}
title="Choose your camper"
subtitle="Big Sur · 7 nights"
nights={7}
duration={650}
cardWidthRatio={0.62}
surfaceColor="#FFF4E2"
textColor="#302537"
fontFamily="YourInterfaceFont"
mediumFontFamily="YourMediumFont"
titleFontFamily="YourEditorialFont"
monoFontFamily="YourNumberFont"
onInteraction={stopAutoplay}
/>;
// Call from a host event or timer:
ref.current?.goTo(2);
ref.current?.open();Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
items | readonly CamperCard[] | Required | Unique IDs, names, artwork, nightly prices, accents, descriptions, optional features and rating |
title | string | Choose your stay | Carousel heading |
subtitle | string | Your next adventure | Trip context |
brand | ReactNode | None | Host-provided brand content |
nights | number | 7 | Booking total multiplier |
fontFamily | string | System | Body text |
mediumFontFamily | string | System | Interface text |
titleFontFamily | string | System | Editorial detail heading |
monoFontFamily | string | System | Tabular prices |
surfaceColor | string | #FFF4E2 | Detail surface |
textColor | string | #302537 | Foreground |
duration | number | 650 | Expansion duration in ms |
cardWidthRatio | number | 0.62 | Card width, constrained to 0.5–0.8 |
onIndexChange | (index) => void | None | Selection callback |
onDetailChange | (open, item) => void | None | Detail state callback |
onBook | (item) => void | None | Host checkout action |
onInteraction | () => void | None | Stop host autoplay |
style | StyleProp<ViewStyle> | None | Container overrides |
testID | string | None | Test selector |
The ref exposes goTo(index), open(), close(), scrollTo(offset), and reset(). Calls to goTo are ignored while detail is expanded. Artwork should be bundled locally. Long descriptions remain scrollable; choose concise names so the single-line morph header fits small phones.
Compatibility
Built for Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, and Gesture Handler 3.3. No Skia requirement. iOS, Android, and web are intended targets.
Accessibility
Browse by swiping, tapping neighboring cards, selecting a pagination control, or using previous/next buttons. All buttons have roles and labels; state changes are announced and the selection has a polite live region. Back is available without a drag. Read more provides a tap alternative to scrolling the collapsing hero. Reduced motion uses a 200 ms fade through the layout change and disables perspective, parallax, hero collapse, and press scaling. Prices do not roll.
Demo
Northwind uses the kit's compass tile and four local illustrations. Autoplay selects the second and third cards, opens at 1.9 seconds, scrolls, closes, and returns to the first card. Any direct interaction clears the timers. The checkout action records a trip confirmation locally.
The shared font loader exposes Geist Medium, not Geist SemiBold; the demo uses the available Medium face instead of introducing an unloaded font.
Icons: Lucide (ISC). Paths copied from the repository kit into icons.ts and rendered as SVG images. The Northwind brand and artwork belong to the demo only.




