Skip to main content

Documentation Index

Fetch the complete documentation index at: https://voyage-theme.fasil.in/llms.txt

Use this file to discover all available pages before exploring further.

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

PageBehaviour
Home pageTriggers when the visitor has scrolled past the hero (one full viewport height down). Fires once per session.
Any other pageTriggers after one viewport of scroll on pages without a hero — but Voyager’s other templates rarely include the popup snippet, so in practice it’s home-only by default.
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:
AN INVITATION
First voyage,
fifteen percent.

Save 15% off your first order*

Letters from the studio — and 15%
on your first piece.

First-voyage subscribers only.

[Reveal my 15% code →]

15% won't appear again on this visit.   No thanks
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.
CODE UNLOCKED
Your 15% is ready — where should we send it?

[Email] | Mobile

[your-email@example.com         ]
[Email my 15% code →]

One letter a season. Unsubscribe anytime.

T&C apply*   No thanks
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.
WELCOME ABOARD
Your code awaits.

Use at checkout
ABBARA15

[Continue browsing]
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:
ActionBehaviour
× buttonCloses, persists voyage-offer-dismissed: true to sessionStorage
”No thanks” linkSame as × button
Backdrop clickSame
Escape keySame
Stage 3 → Continue browsingSame (the conversion is already captured)
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:
ChangeWhere
Discount %Update both the stage-1 headline and the stage-3 code display
Discount codeCurrently ABBARA15 in Stage 3 — replace with your actual code
Headline copyStage 1 <h2 class="offer-pop__head">
Reassurance lineStage 2 <p class="offer-pop__reassurance">
VideoReplace assets/popup-video.mp4 (keep data-lazy-src mechanism)
Poster imageReplace the Unsplash URL in data-lazy-src on the <img>

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.