Shopify Sticky Scroll Banner Code (Copy, Paste, Customize)

Static announcement bars are fine for “set-and-forget” messaging, but sometimes you want something smarter: a sticky scroll banner that appears only when the visitor starts scrolling, sticks to the top while they read, hides when they scroll down (so content gets maximum space), and slides back in when they scroll up (so your message stays handy). .

Sticky Scroll Banner
Sticky Scroll Banner

In this tutorial, we’ll build that interaction using pure HTML, CSS, and JavaScript—no apps and no Liquid. The snippet below is an example you can paste directly into a Custom HTML block in the Theme Editor. After that, we’ll walk through how it works and how to customize colors, timing, thresholds, and behavior (dismiss, shadow, mobile tweaks, etc.).

What you’ll build

  • A sticky banner anchored at the top of the viewport.
  • Appears after the visitor scrolls past a configurable threshold.
  • Auto-hides on scroll-down, reappears on scroll-up (smart behavior).
  • Dismiss button (X) with localStorage memory.
  • Responsive, with easy color/size/theme adjustments.
  • Optional CTA button and emoji/icons support.

Where to paste the code (Shopify)

  1. In your Shopify admin, go to Online Store → Themes → Customize.
  2. Open the page/template where you want the banner (e.g., Home).
  3. Add a Custom HTML (or Custom Liquid) section near the top of the layout.
  4. Paste the full snippet below.
  5. Save and preview.

This snippet is an example meant for learning and quick use. You can adapt it later into a theme section if you want schema settings.

Example Code — Sticky Scroll Banner (HTML/CSS/JS)

Copy everything from <!DOCTYPE html> to </html> and paste into your Custom HTML/Liquid block.

