Docs

Motion Runtime

Every Shopiflame section shares a single runtime snippet — snippets/forge-motion.liquid. This file handles GSAP loading, reduced-motion detection, and the forge-js gate that keeps sections readable without JavaScript.

GSAP lazy-load

GSAP is loaded once, on the first section that needs it, via a module-type script:

{% unless forge_gsap_loaded %}
  <script type="module">
    import gsap from 'https://cdn.skypack.dev/gsap@3';
    import ScrollTrigger from 'https://cdn.skypack.dev/gsap@3/ScrollTrigger';
    gsap.registerPlugin(ScrollTrigger);
    window.__forge = { gsap, ScrollTrigger };
    document.dispatchEvent(new CustomEvent('forge:ready'));
  </script>
  {% assign forge_gsap_loaded = true %}
{% endunless %}

The forge_gsap_loaded Liquid variable ensures GSAP is only requested once per page, even if multiple Shopiflame sections are present. Each section then listens for forge:ready before initializing its own animation timeline.

prefers-reduced-motion

Every section checks window.matchMedia('(prefers-reduced-motion: reduce)') before building its timeline. When the preference is set:

  • The section's final visual state is applied immediately (no transition)
  • No ScrollTrigger is registered
  • GSAP may still load (for other sections), but the per-section animation is skipped entirely

This is not a degraded experience — the content is fully readable and the layout is identical. Motion is an enhancement, not load-bearing.

The forge-js gate

Sections add a forge-js class to their root element from inline JavaScript:

<div class="sf-section" id="{{ section.id }}">
  <script>
    document.currentScript.closest('.sf-section').classList.add('forge-js');
  </script>
  <!-- section content -->
</div>

CSS rules prefixed with .forge-js apply only when JS has run. Without it, the section renders its static fallback — a fully styled, readable layout with no motion dependencies. Content is never hidden behind a JS gate.

CLS and LCP considerations

  • Sections declare explicit aspect-ratio or min-height on their root elements to prevent layout shift as motion initializes.
  • Images use loading="eager" for above-the-fold heroes and loading="lazy" elsewhere.
  • The GSAP script is type="module", which defers automatically — it never blocks rendering.

See Core Web Vitals for Shopify Motion for a deeper breakdown.