Voyager’s most recognisable signature is the page-transition curtain. On every internal link click, a navy panel rises from the bottom of the viewport, fully covers the page, then falls away from the top to reveal the next page. The effect is cinematic — like a theatre curtain between scenes — and gives the storefront a film-like pacing that distinguishes it from the usual hard cut between Shopify pages.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.
How it works
The curtain is a single fixed-position element (<div class="curtain">) sitting at z-index: 800, with a navy background and the Voyager wordmark centred. CSS keyframes handle the rise and fall:
<a>. On click, it:
- Sets
data-state="rising"on the curtain (animation runs, 700 ms) - Writes a
voyage-curtain-pendingflag tosessionStorage - Lets the link navigate normally
layout/theme.liquid) reads the flag before anything else renders. If it sees the flag, it adds .curtain-incoming to <html>, which keeps the curtain pinned at translateY(0) so the page paints underneath a still-covered curtain. Once the page is settled and ready to reveal:
data-state="falling"is set (700 ms fall animation)- The
voyage-curtain-pendingflag is cleared .curtain-incomingis removed atomically with the animation start
Why the pre-paint coordinator
Without the inlined pre-paint script, there’s a window of ~50–200 ms between when the new page’s HTML arrives and when the curtain’s CSS class is applied. In that window, the browser would paint the new page without the curtain over it — a brief flash of the next page, then the curtain falls. The pre-paint coordinator eliminates that flash by readingsessionStorage synchronously before the first paint and pinning the curtain in place.
sessionStorage gate
Thevoyage-curtain-pending key is set on link click and cleared once the curtain finishes falling on the next page. This gate ensures:
- A user clicking two links in quick succession doesn’t trigger two overlapping curtains
- A page that loads without a rising curtain (direct nav, hard reload, external referrer) doesn’t fall an invisible curtain on its own
- Back-forward cache (bfcache) reloads reset to a clean state — when the browser restores a cached page, the flag is gone and the curtain stays at
translateY(100%)(off-screen)
bfcache reset
Browser back-button restores can leave a page in any state. Voyager explicitly resets the curtain onpageshow events where event.persisted === true:
- Curtain
data-statecleared - Inline
transformandanimationcleared .curtain-incomingremoved from<html>voyage-curtain-pendingcleared
Opting out per-link
Adddata-no-curtain to any link or its ancestor to skip the transition:
Accessibility
The curtain respectsprefers-reduced-motion. Visitors with the OS-level “Reduce motion” preference set will see a 700 ms animation only if their reduced-motion preference is not set. With the preference on, the curtain effectively disappears from the experience — the click does navigate, but the animation is skipped.
This is handled by the global @media (prefers-reduced-motion: reduce) rule in premium.css that disables transitions on .reveal, split-text, image blur-to-clear, and the curtain.
Disabling the curtain globally
There’s no theme setting to turn the curtain off — it’s part of Voyager’s signature pacing. If you genuinely don’t want it, you can:- Remove the
<div class="curtain">fromlayout/theme.liquid - Remove the
.curtain-incomingblock from the pre-paint coordinator inline script in the same file - The page-transition click handler will then find no curtain element and silently no-op
What’s next
Magnetic & cursor
The hover micro-interactions that complement the page rhythm.
Dark mode & grain
The other two pillars of Voyager’s premium feel.