When it appears
The trigger uses a scroll listener. Once the threshold is crossed, the popup opens, the scroll listener becomes inert, and the body’s
overflow is locked to hidden so the popup feels modal.
The three stages
Stage state is stored ondata-step on the container (1, 2, or 3). Each stage has its own slot in the markup; CSS shows/hides them based on data-step.
Stage 1 — persuasion
The first thing the visitor sees. It’s framed as an invitation, not an offer:sessionStorage so the claim is honest.
The primary CTA (“Reveal my 15% code →”) commits the visitor to the small first “yes” — the psychological foot-in-the-door that makes the second-stage email field feel less like a transactional cost.
Stage 2 — capture
Triggered when “Reveal my 15% code →” is clicked.Stage 3 — success
Triggered on form submission.Dismissal mechanics
The popup closes on:
Once
voyage-offer-dismissed is true, the popup will not reopen for the rest of the session — even on subsequent page loads, refreshes, or navigation. New session, new opportunity.
The dismissal copy (“15% won’t appear again on this visit”) is honest because of this gate. Don’t loosen it without rewriting the copy.
Performance handling
The popup includes a9:16 video in the media frame. The video file is large (~7.9 MB) and would otherwise drag down the home-page LCP. Voyager handles this with lazy attachment:
- The
<video>element ships withdata-lazy-srcinstead ofsrc - The hidden poster image ships with
data-lazy-srctoo - When the popup opens, JS copies
data-lazy-src→srcand calls.load() - If autoplay doesn’t kick in within 800 ms (iOS Low Power Mode, autoplay block), JS hides the video and shows the poster image instead
Customizing the content
The popup content lives insnippets/offer-popup.liquid. To customize:
Disabling
The popup ships in the home-page section group viaindex.json. To disable:
- Remove the popup section from your
templates/index.jsoncontent_for_indexarray, or - Remove the
{% render 'offer-popup' %}call from wherever it’s rendered
What’s next
Cart drawer
Where the post-discount add-to-bag lands.
Search panel
The other slide-in surface — same data-open / sessionStorage pattern.