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.