All work
Food & beverage · Rizal

Felici Artisan Gelato

A scroll-driven marketing site for the gelato brand of Reign Capunfuerza, built as a static React SPA: landing video, canvas word sequences, a 16-flavour selector, and two store locations.

Visit the live site
Felici Artisan Gelato share image
Felici Artisan Gelato screen 1
Felici Artisan Gelato screen 2

Stack

  • Vite 5 · React 18 SPA · react-router-dom 7
  • GSAP 3 + ScrollTrigger · Lenis smooth scroll
  • Canvas 2D image-sequence player via createImageBitmap
  • Self-hosted webfonts · static client-only build

What we built

Orientation-aware landing video

A full-viewport muted loop that swaps between landscape and portrait sources on orientation change, overlaid with film grain and a wordmark that fades out once the visitor scrolls past 80 pixels.

src/sections/LandingVideo.jsx

Canvas word sequences

The hero plays a 177-frame sequence at 40fps and the story band a 151-frame one, decoded through createImageBitmap onto a 2D canvas rather than shipped as video, so the type stays crisp at any density.

src/sections/Hero.jsx

16-flavour selector

A pinned section where cone buttons and a thumbnail grid swap the centre cup, the giant italic name and the stage colour. Flavours carry real status badges — Bestseller, New, Must Try, and Not Available for Asin Tibuok.

src/sections/FlavorStage.jsx

Two-store locator

Postcards for the Cainta cafe and the Angono gelato counter, each with its own opening hours, a muted loop that plays only once scrolled into view, and an outbound Google Maps link.

src/sections/Locations.jsx

Contact form with a mail fallback

Validated name, email, subject and message. It POSTs JSON when a contact endpoint is configured and otherwise opens a prefilled mailto, so the form still works with no backend deployed.

src/pages/Contact.jsx

Mobile memory and iOS viewport work

Not a visible feature, but the reason the site survives a phone: a frozen viewport unit so the iOS URL bar cannot resize full-screen sections, capped device pixel ratio, a 32-frame ring buffer, and ImageBitmaps closed after playback so the tab is not killed.

src/components/FrameSequence.jsx

Integrations

  • Google Maps as outbound search links, not an embed
  • Instagram and Facebook brand profiles
  • mailto delivery by default, optional POST endpoint

Want the same for your floor?

Start a project