A landing page and shop for a fictional flower studio, built to show how much a page can say with motion alone. Nothing here is for sale and nothing is sent or stored: the basket, the Sunday-list form and the checkout are demos that live in the page's memory.
How it's built
- Hero. The headline is split into characters with SplitText and each one flies in from above or below with a small spin, in random order. Six photos arrive from outside the screen with a rotation and a scale-up, then each floats on its own slow sine loop. Three nested layers per photo keep the animations from fighting each other: the outer layer takes the fly-in and the scroll-away, the middle one the pointer parallax (fine pointers only,
gsap.quickTo at a different depth per photo), the inner one the float. Photo rotation uses the CSS rotate property so GSAP's transform never overwrites it. - Petals. Fourteen SVG petals fall on a looping linear
y tween while two other tweens add a sine sway and a flip, so the paths never repeat. Only transform and opacity animate. The same shapes are used for a petal burst when the demo checkout is pressed. - Marquee. A ribbon of flower names that loops on its own and reads the page's scroll velocity through ScrollTrigger: it speeds up while you scroll and flips direction when you scroll back.
- Pinned bouquet row. On desktop with a pointer the section pins and the track slides sideways, scrubbed to the scroll. Each card has two scrubbed tweens that use the slide as their
containerAnimation: the photo drifts inside its frame, and the card straightens and fades in as it arrives. On touch and narrow screens the row is a normal horizontal scroller with snap and a staggered fly-in, because a pinned sideways slide fights the browser's own scrolling there. - Basket. Adding a bouquet clones its photo at the card's position and sends it to the basket button along an arc (one tween per axis with different eases), then the button bounces and the counter pops. The drawer is a native
<dialog>, so focus trapping, Esc and the inert page behind it come from the platform; its items fly in staggered, the total counts up and the free-delivery bar fills. - Moods. Four moods change the section colours, the accent used by petals, buttons and the bloom, and the bouquet shown. The new photo is a second layer revealed with a
clip-path: circle() that grows from the button you pressed; the name is re-split and its letters fly in. The name element is keyed by bouquet so React never holds a node that SplitText has replaced. - Studio. The manifesto is split into words whose opacity is scrubbed one after another, so the sentence lights up as it is read. Photos open with clip-path wipes and drift at different speeds; the three figures count up.
- Delivery. A stem (one SVG path) is drawn with DrawSVG on a scrubbed trigger, a leaf unfolds beside each step, the steps fly in from alternating sides and a ten-petal flower blooms on the end of the stem, then turns slowly.
- Footer. The wordmark's letters rise on entry and lift on pointer hover with an elastic return; a pool of six small photos follows the pointer as a trail (fine pointers only).
- Photos. Eighteen free-licence photos from Pexels, downloaded once and resized to WebP at two sizes (1400 and 560 px on the long side). The first three hero photos are decoded before the loader lifts; the rest load lazily.
- Loader. A flower opens petal by petal as loading counts up, on a stem that grows with two leaves, while petals drift down and the name fades in (own skin, CSS and SVG only).
- Fonts. Fraunces (soft, optical-size axis) for display and DM Sans for text, loaded only inside this experiment.
Reduced motion
Nothing flies, floats, falls, scrubs or loops. The hero photos sit still, the bouquet row is a normal sideways scroller, the stem is fully drawn and the flower is open; the basket and mood changes switch at once.