Skip to main content
Voyager’s offer popup is a small, considered conversion surface. It’s intentionally not a generic “10% off!” overlay — it’s framed editorially (“First voyage, fifteen percent.”) and stages the conversion in three deliberate steps that mirror how a fashion-led brand would speak to a first-time visitor. The popup is gated behind a sessionStorage flag, fires once per session on the home page only, and is dismissible at every step.

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 on data-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:
The headline uses the Voyager script-and-serif typography. The asterisk and “won’t appear again on this visit” copy lean into the loss-aversion principle — once the visitor declines, the dismissal is persisted to 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.
Two tabs — Email / Mobile — let the visitor pick their preferred channel. The form has a single field (email or phone), a single CTA, and a reassurance line (“One letter a season. Unsubscribe anytime.”) that addresses the visitor’s likely worry about spam. Switching tabs swaps the input type, placeholder, and submit-button label.

Stage 3 — success

Triggered on form submission.
The code is displayed as a single tile. The “Continue browsing” CTA closes the popup and returns the visitor to the page.
The offer popup’s form submission is intentionally not wired to a backend by default. On submit, the popup advances to Stage 3 without sending the email anywhere. To actually capture the lead, wire the form’s submit handler to your ESP — Klaviyo, Mailchimp, Shopify Email — by adding a fetch to your endpoint in the popup’s submit handler (assets/theme.js, look for '.offer-pop__form').

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 a 9: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 with data-lazy-src instead of src
  • The hidden poster image ships with data-lazy-src too
  • When the popup opens, JS copies data-lazy-srcsrc and 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
The result: the popup’s video never enters the network payload on initial page load, and the page’s Lighthouse performance score stays high.

Customizing the content

The popup content lives in snippets/offer-popup.liquid. To customize:

Disabling

The popup ships in the home-page section group via index.json. To disable:
  • Remove the popup section from your templates/index.json content_for_index array, or
  • Remove the {% render 'offer-popup' %} call from wherever it’s rendered
Either way, the popup snippet stays in the codebase but isn’t injected — no JS overhead.

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.