teasanti.netlify.app
teasanti.
A single-page, cinematic e-commerce experience for a premium tea brand — the entire first fold is a scroll-scrubbed video, the user's scroll position directly driving playback in sync with six brand pillars, before the page unfolds into a horizontally-scrolling product strip, an editorial brand story, a ritual guide, and a full-bleed poster CTA.
Role
Design + Front-end (Solo)
Stack
Next.js 16 · React 19 · TS
Motion
GSAP · ScrollTrigger · Lenis
Type
E-commerce · Web Experience
teasanti.netlify.app
Pure
Natural
Calm
Fresh
Rare
Timeless
785
Lines · Hero Component
30+
Iterative Commits
6
Brand Pillars
8×
Max Playback Rate
360px
Min Supported Viewport
pure
·
natural
·
calm
·
fresh
·
rare
·
timeless
·
pure
·
natural
·
calm
·
01 01 — Direction
Ceremony, not brightness.
The goal was to prove a small, image and video-heavy brand site could feel as directed as a high-end fashion or spirits microsite — while staying performant and fully responsive down to 360px. Motion isn't decorative here; the scroll-scrubbed video is the hero, and every entrance is staged on scroll position, never autoplay.
Palette
Near-black, gold, sage
Near-black backgrounds (#020008) with warm gold (#c8a96e) and sage green (#8aaa78) accents pulled directly from the tea imagery — ceremony and craft over typical e-commerce brightness.
Brand-led colour
Type
Old-world meets modern
Cinzel (serif display) for brand moments paired with a light, wide-tracked sans for body copy — the tension between an ancient serif and modern spacing reinforces "ancient wisdom, modern ritual."
Cinzel + wide sans
Motion
Motion as narrative
Every section entrance — product cards, story columns, stats, ritual image, CTA — is staged with GSAP timelines tied to scroll position rather than autoplaying on load.
Scroll-staged GSAP
The project went through several creative directions before landing on Teasanti — an earlier pass explored a 3D "magic book" fantasy concept, and a "CHAIYA" tea brand variant, before consolidating into a single, focused Teasanti experience and refining it over ~30 iterative commits.
02 02 — Technical Challenges
Five problems, one hero fold.
The hero video needed to feel directly tied to the scrollbar — like a native range input — not like a clip that merely starts and stops. Every challenge below traces back to that one constraint.
01 — Playback
Scroll-scrubbed video without stutter
Calling video.currentTime = on every scroll tick was ruled out — repeated seeks on compressed video are expensive and visibly stutter. The fix: a "play-rate chasing" driver on requestAnimationFrame. Each frame compares current time to a scroll-derived target; small gaps do nothing, larger gaps raise playbackRate up to 8× and let the browser's own decode pipeline catch up, falling back to a hard seek only when scrolling backwards.
requestAnimationFrame playbackRate chase Hard-seek fallback
8×
02 — Cross-Browser
Safari couldn't keep up
Safari handles playbackRate and blob-URL seeking very differently from Chromium — the chasing technique broke down there. Rather than force one code path everywhere, the driver branches per-browser: Safari gets direct currentTime scrubbing against a preload="auto" src, accepting readyState >= 1 instead of waiting for full buffering, while Chromium keeps the smoother play-rate chase against a preloaded Blob URL.
Per-browser branch Direct seek (Safari) Blob URL (Chromium)
2 paths
03 — Load Strategy
Zero-lag start
To avoid a first-scroll stutter while the browser fetches the video, the full file streams via fetch() with a ReadableStream reader, is assembled into a Blob, and swapped in as a blob: URL — with a visible load-percentage screen so the wait reads as intentional, not broken.
ReadableStream Blob URL Load-% screen
Blob
04 — Sync Architecture
One ScrollTrigger, two animations
An early version used separate ScrollTriggers for the video scrub and the sidebar word highlights, which caused desync — words highlighting simultaneously, or not at all, across browsers. The fix was architectural: a single pinned ScrollTrigger owns scroll progress, and both the video's target time and the active sidebar word derive from the same onUpdate callback, eliminating any chance of the two drifting apart.
Single pinned trigger Shared onUpdate
1 trigger
05 — Responsive
Full mobile responsiveness
Every section was rebuilt to work from 360px up: the desktop left/right split scroll-text layout collapses to stacked centered text, GSAP animations drop their horizontal offset on mobile so nothing slides in from off-screen, the header collapses to logo + cart with a slide-down menu, and the product grid reflows 1 → 2 → 3 columns with clamp()-based fluid typography throughout.
Stacked layout Fluid clamp() type 1→2→3 grid
360px
03 03 — Result
A fully directed brand story.
A single-component page (plus a product-card component) rendering a fully custom, motion-directed brand story: pinned video hero → horizontal product carousel with tilt/glow hover cards → brand statement → two-column editorial story with stats and pull-quote → illustrated ritual guide → full-viewport poster CTA — all running on Lenis-smoothed scroll and GSAP.
6
Major sections — pinned hero, product strip, brand statement, story, ritual guide, poster CTA
GSAP
+ ScrollTrigger + Lenis driving every entrance and the hero scrub, no autoplay animation
CI/CD
Deployed on Netlify with GitHub Actions auto-deploy on every push to main
100%
Custom-built — no template, no page builder, hand-authored motion timelines throughout
04 04 — Product Line
Seven blends, one shelf presence.
Packaging design carried the same "ceremony, not brightness" direction off-screen — a consistent Teasanti wordmark and badge system dropped onto a colourway unique to each blend, so the range reads as one family from across a shelf while every bag still tells its own flavour story.
Teasanti Herbal Green Tea packaging
Herbal Green Tea
Pure · Balanced · Refreshing
Teasanti Jasmine Green Tea packaging
Jasmine Green Tea
Floral · Smooth · Revitalizing
Teasanti Matcha Green Tea packaging
Matcha Green Tea
Pure · Energizing · Focused
Teasanti Lemon Ginger Green Tea packaging
Lemon Ginger Green Tea
Zesty · Warming · Refreshing
Teasanti Hibiscus Green Tea packaging
Hibiscus Green Tea
Tangy · Floral · Refreshing
Teasanti Hibiscus Rose Green Tea packaging
Hibiscus Rose Green Tea
Floral · Antioxidant · Refreshing
Teasanti Wild Berry Green Tea packaging
Wild Berry Green Tea
Vibrant · Antioxidant · Refreshing
05 05 — What I'd Improve Next
Still brewing.
01
Move video scrubbing to requestVideoFrameCallback where supported, for frame-accurate sync instead of RAF polling.
02
Add a lower-fidelity poster fallback for slow connections instead of gating the whole experience behind full video preload.
03
Wire the "Add to Cart" and nav actions to real state now that the visual language is settled.
Live
See it live.

Scroll the hero to feel the video-scrub sync, then unfold the product strip, story, and ritual guide for yourself.

Visit Teasanti View Source
teasanti.netlify.app
Next Project
ghāṭī.
buddhpriya93@gmail.com  ·  +91-8840-871-715  ·  buddh.in