About this animation
A gift box that opens to reveal interactive content, with configurable reward content, colors, labels, and box size.
Why this one
Two satin bow loops follow a resisted pull. The ribbon slips away, the solid lid jumps and lands beside the box, and warm light escapes through the open rim. The reward emerges behind the front wall before settling above the box. No wrapping-paper flaps or disappearing body.
How it works
- Follow the knot drag with bounded resistance and spring back if released before opening.
- Drive the ribbon release, detached lid, rising card, and particles from one opening phase.
- Layer native card content between two Skia canvases so the box rim occludes the reveal; reduced motion uses static poses and a crossfade.
Opening choreography
One 2,300 ms phase drives the opening after release:
- 0–500 ms: the two loops narrow and separate; the crossed ribbons slide out and fall with rotation and gravity.
- 450–1,050 ms: the lid rises by 28% of the scene width in an arc, rotates from a negative tilt to +18°, and lands to the right with one small bounce. It remains visible.
- 450 ms onward: a radial gold light and five soft rays emerge from the dark interior.
- 900–2,300 ms: a moving target with
damping: 14, stiffness: 160, mass: 1raises the opaque card to 84% width. A native clip follows the front rim; a foreground Skia canvas paints the front wall over the entering card. - 1,650 ms: 24 confetti pieces and six sparkles launch from the mouth after the card has cleared it.
- Final: the box settles to 82% scale below the measured card, with its detached lid and a faint warm glow.
The closed gift breathes from 1 to 1.015 over 2.4 seconds. The knot follows the finger with a maximum displacement of 12% of scene width. Releasing early returns it with an interruptible spring. Two Skia canvases handle the complete scene, with native interactive content between them.
Install
npx expo install @shopify/react-native-skia expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-workletsTargets Expo SDK 58, React Native 0.88.0, Skia 2.14.0, Reanimated 4.7.1, Worklets 0.13.0, and Gesture Handler ~3.3.0. Wrap the host in GestureHandlerRootView. For web, load CanvasKit before rendering the piece and serve canvaskit.wasm. Copy all three source files listed in piece.json.
Usage
import { GiftUnwrapReveal, type GiftUnwrapRevealRef } from './gift-unwrap-reveal';
const gift = useRef<GiftUnwrapRevealRef>(null);
<GiftUnwrapReveal
ref={gift}
style={{ flex: 0, width: 300, height: 400 }}
fontFamily="Geist_500Medium"
titleFontFamily="InstrumentSerif_400Regular"
monoFontFamily="GeistMono_400Regular"
colors={{ paper: '#426A5A', shadow: '#254539', ribbon: '#F3D69B' }}
onAccept={() => addGiftCardToWallet()}
/>;
gift.current?.unwrap();
gift.current?.rewrap();Load these fonts in the host or pass your own. There are no catalog or theme imports. Provide children to replace the default opaque card with native application content. Custom content should fit 84% of the scene width and leave room below for the box. Its measured height sets the final rim position. Give the component a bounded parent; enlarge the host for long copy or accessibility text sizes.
Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
children | ReactNode | Gift card | Content revealed above the box |
onReveal | () => void | — | Once after each completed reveal |
onAccept | () => void | — | Default wallet action |
colors | Partial theme | Holiday palette | GiftColors plus cream, ink, glass, border |
labels | Partial labels | English holiday copy | pull, unwrap, hint, title, amount, description, accept, announcement |
pullThreshold | number | 0.7 | Release threshold, clamped to 0.15–1 |
size | number | 400 | Maximum scene width |
fontFamily | string | System | Interface font |
titleFontFamily | string | System | Card heading font |
monoFontFamily | string | System | Amount font |
onInteraction | () => void | — | Stops host autoplay on direct input |
style | StyleProp<ViewStyle> | — | Outer container |
testID | string | gift-unwrap-reveal | Root identifier |
Exported GiftColors: paper (printed box), pattern, ribbon, reverse (rim and lid light), shadow, highlight, and confetti. Set confetti: [] to disable paper particles. Legacy color keys remain supported. No blur dependency is required; the reward uses an opaque surface with a light gradient.
Ref: unwrap(): void simulates a 350 ms resisted pull and runs the same opening as the gesture. rewrap(): void cancels the sequence and restores the complete gift immediately, including during opening. There is no fade to an empty scene.
Compatibility
Designed for Expo Go on SDK 58 and web.
Accessibility
The gift is a button labeled “Unwrap gift,” with a tap alternative, a 48-point knot target, and the activate accessibility action. The card is excluded from the accessibility tree until reveal completes; its wallet button is at least 48 points high. Announcements and visible copy are configurable.
Reduced motion uses a 200 ms crossfade between static closed and open compositions and the card. Breathing, flying ribbons, lid travel, card movement, digit rolling, and particles are disabled.




