what this replaces

Sticky ATC apps charge $9–$15/month. The native version uses a section schema, a Liquid snippet, and about 30 lines of vanilla JS. It also performs better because it doesn't inject a separate script bundle.

the html structure

<div id="sticky-atc" class="sticky-atc hidden" aria-hidden="true">
  <span class="sticky-title">{{ product.title }}</span>
  <span class="sticky-price">{{ product.selected_or_first_available_variant.price | money }}</span>
  <button class="sticky-btn" id="stickyBtn">add to cart</button>
</div>

the scroll trigger

const bar  = document.getElementById('sticky-atc');
const atcBtn = document.querySelector('.product-form__submit');

const observer = new IntersectionObserver(entries => {
  bar.classList.toggle('hidden', entries[0].isIntersecting);
  bar.setAttribute('aria-hidden', entries[0].isIntersecting);
}, { threshold: 0 });

if (atcBtn) observer.observe(atcBtn);

The IntersectionObserver hides the bar while the main ATC button is visible and shows it once the user scrolls past it — no scroll event polling.

wiring up add-to-cart

On click, submit the product form programmatically or send a POST to /cart/add.js with the selected variant ID. Mirror the same variant selector the main product form uses.

placement

Add the snippet to the bottom of your product.json template or inside main-product.liquid. Position it fixed at the top or bottom with position: fixed; top: 0; z-index: 99.