Want personalized guidance adding this to your store?
Check out our Insiders community: https://www.skool.com/the-prompted
Members of The Prompted community receive a detailed store audit, 1-on-1 guidance for implementing new features, and access to an exclusive theme. You'll also get marketing support, the same tactics we use to spend over $100k/mo on Meta Ads.
---
In this tutorial, we're adding a sticky add to cart feature to your Shopify store. This enhancement keeps the add to cart button visible as customers scroll, making it easier for them to complete their purchase and potentially increasing your store's conversion rates.
Compatible Themes: This code should work on v15 of all free Shopify themes (Dawn, Refresh, Craft, Studio, Publisher, Crave, Origin, Taste, Colorblock, Sense, Ride, Spotlight).
Add settings to settings_schema.json
{
"name": "Sticky Add To Cart Custom",
"settings": [
{
"type": "checkbox",
"id": "enable_sticky_atc",
"label": "Use Sticky Add To Cart",
"default": false
},
{
"type": "checkbox",
"id": "enable_sticky_atc_variant_picker",
"label": "Enable Variant Picker",
"default": true
},
{
"type": "checkbox",
"id": "enable_atc_slide_out",
"label": "Slide ATC Into View",
"info": "Slides into view when default ATC button scrolls off screen",
"default": true
},
{
"type": "header",
"content": "Desktop Settings"
},
{
"type": "select",
"id": "desktop_sticky_position",
"options": [
{
"value": "top",
"label": "Top"
},
{
"value": "bottom",
"label": "Bottom"
}
],
"default": "bottom",
"label": "Desktop Sticky Position"
},
{
"type": "range",
"id": "desktop_sticky_top_offset",
"min": 0,
"max": 100,
"step": 1,
"default": 0,
"unit": "px",
"label": "Top Offset"
},
{
"type": "range",
"id": "sticky_atc_desktop_img_width",
"min": 3,
"max": 10,
"step": 1,
"default": 6,
"unit": "%",
"label": "Desktop Image Width (%)"
},
{
"type": "header",
"content": "Mobile Settings"
},
{
"type": "select",
"id": "mobile_sticky_position",
"options": [
{
"value": "top",
"label": "Top"
},
{
"value": "bottom",
"label": "Bottom"
}
],
"default": "bottom",
"label": "Mobile Sticky Position"
},
{
"type": "range",
"id": "mobile_sticky_top_offset",
"min": 0,
"max": 100,
"step": 1,
"default": 0,
"unit": "px",
"label": "Top Offset"
},
{
"type": "range",
"id": "sticky_atc_mobile_img_width",
"min": 15,
"max": 35,
"step": 1,
"default": 20,
"unit": "%",
"label": "Mobile Image Width (%)"
},
{
"type": "header",
"content": "Formatting Settings"
},
{
"id": "atc_button_background_color",
"type": "color",
"default": "#000000",
"label": "ATC Button Background Color"
},
{
"id": "atc_button_text_border_color",
"type": "color",
"default": "#FFFFFF",
"label": "ATC Button Text and Border Color"
},
{
"type": "select",
"id": "sticky_atc_title_font_style",
"options": [
{
"label": "Regular",
"value": "regular"
},
{
"label": "Bold",
"value": "bold"
},
{
"label": "Italic",
"value": "italic"
},
{
"label": "Bold Italic",
"value": "bold_italic"
}
],
"default": "bold",
"label": "Product Title Font Style"
},
{
"id": "sticky_atc_title_font_size",
"type": "range",
"min": 1,
"max": 2,
"step": 0.1,
"unit": "rem",
"default": 1.3,
"label": "Product Title Font Size"
},
{
"id": "sticky_atc_price_font_style",
"type": "select",
"options": [
{
"label": "Regular",
"value": "regular"
},
{
"label": "Bold",
"value": "bold"
},
{
"label": "Italic",
"value": "italic"
},
{
"label": "Bold Italic",
"value": "bold_italic"
}
],
"default": "regular",
"label": "Price Font Style"
},
{
"id": "sticky_atc_price_font_size",
"type": "range",
"min": 1,
"max": 2,
"step": 0.1,
"unit": "rem",
"default": 1.5,
"label": "Price Font Size"
},
{
"id": "sticky_atc_price_font_color",
"type": "color",
"default": "#000000",
"label": "Price Font Color"
}
]
}
create new snippet sticky-atc-custom.liquid
{% comment %}
Renders sticky product add to cart button.
Accepts:
- product: {Object} product object.
- block: {Object} passing the block information.
- product_form_id: {String} product form id.
- section_id: {String} id of section to which this snippet belongs.
Usage:
{% render 'sticky-atc-custom', block: block, product: product, product_form_id: product_form_id, section_id: section.id %}
{% endcomment %}
{% if settings.enable_sticky_atc %}
{% assign has_variants = true %}
{% if product.options.size == 1 and product.options.first == 'Title' %}
{% assign has_variants = false %}
{% endif %}
<div
class="product-form sticky-atc{% unless settings.enable_atc_slide_out %} always-visible{% endunless %} quick-add-hidden"
data-hide-errors="{{ gift_card_recipient_feature_active }}"
data-section-id="{{ section.id }}"
data-desktop-position="{{ settings.desktop_sticky_position }}"
data-mobile-position="{{ settings.mobile_sticky_position }}"
>
<div class="product-form__error-message-wrapper" role="alert" hidden>
<svg
aria-hidden="true"
focusable="false"
class="icon icon-error"
viewBox="0 0 13 13"
>
<circle cx="6.5" cy="6.50049" r="5.5" stroke="white" stroke-width="2"/>
<circle cx="6.5" cy="6.5" r="5.5" fill="#EB001B" stroke="#EB001B" stroke-width="0.7"/>
<path d="M5.87413 3.52832L5.97439 7.57216H7.02713L7.12739 3.52832H5.87413ZM6.50076 9.66091C6.88091 9.66091 7.18169 9.37267 7.18169 9.00504C7.18169 8.63742 6.88091 8.34917 6.50076 8.34917C6.12061 8.34917 5.81982 8.63742 5.81982 9.00504C5.81982 9.37267 6.12061 9.66091 6.50076 9.66091Z" fill="white"/>
<path d="M5.87413 3.17832H5.51535L5.52424 3.537L5.6245 7.58083L5.63296 7.92216H5.97439H7.02713H7.36856L7.37702 7.58083L7.47728 3.537L7.48617 3.17832H7.12739H5.87413ZM6.50076 10.0109C7.06121 10.0109 7.5317 9.57872 7.5317 9.00504C7.5317 8.43137 7.06121 7.99918 6.50076 7.99918C5.94031 7.99918 5.46982 8.43137 5.46982 9.00504C5.46982 9.57872 5.94031 10.0109 6.50076 10.0109Z" fill="white" stroke="#EB001B" stroke-width="0.7">
</svg>
<span class="product-form__error-message"></span>
</div>
<div class="content-container">
<div class="sticky-atc-mobile">
<div class="sticky-atc-title-mobile">{{ product.title }}</div>
<div class="sticky-atc-mobile-flex">
<div class="sticky-atc-image">
{% if product.selected_or_first_available_variant.image %}
{% assign image = product.selected_or_first_available_variant.image %}
<img
src="{{ image.src | image_url }}"
alt="Selected Variant Image"
id="selectedVariantImage-mobile"
width="{{ image.width }}"
height="{{ image.width | divided_by: image.aspect_ratio | round }}"
loading="lazy"
>
{% elsif has_variants == false and product.images.size > 0 %}
{% assign image = product.images.first %}
<img
src="{{ image.src | image_url }}"
alt="Product Image"
id="selectedVariantImage-mobile"
width="{{ image.width }}"
height="{{ image.width | divided_by: image.aspect_ratio | round }}"
loading="lazy"
>
{% endif %}
</div>
<div class="sticky-atc-mobile-content">
<div class="sticky-atc-variants">
{% if settings.enable_sticky_atc_variant_picker %}
{%- unless product.has_only_default_variant -%}
<variant-selects
id="variant-selects-mobile-{{ section.id }}"
class="sticky-atc-variant-selects"
data-section="{{ section.id }}"
{{ block.shopify_attributes }}
>
{%- for option in product.options_with_values -%}
{%- liquid
assign swatch_count = option.values | map: 'swatch' | compact | size
assign picker_type = 'dropdown'
if swatch_count > 0 and block.settings.swatch_shape != 'none'
assign picker_type = 'swatch_dropdown'
endif
-%}
<div class="product-form__input product-form__input--dropdown">
<label class="form__label" for="Option-mobile-{{ section.id }}-{{ forloop.index0 }}">
{{ option.name }}
</label>
<div class="select">
{%- if picker_type == 'swatch_dropdown' -%}
<span
data-selected-value
class="dropdown-swatch"
>
{% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
</span>
{%- endif -%}
<select
id="Option-mobile-{{ section.id }}-{{ forloop.index0 }}"
class="select__select"
name="options[{{ option.name | escape }}]"
form="{{ product_form_id }}"
>
{% render 'product-variant-options',
product: product,
option: option,
block: block,
picker_type: picker_type
%}
</select>
{% render 'icon-caret' %}
</div>
</div>
{%- endfor -%}
<script type="application/json" data-selected-variant>{{ product.selected_or_first_available_variant | json }}</script>
</variant-selects>
{%- endunless -%}
{% else %}
<div class="sticky-atc-active-variant">
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<div class="sticky-atc-active-variant__option">
<span class="sticky-atc-active-variant__option-name">{{ option.name }}:</span>
<span class="sticky-atc-active-variant__option-value">{{ product.selected_or_first_available_variant.options[forloop.index0] }}</span>
</div>
{% endfor %}
{% endunless %}
</div>
{% endif %}
</div>
<div class="sticky-atc-price">
<div id="price-mobile-{{ section.id }}" role="status" {{ block.shopify_attributes }}>
{%- render 'price',
product: product,
use_variant: true,
show_badges: false
-%}
</div>
</div>
</div>
</div>
</div>
<div class="sticky-atc-desktop">
<div class="sticky-atc-image">
{% if product.selected_or_first_available_variant.image %}
{% assign image = product.selected_or_first_available_variant.image %}
<img
src="{{ image.src | image_url }}"
alt="Selected Variant Image"
id="selectedVariantImage-desktop"
width="{{ image.width }}"
height="{{ image.width | divided_by: image.aspect_ratio | round }}"
loading="lazy"
>
{% elsif has_variants == false and product.images.size > 0 %}
{% assign image = product.images.first %}
<img
src="{{ image.src | image_url }}"
alt="Product Image"
id="selectedVariantImage-desktop"
width="{{ image.width }}"
height="{{ image.width | divided_by: image.aspect_ratio | round }}"
loading="lazy"
>
{% endif %}
</div>
<div class="sticky-atc-title">
<div class="selected-variant-title sticky-atc-title-desktop">{{ product.title }}</div>
</div>
<div class="sticky-atc-variants">
{% if settings.enable_sticky_atc_variant_picker %}
{%- unless product.has_only_default_variant -%}
<variant-selects
id="variant-selects-desktop-{{ section.id }}"
class="sticky-atc-variant-selects"
data-section="{{ section.id }}"
{{ block.shopify_attributes }}
>
{%- for option in product.options_with_values -%}
{%- liquid
assign swatch_count = option.values | map: 'swatch' | compact | size
assign picker_type = 'dropdown'
if swatch_count > 0 and block.settings.swatch_shape != 'none'
assign picker_type = 'swatch_dropdown'
endif
-%}
<div class="product-form__input product-form__input--dropdown">
<label class="form__label" for="Option-desktop-{{ section.id }}-{{ forloop.index0 }}">
{{ option.name }}
</label>
<div class="select">
{%- if picker_type == 'swatch_dropdown' -%}
<span
data-selected-value
class="dropdown-swatch"
>
{% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
</span>
{%- endif -%}
<select
id="Option-desktop-{{ section.id }}-{{ forloop.index0 }}"
class="select__select"
name="options[{{ option.name | escape }}]"
form="{{ product_form_id }}"
>
{% render 'product-variant-options',
product: product,
option: option,
block: block,
picker_type: picker_type
%}
</select>
{% render 'icon-caret' %}
</div>
</div>
{%- endfor -%}
<script type="application/json" data-selected-variant>{{ product.selected_or_first_available_variant | json }}</script>
</variant-selects>
{%- endunless -%}
{% else %}
<div class="sticky-atc-active-variant">
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<div class="sticky-atc-active-variant__option">
<span class="sticky-atc-active-variant__option-name">{{ option.name }}:</span>
<span class="sticky-atc-active-variant__option-value">{{ product.selected_or_first_available_variant.options[forloop.index0] }}</span>
</div>
{% endfor %}
{% endunless %}
</div>
{% endif %}
</div>
<div class="sticky-atc-price">
<div id="price-desktop-{{ section.id }}" role="status" {{ block.shopify_attributes }}>
{%- render 'price',
product: product,
use_variant: true,
show_badges: false
-%}
</div>
</div>
</div>
<div class="atc-button-container">
<button
type="submit"
name="add"
form="{{ product_form_id }}"
class="product-form__submit button button--full-width button--primary sticky-atc-button"
{% if product.selected_or_first_available_variant.available == false or quantity_rule_soldout %}
disabled
{% endif %}
>
<span>
{%- if product.selected_or_first_available_variant.available == false or quantity_rule_soldout -%}
{{ 'products.product.sold_out' | t }}
{%- else -%}
{{ 'products.product.add_to_cart' | t }}
{%- endif -%}
</span>
{%- render 'loading-spinner' -%}
</button>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var atcElement = document.querySelector(".sticky-atc");
var targetElement = document.querySelector('[id^="ProductSubmitButton-"]');
var enableSlideOut = {{ settings.enable_atc_slide_out | json }};
var desktopPosition = atcElement.dataset.desktopPosition;
var mobilePosition = atcElement.dataset.mobilePosition;
function updateStickyPosition() {
var isMobile = window.innerWidth < 750;
var position = isMobile ? mobilePosition : desktopPosition;
atcElement.style.top = position === 'top' ? '-200px' : 'auto';
atcElement.style.bottom = position === 'bottom' ? '-200px' : 'auto';
}
updateStickyPosition();
window.addEventListener('resize', updateStickyPosition);
if (enableSlideOut) {
window.addEventListener("scroll", function() {
var isTargetElementOutOfView = targetElement.getBoundingClientRect().top < 0;
if (isTargetElementOutOfView) {
atcElement.classList.add('show');
} else {
atcElement.classList.remove('show');
}
});
} else {
atcElement.classList.add('show');
}
});
</script>
<style>
.sticky-atc-content {
display: flex;
flex-direction: column;
gap: 10px;
}
.sticky-atc-mobile,
.sticky-atc-desktop {
width: 100%;
}
#selectedVariantImage-mobile,
#selectedVariantImage-desktop {
width: 100%;
height: 100%;
object-fit: cover;
}
.sticky-atc {
position: fixed;
left: 0;
z-index: 2;
width: 100%;
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 10px;
transition: top 0.3s ease-out, bottom 0.3s ease-out;
}
.sticky-atc.show {
top: auto;
bottom: auto;
}
.sticky-atc.always-visible {
top: auto !important;
bottom: auto !important;
}
.sticky-atc .sticky-atc-variant-selects {
display: flex;
flex-direction: column;
gap: 5px;
}
.sticky-atc-active-variant {
display: flex;
flex-wrap: wrap;
gap: 0 20px;
}
.sticky-atc-active-variant__option {
font-size: 14px;
color: rgb(var(--color-foreground));
}
.sticky-atc-active-variant__option-name {
font-weight: normal;
margin-right: 5px;
}
.sticky-atc-active-variant__option-value {
color: rgb(var(--color-foreground));
}
.sticky-atc-variants select {
height: 3.2rem;
}
.sticky-atc-variants .product-form__input {
margin: 0;
}
.sticky-atc-price {
display: flex;
justify-content: center;
align-items: center;
}
.atc-button-container .form {
display: flex;
align-items: center;
justify-content: center;
}
.product-form__buttons.sticky-product-form__buttons {
width: 100%;
}
.atc-button-container .product-form__submit {
margin: 1rem 0;
}
.atc-button-container .button {
color: {{ settings.atc_button_text_border_color }};
background-color: {{ settings.atc_button_background_color }};
}
.atc-button-container .button:after {
box-shadow: 0 0 0 calc(var(--buttons-border-width) + var(--border-offset))
rgba(var(--color-button-text), var(--border-opacity)),
0 0 0 var(--buttons-border-width) {{ settings.atc_button_text_border_color }};
}
@media (max-width: 749px) {
.sticky-atc-desktop {
display: none;
}
.sticky-atc.show,
.sticky-atc.always-visible {
{% if settings.mobile_sticky_position == "top" %}
top: {{ settings.mobile_sticky_top_offset }}px !important;
bottom: auto !important;
{% else %}
bottom: 0 !important;
top: auto !important;
{% endif %}
}
.sticky-atc-mobile-content {
flex: 0 0 75%;
}
.sticky-atc-mobile-flex {
display: flex;
align-items: flex-start;
gap: 10px;
margin: 0 10px;
}
.sticky-atc-image {
flex: 0 0 {{ settings.sticky_atc_mobile_img_width }}%;
max-width: 120px;
}
.sticky-atc-title-mobile {
font-weight: {% if settings.sticky_atc_title_font_style == 'bold' or settings.sticky_atc_title_font_style == 'bold_italic' %}bold{% else %}normal{% endif %};
font-style: {% if settings.sticky_atc_title_font_style == 'italic' or settings.sticky_atc_title_font_style == 'bold_italic' %}italic{% else %}normal{% endif %};
font-size: {{ settings.sticky_atc_title_font_size }}rem;
margin-bottom: 5px;
color: rgb(var(--color-foreground));
}
.sticky-atc .product-form__input--dropdown {
width: 100%;
max-width: none;
display: flex;
align-items: center;
}
.sticky-atc .product-form__input--dropdown .form__label {
flex: 0 0 auto;
width: 20%;
margin-right: 10px;
margin-bottom: 0;
font-size: 1.2rem;
}
.sticky-atc .product-form__input--dropdown .select {
flex: 1 1 auto;
}
.sticky-atc .product-form__input--dropdown select {
width: 100%;
height: 2.8rem;
}
.sticky-atc-variants {
margin-bottom: 0;
max-width: 40rem;
}
.sticky-atc-active-variant {
display: block;
}
.sticky-atc-active-variant__option {
display: block;
line-height: normal;
}
.sticky-atc-active-variant__option-name,
.sticky-atc-active-variant__option-value {
display: inline-block;
}
.sticky-atc-price {
justify-content: normal;
{% if settings.enable_sticky_atc_variant_picker %}
margin: 2px 0;
{% endif %}
}
.sticky-atc-mobile .sticky-atc-price .price__container {
color: {{ settings.sticky_atc_price_font_color }};
font-size: {{ settings.sticky_atc_price_font_size }}rem;
font-weight: {% if settings.sticky_atc_price_font_style == 'bold' or settings.sticky_atc_price_font_style == 'bold_italic' %}bold{% else %}normal{% endif %};
font-style: {% if settings.sticky_atc_price_font_style == 'italic' or settings.sticky_atc_price_font_style == 'bold_italic' %}italic{% else %}normal{% endif %};
}
.atc-button-container {
margin: 0 10px;
}
.atc-button-container .product-form__submit {
margin: 0;
}
}
@media (min-width: 750px) {
.sticky-atc {
padding: 10px 20px;
}
.sticky-atc-mobile {
display: none;
}
.sticky-atc.show,
.sticky-atc.always-visible {
{% if settings.desktop_sticky_position == "top" %}
top: {{ settings.desktop_sticky_top_offset }}px !important;
bottom: auto !important;
{% else %}
bottom: 0 !important;
top: auto !important;
{% endif %}
}
.sticky-atc .content-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
}
.sticky-atc-desktop {
display: flex;
align-items: center;
gap: 20px;
flex: 1;
}
.sticky-atc-image {
flex: 0 0 {{ settings.sticky_atc_desktop_img_width }}%;
min-width: 50px;
max-width: 75px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
#selectedVariantImage-desktop {
width: 100%;
height: 100%;
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
.sticky-atc-title {
/* flex: 0 0 25%; */
}
.sticky-atc-variants {
flex: 0 0 50%;
}
.sticky-atc-price {
flex: 0 0 15%;
}
.atc-button-container {
flex: 0 0 20%;
max-width: 40rem;
}
.sticky-atc-title-desktop {
font-size: {{ settings.sticky_atc_title_font_size }}rem;
font-weight: {% if settings.sticky_atc_title_font_style == 'bold' or settings.sticky_atc_title_font_style == 'bold_italic' %}bold{% else %}normal{% endif %};
font-style: {% if settings.sticky_atc_title_font_style == 'italic' or settings.sticky_atc_title_font_style == 'bold_italic' %}italic{% else %}normal{% endif %};
color: rgb(var(--color-foreground));
}
.sticky-atc-desktop .sticky-atc-price .price__container {
color: {{ settings.sticky_atc_price_font_color }};
font-size: {{ settings.sticky_atc_price_font_size }}rem;
font-weight: {% if settings.sticky_atc_price_font_style == 'bold' or settings.sticky_atc_price_font_style == 'bold_italic' %}bold{% else %}normal{% endif %};
font-style: {% if settings.sticky_atc_price_font_style == 'italic' or settings.sticky_atc_price_font_style == 'bold_italic' %}italic{% else %}normal{% endif %};
}
.sticky-atc .sticky-atc-variant-selects {
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
}
.sticky-atc .product-form__input--dropdown {
flex: 1 1 auto;
min-width: 50px;
max-width: 250px;
}
}
@media (min-width: 990px) {
{% if settings.desktop_sticky_position == "top" %}
.sticky-atc.show {
top: {{ settings.desktop_sticky_top_offset }}px;
}
{% endif %}
.sticky-atc-active-variant {
gap: 0 40px;
}
}
@media (min-width: 750px) and (max-width: 1099px) {
.sticky-atc-variants {
flex: 0 0 40%;
}
}
@media (min-width: 1440px) {
.sticky-atc {
padding: 10px 40px;
}
}
</style>
{% endif %}
Update main-product.liquid
Add the following variable assignment
{% assign variant_picker_block = block %}
Render the sticky add to cart
{% render 'sticky-atc-custom', block: variant_picker_block, product: product, product_form_id: product_form_id, section_id: section.id %}
Update product-info.js
Create new method updateStickyATC
updateStickyATC(html) {
const newStickyATCContainer = html.querySelector('.sticky-atc');
const currentStickyATCContainer = document.querySelector('.sticky-atc');
if (newStickyATCContainer && currentStickyATCContainer) {
const newContent = newStickyATCContainer.innerHTML.trim();
if (newContent === '') {
currentStickyATCContainer.classList.add('hidden');
currentStickyATCContainer.innerHTML = '';
} else {
currentStickyATCContainer.classList.remove('hidden');
currentStickyATCContainer.innerHTML = newContent;
}
}
}
Call updateStickyATC in the handleUpdateProductInfo method
this.updateStickyATC(html);