DESIGNED FOR REFERENCE ONLY
NOTE : THIS IS AN EXAMPLE CODE
<body>


  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Shopify Sticky Scroll Banner</title>
  <style>
    :root{
      /* Customize these */
      --sb-bg: #111;          /* background color */
      --sb-fg: #fff;          /* text color */
      --sb-accent: #00d084;   /* CTA background */
      --sb-height: 56px;      /* banner height */
      --sb-shadow: 0 6px 18px rgba(0,0,0,.18);
      --sb-z: 9999;           /* stack above header if needed */
      --sb-transition: 260ms ease; /* slide/fade animation speed */
    }

    /* Page reset to see behavior clearly (safe to remove) */
    body{ margin:0; font-family: Arial, sans-serif; }

    /* STICKY BANNER */
    .sticky-banner{
      position: sticky; top: 0; left:0; right:0;
      display: flex; align-items: center; gap: 14px;
      height: var(--sb-height);
      background: var(--sb-bg); color: var(--sb-fg);
      padding: 0 16px;
      transform: translateY(-100%); /* start hidden above */
      opacity: 0;
      transition: transform var(--sb-transition), opacity var(--sb-transition);
      z-index: var(--sb-z);
      box-shadow: none;
    }

    /* When eligible to show (scrolled past threshold, not dismissed) */
    .sticky-banner.is-ready{ box-shadow: var(--sb-shadow); }

    /* Visible state */
    .sticky-banner.is-visible{
      transform: translateY(0);
      opacity: 1;
    }

    /* Hidden-by-scroll (user scrolling down) */
    .sticky-banner.is-hidden{
      transform: translateY(-100%);
      opacity: 0;
    }

    .sb-text{
      flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
      font-size: 14px; line-height: 1.1;
    }

    .sb-cta{
      appearance: none; border: 0; border-radius: 6px;
      background: var(--sb-accent); color: #111;
      padding: 10px 14px; font-weight: 700; cursor: pointer;
      transition: filter 160ms ease;
      white-space: nowrap;
    }
    .sb-cta:hover{ filter: brightness(0.9); }

    .sb-close{
      appearance: none; border: 0; background: transparent; color: currentColor;
      font-size: 20px; line-height: 1; padding: 6px; cursor: pointer;
    }

    /* Small screens: tighter layout */
    @media (max-width: 480px){
      :root{ --sb-height: 52px; }
      .sb-text{ font-size: 13px; }
      .sb-cta{ padding: 8px 12px; font-size: 13px; }
    }

    /* Demo content to enable scrolling — remove in production */
    .demo-fill{
      height: 1800px;
      background: linear-gradient(#fafafa,#eaeaea);
      padding: 24px;
      color:#333;
    }
  </style>



  <!-- STICKY BANNER (appears after N px, hides on scroll-down, shows on scroll-up) -->
  <div class="sticky-banner" id="stickyBanner" data-threshold="180" how many px to scroll before showing>
       data-remember="true"           <!-- remember dismissal in localStorage -->
       data-storage-key="sb_closed_v1"
       data-auto-show="true"          <!-- enable auto show logic -->
       data-show-on-scroll-up="true"  <!-- show when user scrolls up -->
       aria-live="polite" role="region" aria-label="Promotion">
    <div class="sb-text" id="sbText">
      🚚 Free shipping over $50 • ⭐ New arrivals just dropped • ⏳ Ends Sunday — don’t miss it
    </div>
    <button class="sb-cta" type="button" id="sbCta">Shop now</button>
    <button class="sb-close" type="button" id="sbClose" aria-label="Dismiss banner">×</button>
  </div>

  <!-- Demo page content (only for preview) -->
  <div class="demo-fill">
    <h1>Page content</h1>
    <p>Scroll down to see the banner appear. Keep scrolling to see hide-on-down / show-on-up.</p>
    <p>Remove this block in production.</p>
  </div>

  <script>
    (function(){
      const banner = document.getElementById('stickyBanner');
      if(!banner) return;

      // CONFIG FROM DATA-ATTRIBUTES
      const threshold = parseInt(banner.getAttribute('data-threshold') || '180', 10);
      const remember   = banner.getAttribute('data-remember') === 'true';
      const storageKey = banner.getAttribute('data-storage-key') || 'sb_closed';
      const autoShow   = banner.getAttribute('data-auto-show') === 'true';
      const showOnUp   = banner.getAttribute('data-show-on-scroll-up') === 'true';

      // Respect previous dismissal
      if(remember && window.localStorage && localStorage.getItem(storageKey) === '1'){
        // Never show if previously dismissed
        return;
      }

      let lastY = window.scrollY;
      let ready = false;
      let visible = false;

      function setVisible(v){
        if(!ready) return;
        visible = v;
        banner.classList.toggle('is-visible', v);
        banner.classList.toggle('is-hidden', !v);
      }

      function evaluate(){
        const y = window.scrollY || window.pageYOffset || 0;

        // Become "ready" after threshold
        if(!ready && y >= threshold){
          ready = true;
          banner.classList.add('is-ready');
          // initial reveal if autoShow
          if(autoShow) setVisible(true);
        }

        if(!ready) return;

        // Hide on scroll down, show on scroll up (only after we passed threshold)
        if(showOnUp){
          if(y > lastY + 6){        // scrolling down
            setVisible(false);
          } else if(y < lastY - 6){ // scrolling up
            setVisible(true);
          }
        }

        lastY = y;
      }

      // Dismiss button
      const closeBtn = document.getElementById('sbClose');
      if(closeBtn){
        closeBtn.addEventListener('click', function(){
          banner.classList.remove('is-visible');
          banner.classList.add('is-hidden');
          if(remember && window.localStorage){
            try{ localStorage.setItem(storageKey, '1'); } catch(e){}
          }
        });
      }

      // CTA example — replace with your URL/navigation as needed
      const cta = document.getElementById('sbCta');
      if(cta){
        cta.addEventListener('click', function(){
          // Replace with a real link; this keeps the snippet self-contained.
          window.location.href = '/collections/all';
        });
      }

      // Init + listeners
      evaluate();
      window.addEventListener('scroll', evaluate, {passive:true});
      window.addEventListener('resize', evaluate);
      window.addEventListener('orientationchange', evaluate);
    })();
  </script>

</body>

How it works (quick breakdown)

  • HTML: The banner is a single div.sticky-banner with text, a CTA button, and a close button. Data-attributes control behavior (threshold, memory, etc.).
  • CSS:position: sticky; top:0 keeps the bar glued to the top once it’s in the document flow.We animate visibility via transform and opacity.Variables make it easy to change colors, height, and shadow.
  • JavaScript:Waits until the user scrolls past data-threshold before enabling the banner.Tracks scroll direction: down → hide, up → show.Dismiss persists via localStorage when enabled.

Customization guide

  • Colors and theme: tweak --sb-bg, --sb-fg, --sb-accent.
  • Height: --sb-height. If your header is tall, set --sb-z higher.
  • Reveal threshold: set data-threshold="120" (or any px value).
  • Behavior:Always show once threshold is reached: keep data-auto-show="true".Don’t auto show? set data-auto-show="false" and toggle with your own logic.Disable show-on-up: set data-show-on-scroll-up="false" to keep it always visible after threshold.
  • Dismiss memory:Off: data-remember="false" (banner will reappear on next visit).On: data-remember="true" (default).
  • Copy and CTA: edit the inner text and change the window.location.href in the CTA click handler.
  • Mobile polish: reduce --sb-height, tweak font sizes, or hide the CTA text and use a concise label.
  • Shadow: change --sb-shadow or set to none for ultra-flat style.
  • Page targeting: paste this only on the templates you want (e.g., Home and Collection, but not Checkout).

UX patterns you can test

  • “Reveal on intent”: higher threshold (e.g., 400–600px) so the banner appears only for engaged scrollers.
  • “Deal mode”: show the banner immediately on sale/launch days by setting data-threshold="0".
  • Shipping threshold helper: change copy dynamically when cart_total >= $X (requires additional JS pulling cart info).
  • Exit-intent: if you prefer, show it when the cursor moves to the top (desktop) instead of scroll—easy to wire up.

Troubleshooting

  • Banner never appears:Ensure you actually scrolled past the data-threshold.If you dismissed it earlier and data-remember="true", clear site storage or change the data-storage-key.
  • Banner sits under the header: increase --sb-z (e.g., 100000).
  • Jitter on mobile: lower the threshold and remove the hide-on-down/show-on-up logic by setting data-show-on-scroll-up="false".
  • Collides with another sticky header: Add top margin to your header or place this snippet above the header section in the layout.
  • CTA not navigating: replace window.location.href = '/collections/all' with your live URL.

Final notes

This is a teaching example: quick to paste, easy to tweak, and perfect for posts where you want to show readers how and why it works—without Liquid or apps. If later you want store owners to control copy/colors from the Theme Customizer, you can port this logic into a Section with schema. Until then, this snippet gets you a clean, modern sticky scroll bannerthat behaves like top e-commerce sites: visible when helpful, invisible when in the way.