Shopify product page variant option image

Hey, I’m Rakeshraj Mahakud, a passionate full-stack developer and digital problem solver from India. I specialize in building high-performance web applications and custom Shopify solutions that are not only functional but user-centric.
I enjoy turning complex challenges into clean, scalable code—whether it’s crafting fast-loading storefronts, designing interactive UIs, or building backend logic that just works.
When I’m not coding, you’ll find me exploring the intersection of design and development, leveling up on modern tech stacks, or documenting what I learn to help the dev community.
🔧 Tech I love working with:
JavaScript, TypeScript, Liquid, React, Next.js, Node.js, Express, Shopify theme & app development ,Firebase, MongoDB, REST APIs, GSAP, Splide, Tailwind CSS
💡 On Hashnode, I write about Web development tips & real-world problemsShopify dev hacks & theme customization Performance optimization & clean UI/UX Dev tools, productivity, and beyond
1. Turn off “Show second image on hover” in the theme editor
Create a new snippet file card-variant-swatch-custom.liquid
Copy Code
{% comment %}
Description:
Renders product variant swatch options for collection page based on image URLs.
Defaults to buttons if no image URL is present.
Accepts:
- product: {Object} product object.
- option: {Object} current product_option object.
- variant_images_data: list of variant images
- lazy_load_all_variants: lazy load all variant images for immediate availability when clicking swatches
Usage:
{% render 'variant-swatch-collection',
product: product,
option: option,
variant_images_data: variant_images_data,
lazy_load_all_variants: lazy_load_all_variants
%}
{% endcomment %}
{% assign base_store_files_url = '//YOUR-SHOP-NAME.myshopify.com/cdn/shop/files/' %}
{% assign product_form_id = 'product-form-' | append: product.id %}
{% liquid
assign swatchStyle = settings.CollectionswatchStyle
assign swatchSize = settings.CollectionswatchSize
%}
{% for value in option.values %}
{% assign variant_image_url = nil %}
{% assign variant_id = nil %}
{% assign option_disabled = true %}
{% assign first_match_found = false %}
{% for variant in product.variants %}
{% if first_match_found == false %}
{% case option.position %}
{% when 1 %}
{% if variant.option1 == value %}
{% assign variant_image_url = variant.featured_media | img_url: '300x300' %}
{% assign variant_id = variant.id %}
{% if lazy_load_all_variants %}
<!-- Preload image -->
<img
src="{{ variant_image_url }}"
alt="{{ variant.title }}"
style="display: none;"
loading="lazy"
data-variant-id="{{ variant.id }}"
width="{{ variant.featured_media.width }}"
height="{{ variant.featured_media.height }}">
{% endif %}
{% if variant.available %}
{% assign option_disabled = false %}
{% endif %}
{% assign first_match_found = true %}
{% endif %}
{% when 2 %}
{% if variant.option2 == value and variant.option1 == product.selected_or_first_available_variant.option1 %}
{% assign variant_image_url = variant.featured_media | img_url: '300x300' %}
{% assign variant_id = variant.id %}
{% if lazy_load_all_variants %}
<!-- Preload image -->
<img
src="{{ variant_image_url }}"
alt="{{ variant.title }}"
style="display: none;"
loading="lazy"
data-variant-id="{{ variant.id }}"
width="{{ variant.featured_media.width }}"
height="{{ variant.featured_media.height }}">
{% endif %}
{% if variant.available %}
{% assign option_disabled = false %}
{% endif %}
{% assign first_match_found = true %}
{% endif %}
{% when 3 %}
{% if variant.option3 == value and variant.option1 == product.selected_or_first_available_variant.option1 and variant.option2 == product.selected_or_first_available_variant.option2 %}
{% assign variant_image_url = variant.featured_media | img_url: '300x300' %}
{% assign variant_id = variant.id %}
{% if lazy_load_all_variants %}
<!-- Preload image -->
<img
src="{{ variant_image_url }}"
alt="{{ variant.title }}"
style="display: none;"
loading="lazy"
data-variant-id="{{ variant.id }}"
width="{{ variant.featured_media.width }}"
height="{{ variant.featured_media.height }}">
{% endif %}
{% if variant.available %}
{% assign option_disabled = false %}
{% endif %}
{% assign first_match_found = true %}
{% endif %}
{% endcase %}
{% endif %}
{% if variant_image_url %}
{% for item in variant_images_data %}
{% if item.variant_value == value %}
{% assign variant_filename = item.variant_swatch %}
{% unless variant_filename == blank %}
{% assign variant_image_url = base_store_files_url | append: variant_filename %}
{% endunless %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
<div class="collection-product-card__swatch">
<input
type="radio"
id="collection-{{ section.id }}-{{ product.id }}-{{ option.position }}-{{ forloop.index0 }}"
name="collection-{{ section.id }}-{{ product.id }}-{{ option.name }}"
value="{{ value | escape }}"
form="{{ product_form_id }}"
data-section-id="{{ section.id }}"
data-product-id="{{ product.id }}"
data-variant-id="{{ variant_id }}"
data-image-url="{{ variant_image_url }}"
{% if option.selected_value == value %}
checked
{% endif %}
{% if option_disabled %}
class="disabled"
{% endif %}
>
<label
class="color-swatch {% case swatchStyle %} {% when "round" %}round{% when "square-round-corners" %}square-round-corners{% endcase %}" style="background:url('{{ variant_image_url }}');"
for="collection-{{ section.id }}-{{ product.id }}-{{ option.position }}-{{ forloop.index0 }}">
<span class="visually-hidden">{{ 'products.product.variant_sold_out_or_unavailable' | t }}</span>
</label>
</div>
{% endfor %}
<style>
.collection-product-card__swatch {
display: inline-block;
margin-top: 5px;
margin-right: 5px;
}
.collection-product-card__swatch input {
display: none;
}
.collection-product-card__swatch label {
display: block;
width: {{swatchSize}}px; /* Adjust for desired swatch size */
height: {{swatchSize}}px; /* Adjust for desired swatch size */
border: 1px solid #777;
border-radius: 50%; /* 50% for circle, 0% for square */
background-size: cover !important;
cursor: pointer;
transition: border-color 0.3s ease;
}
/* .collection-product-card__swatch label{
{% if swatchStyle == "round" %}
.collection-product-card__swatch + label.round,
.collection-product-card__swatch + label.round:after,
.collection-product-card__swatch + label.round:before{
border-radius: 100% !important;
}
{% endif %}
{% if swatchStyle == "square" %}
.collection-product-card__swatch + label,
.collection-product-card__swatch + label:after,
.collection-product-card__swatch + label:before{
border-radius: 0 !important;
}
{% endif %}
{% if swatchStyle == "round" %}
.collection-product-card__swatch + label,
.collection-product-card__swatch + label:after,
.collection-product-card__swatch + label:before{
border-radius: 5px !important;
}
{% endif %}
} */
.collection-product-card__swatch label:hover {
border-color: #333 !important;
}
.collection-product-card__swatch input:checked + label {
border-color: #333;
border-width: 2px;
box-shadow: inset 0 0 0 1px #fff;
}
.collection-product-card__swatch input.disabled + label {
opacity: 0.5;
position: relative;
}
.collection-product-card__swatch input.disabled + label::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: linear-gradient(to bottom right, transparent 45%, rgba(255, 0, 0, 0.6) 50%, transparent 55%);
pointer-events: none;
}
.square{
border-radius: 100% !important;
}
.square{
border-radius: 0px !important;
}
.square-round-corners{
border-radius: 5px !important;
}
</style>
<script>
var colorSwatch = document.querySelectorAll('label.color-swatch');
{% if swatchStyle == "square" %}
console.log(colorSwatch);
colorSwatch.forEach(function(label) {
// Add the additional class, for example, "new-class"
label.classList.add('square');
});
{% endif %}
{% if swatchStyle == "round" %}
console.log(colorSwatch);
colorSwatch.forEach(function(label) {
// Add the additional class, for example, "new-class"
label.classList.add('round');
});
{% endif %}
{% if swatchStyle == "square-round-corners" %}
console.log(colorSwatch);
colorSwatch.forEach(function(label) {
// Add the additional class, for example, "new-class"
label.classList.add('square-round-corners');
});
{% endif %}
</script>
Create a new snippet file card-product-custom.liquid
Copy Code
{% comment %}
Renders a product card
Accepts:
- card_product: {Object} Product Liquid object (optional)
- media_aspect_ratio: {String} Size of the product image card. Values are "square" and "portrait". Default is "square" (optional)
- image_shape: {String} Image mask to apply to the product image card. Values are "arch", "blob", "chevronleft", "chevronright", "diamond", "parallelogram", and "round". (optional)
- show_secondary_image: {Boolean} Show the secondary image on hover. Default: false (optional)
- show_vendor: {Boolean} Show the product vendor. Default: false
- show_rating: {Boolean} Show the product rating. Default: false
- extend_height: {Boolean} Card height extends to available container space. Default: true (optional)
- lazy_load: {Boolean} Image should be lazy loaded. Default: true (optional)
- show_quick_add: {Boolean} Show the quick add button.
- section_id: {String} The ID of the section that contains this card.
- horizontal_class: {Boolean} Add a card--horizontal class if set to true. Default: false (optional)
- horizontal_quick_add: {Boolean} Changes the quick add button styles when set to true. Default: false (optional)
- placeholder_image: {String} The placeholder image to use when no product exists. Default: 'product-apparel-2' (optional)
Usage:
{% render 'card-product', show_vendor: section.settings.show_vendor %}
{% endcomment %}
{{ 'component-rating.css' | asset_url | stylesheet_tag }}
{{ 'component-volume-pricing.css' | asset_url | stylesheet_tag }}
{% assign variant_option_name = "Color" %} {% comment %}Setting: Variant Option Name To Display On Collection Page{% endcomment %}
{% assign entry_title = "variant-swatch-mapping" %} {% comment %}Setting: Name of Metaobject entry{% endcomment %}
{% assign lazy_load_all_variants = false %} {% comment %}Setting: Turn Lazy Load on or off. true: ON, false: OFF{% endcomment %}
{%- if card_product and card_product != empty -%}
{%- liquid
assign ratio = 1
if card_product.featured_media and media_aspect_ratio == 'portrait'
assign ratio = 0.8
elsif card_product.featured_media and media_aspect_ratio == 'adapt'
assign ratio = card_product.featured_media.aspect_ratio
endif
if ratio == 0 or ratio == null
assign ratio = 1
endif
-%}
{% assign target_entry = nil %}
{% for entry in shop.metaobjects.variant_swatch_map.values %}
{% if entry.title == entry_title %}
{% assign target_entry = entry %}
{% break %}
{% endif %}
{% endfor %}
{% if target_entry %}
{% assign variant_images_data = target_entry.variant_images_json %}
{% else %}
{% assign variant_images_data = nil %}
{% endif %}
<div class="card-product-custom-div" data-section-id="{{ section.id }}" data-lazy-load-all-variants="{{ lazy_load_all_variants }}">
<div class="card-wrapper product-card-wrapper underline-links-hover">
<div
class="
card card--{{ settings.card_style }}
{% if card_product.featured_media %} card--media{% else %} card--text{% endif %}
{% if settings.card_style == 'card' %} color-{{ settings.card_color_scheme }} gradient{% endif %}
{% if image_shape and image_shape != 'default' %} card--shape{% endif %}
{% if extend_height %} card--extend-height{% endif %}
{% if card_product.featured_media == nil and settings.card_style == 'card' %} ratio{% endif %}
{% if horizontal_class %} card--horizontal{% endif %}
"
style="--ratio-percent: {{ 1 | divided_by: ratio | times: 100 }}%;"
>
<div
class="card__inner {% if settings.card_style == 'standard' %}color-{{ settings.card_color_scheme }} gradient{% endif %}{% if card_product.featured_media or settings.card_style == 'standard' %} ratio{% endif %}"
style="--ratio-percent: {{ 1 | divided_by: ratio | times: 100 }}%;"
>
{%- if card_product.featured_media -%}
<div class="card__media{% if image_shape and image_shape != 'default' %} shape--{{ image_shape }} color-{{ settings.card_color_scheme }} gradient{% endif %}" id="ProductCardImage-{{ card_product.id }}"> {% comment %} Code changed here {% endcomment %}
<div class="media media--transparent media--hover-effect">
{% comment %}theme-check-disable ImgLazyLoading{% endcomment %}
<img
srcset="
{%- if card_product.featured_media.width >= 165 -%}{{ card_product.featured_media | image_url: width: 165 }} 165w,{%- endif -%}
{%- if card_product.featured_media.width >= 360 -%}{{ card_product.featured_media | image_url: width: 360 }} 360w,{%- endif -%}
{%- if card_product.featured_media.width >= 533 -%}{{ card_product.featured_media | image_url: width: 533 }} 533w,{%- endif -%}
{%- if card_product.featured_media.width >= 720 -%}{{ card_product.featured_media | image_url: width: 720 }} 720w,{%- endif -%}
{%- if card_product.featured_media.width >= 940 -%}{{ card_product.featured_media | image_url: width: 940 }} 940w,{%- endif -%}
{%- if card_product.featured_media.width >= 1066 -%}{{ card_product.featured_media | image_url: width: 1066 }} 1066w,{%- endif -%}
{{ card_product.featured_media | image_url }} {{ card_product.featured_media.width }}w
"
src="{{ card_product.featured_media | image_url: width: 533 }}"
sizes="(min-width: {{ settings.page_width }}px) {{ settings.page_width | minus: 130 | divided_by: 4 }}px, (min-width: 990px) calc((100vw - 130px) / 4), (min-width: 750px) calc((100vw - 120px) / 3), calc((100vw - 35px) / 2)"
alt="{{ card_product.featured_media.alt | escape }}"
class="motion-reduce"
{% unless lazy_load == false %}
loading="lazy"
{% endunless %}
width="{{ card_product.featured_media.width }}"
height="{{ card_product.featured_media.height }}"
>
{% comment %}theme-check-enable ImgLazyLoading{% endcomment %}
{%- if card_product.media[1] != null and show_secondary_image -%}
<img
srcset="
{%- if card_product.media[1].width >= 165 -%}{{ card_product.media[1] | image_url: width: 165 }} 165w,{%- endif -%}
{%- if card_product.media[1].width >= 360 -%}{{ card_product.media[1] | image_url: width: 360 }} 360w,{%- endif -%}
{%- if card_product.media[1].width >= 533 -%}{{ card_product.media[1] | image_url: width: 533 }} 533w,{%- endif -%}
{%- if card_product.media[1].width >= 720 -%}{{ card_product.media[1] | image_url: width: 720 }} 720w,{%- endif -%}
{%- if card_product.media[1].width >= 940 -%}{{ card_product.media[1] | image_url: width: 940 }} 940w,{%- endif -%}
{%- if card_product.media[1].width >= 1066 -%}{{ card_product.media[1] | image_url: width: 1066 }} 1066w,{%- endif -%}
{{ card_product.media[1] | image_url }} {{ card_product.media[1].width }}w
"
src="{{ card_product.media[1] | image_url: width: 533 }}"
sizes="(min-width: {{ settings.page_width }}px) {{ settings.page_width | minus: 130 | divided_by: 4 }}px, (min-width: 990px) calc((100vw - 130px) / 4), (min-width: 750px) calc((100vw - 120px) / 3), calc((100vw - 35px) / 2)"
alt=""
class="motion-reduce"
loading="lazy"
width="{{ card_product.media[1].width }}"
height="{{ card_product.media[1].height }}"
>
{%- endif -%}
</div>
</div>
{%- endif -%}
<div class="card__content">
<div class="card__information">
<h3
class="card__heading"
{% if card_product.featured_media == null and settings.card_style == 'standard' %}
id="title-{{ section_id }}-{{ card_product.id }}"
{% endif %}
>
<a
href="{{ card_product.url }}"
id="StandardCardNoMediaLink-{{ section_id }}-{{ card_product.id }}"
class="full-unstyled-link"
aria-labelledby="StandardCardNoMediaLink-{{ section_id }}-{{ card_product.id }} NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
>
{{ card_product.title | escape }}
</a>
</h3>
</div>
<div class="card__badge {{ settings.badge_position }}">
{%- if card_product.available == false -%}
<span
id="NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sold_out_badge_color_scheme }}"
>
{{- 'products.product.sold_out' | t -}}
</span>
{%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
{%- assign difference = card_product.compare_at_price | minus: card_product.price -%}
{%- assign float_difference = difference | times: 1.0 -%}
{%- assign discount_fraction = float_difference | divided_by: card_product.compare_at_price -%}
{%- assign discount_percentage = discount_fraction | times: 100 | round -%}
<span
id="NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sale_badge_color_scheme }}"
>
{% comment %}{{- 'products.product.on_sale' | t -}}{% endcomment %}
{{- discount_percentage }}% OFF
</span>
{%- endif -%}
</div>
</div>
</div>
<h3
class="card__heading{% if card_product.featured_media or settings.card_style == 'standard' %} h5{% endif %}"
{% if card_product.featured_media or settings.card_style == 'card' %}
id="title-{{ section_id }}-{{ card_product.id }}"
{% endif %}
>
<a
href="{{ card_product.url }}"
id="CardLink-{{ section_id }}-{{ card_product.id }}"
class="full-unstyled-link"
aria-labelledby="CardLink-{{ section_id }}-{{ card_product.id }} Badge-{{ section_id }}-{{ card_product.id }}"
>
</a>
</h3>
</div>
</div>
<div class = "collection-product-card__variants">
{%- for card_product_option in card_product.options_with_values -%}
{% if card_product_option.name == variant_option_name %}
{% assign variant_options_images = variant_images_data.value | where: "variant_name", card_product_option.name %}
{% assign special_handling = false %}
{% if variant_options_images.size > 0 %}
{% assign special_handling = true %}
{% endif %}
{% render 'card-variant-swatch-custom', product: card_product, option: card_product_option, variant_images_data: variant_options_images, lazy_load_all_variants: lazy_load_all_variants %}
{% endif %}
{%- endfor -%}
</div>
<div class="card-wrapper product-card-wrapper underline-links-hover">
<div
class="
card card--{{ settings.card_style }}
{% if card_product.featured_media %} card--media{% else %} card--text{% endif %}
{% if settings.card_style == 'card' %} color-{{ settings.card_color_scheme }} gradient{% endif %}
{% if image_shape and image_shape != 'default' %} card--shape{% endif %}
{% if extend_height %} card--extend-height{% endif %}
{% if card_product.featured_media == nil and settings.card_style == 'card' %} ratio{% endif %}
{% if horizontal_class %} card--horizontal{% endif %}
"
style="--ratio-percent: {{ 1 | divided_by: ratio | times: 100 }}%;"
>
<div class="card__content">
<div class="card__information">
<h3
class="card__heading{% if card_product.featured_media or settings.card_style == 'standard' %} h5{% endif %}"
{% if card_product.featured_media or settings.card_style == 'card' %}
id="title-{{ section_id }}-{{ card_product.id }}"
{% endif %}
>
<a
href="{{ card_product.url }}"
id="CardLink-{{ section_id }}-{{ card_product.id }}"
class="full-unstyled-link"
aria-labelledby="CardLink-{{ section_id }}-{{ card_product.id }} Badge-{{ section_id }}-{{ card_product.id }}"
>
{{ card_product.title | escape }}
</a>
</h3>
<div class="card-information">
{%- if show_vendor -%}
<span class="visually-hidden">{{ 'accessibility.vendor' | t }}</span>
<div class="caption-with-letter-spacing light">{{ card_product.vendor }}</div>
{%- endif -%}
<span class="caption-large light">{{ block.settings.description | escape }}</span>
{%- if show_rating and card_product.metafields.reviews.rating.value != blank -%}
{% liquid
assign rating_decimal = 0
assign decimal = card_product.metafields.reviews.rating.value.rating | modulo: 1
if decimal >= 0.3 and decimal <= 0.7
assign rating_decimal = 0.5
elsif decimal > 0.7
assign rating_decimal = 1
endif
%}
<div
class="rating"
role="img"
aria-label="{{ 'accessibility.star_reviews_info' | t: rating_value: card_product.metafields.reviews.rating.value, rating_max: card_product.metafields.reviews.rating.value.scale_max }}"
>
<span
aria-hidden="true"
class="rating-star"
style="--rating: {{ card_product.metafields.reviews.rating.value.rating | floor }}; --rating-max: {{ card_product.metafields.reviews.rating.value.scale_max }}; --rating-decimal: {{ rating_decimal }};"
></span>
</div>
<p class="rating-text caption">
<span aria-hidden="true">
{{- card_product.metafields.reviews.rating.value }} /
{{ card_product.metafields.reviews.rating.value.scale_max -}}
</span>
</p>
<p class="rating-count caption">
<span aria-hidden="true">({{ card_product.metafields.reviews.rating_count }})</span>
<span class="visually-hidden">
{{- card_product.metafields.reviews.rating_count }}
{{ 'accessibility.total_reviews' | t -}}
</span>
</p>
{%- endif -%}
{% render 'price', product: card_product, price_class: '', show_compare_at_price: true %}
{%- if card_product.quantity_price_breaks_configured? -%}
<div class="card__information-volume-pricing-note">
<span class="caption">{{ 'products.product.volume_pricing.note' | t }}</span>
</div>
{%- endif -%}
</div>
</div>
{%- if show_quick_add -%}
<div class="quick-add no-js-hidden">
{%- liquid
assign product_form_id = 'quick-add-' | append: section_id | append: card_product.id
assign qty_rules = false
if card_product.selected_or_first_available_variant.quantity_rule.min > 1 or card_product.selected_or_first_available_variant.quantity_rule.max != null or card_product.selected_or_first_available_variant.quantity_rule.increment > 1
assign qty_rules = true
endif
-%}
{%- if card_product.variants.size > 1 or qty_rules -%}
<modal-opener data-modal="#QuickAdd-{{ card_product.id }}">
<button
id="{{ product_form_id }}-submit"
type="submit"
name="add"
class="quick-add__submit button button--full-width button--secondary{% if horizontal_quick_add %} card--horizontal__quick-add animate-arrow{% endif %}"
aria-haspopup="dialog"
aria-labelledby="{{ product_form_id }}-submit title-{{ section_id }}-{{ card_product.id }}"
data-product-url="{{ card_product.url }}"
>
{{ 'products.product.choose_options' | t }}
{%- if horizontal_quick_add -%}
<span class="icon-wrap">{% render 'icon-arrow' %}</span>
{%- endif -%}
<div class="loading-overlay__spinner hidden">
<svg
aria-hidden="true"
focusable="false"
class="spinner"
viewBox="0 0 66 66"
xmlns="http://www.w3.org/2000/svg"
>
<circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
</svg>
</div>
</button>
</modal-opener>
<quick-add-modal id="QuickAdd-{{ card_product.id }}" class="quick-add-modal">
<div
role="dialog"
aria-label="{{ 'products.product.choose_product_options' | t: product_name: card_product.title | escape }}"
aria-modal="true"
class="quick-add-modal__content global-settings-popup"
tabindex="-1"
>
<button
id="ModalClose-{{ card_product.id }}"
type="button"
class="quick-add-modal__toggle"
aria-label="{{ 'accessibility.close' | t }}"
>
{% render 'icon-close' %}
</button>
<div id="QuickAddInfo-{{ card_product.id }}" class="quick-add-modal__content-info"></div>
</div>
</quick-add-modal>
{%- else -%}
<product-form data-section-id="{{ section.id }}">
{%- form 'product',
card_product,
id: product_form_id,
class: 'form',
novalidate: 'novalidate',
data-type: 'add-to-cart-form'
-%}
<input
type="hidden"
name="id"
value="{{ card_product.selected_or_first_available_variant.id }}"
class="product-variant-id"
disabled
>
<button
id="{{ product_form_id }}-submit"
type="submit"
name="add"
class="quick-add__submit button button--full-width button--secondary{% if horizontal_quick_add %} card--horizontal__quick-add{% endif %}"
aria-haspopup="dialog"
aria-labelledby="{{ product_form_id }}-submit title-{{ section_id }}-{{ card_product.id }}"
aria-live="polite"
data-sold-out-message="true"
{% if card_product.selected_or_first_available_variant.available == false %}
disabled
{% endif %}
>
<span>
{%- if card_product.selected_or_first_available_variant.available -%}
{{ 'products.product.add_to_cart' | t }}
{%- else -%}
{{ 'products.product.sold_out' | t }}
{%- endif -%}
</span>
<span class="sold-out-message hidden">
{{ 'products.product.sold_out' | t }}
</span>
{%- if horizontal_quick_add -%}
<span class="icon-wrap">{% render 'icon-plus' %}</span>
{%- endif -%}
<div class="loading-overlay__spinner hidden">
<svg
aria-hidden="true"
focusable="false"
class="spinner"
viewBox="0 0 66 66"
xmlns="http://www.w3.org/2000/svg"
>
<circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
</svg>
</div>
</button>
{%- endform -%}
</product-form>
{%- endif -%}
</div>
{%- endif -%}
<div class="card__badge {{ settings.badge_position }}">
{%- if card_product.available == false -%}
<span
id="Badge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sold_out_badge_color_scheme }}"
>
{{- 'products.product.sold_out' | t -}}
</span>
{%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
<span
id="Badge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sale_badge_color_scheme }}"
>
{{- 'products.product.on_sale' | t -}}
</span>
{%- endif -%}
</div>
</div>
</div>
</div>
</div>
{%- else -%}
<div class="card-wrapper product-card-wrapper underline-links-hover">
<div
class="
card card--{{ settings.card_style }}
{% if extend_height %} card--extend-height{% endif %}
{% if settings.card_style == 'card' %} color-{{ settings.card_color_scheme }} gradient{% endif %}
"
style="--ratio-percent: 100%;"
>
<div
class="card__inner{% if settings.card_style == 'standard' %} color-{{ settings.card_color_scheme }} gradient{% endif %} ratio"
style="--ratio-percent: 100%;"
>
<div class="card__media" id="ProductCardPlaceholderImage-{{ card_product.id }}"> {% comment %} Code changed here {% endcomment %}
<div class="media media--transparent">
{%- if placeholder_image -%}
{{ placeholder_image | placeholder_svg_tag: 'placeholder-svg' }}
{%- else -%}
{{ 'product-apparel-2' | placeholder_svg_tag: 'placeholder-svg' }}
{% endif %}
</div>
</div>
</div>
<div class="card__content">
<div class="card__information">
<h3 class="card__heading card__heading--placeholder{% if settings.card_style == 'standard' %} h5{% endif %}">
<a role="link" aria-disabled="true" class="full-unstyled-link">
{{ 'onboarding.product_title' | t }}
</a>
</h3>
<div class="card-information">
{%- if show_vendor -%}
<span class="visually-hidden">{{ 'accessibility.vendor' | t }}</span>
<div class="caption-with-letter-spacing light">{{ 'products.product.vendor' | t }}</div>
{%- endif -%}
{% render 'price', show_compare_at_price: true %}
</div>
</div>
</div>
</div>
</div>
{%- endif -%}
<style>
.card__information {
padding-top: 10px;
}
.collection-product-card__variants {
display: flex;
flex-wrap: wrap;
}
</style>
Create a new asset file card-product-variant-selection-custom.js
Copy Code
document.addEventListener('DOMContentLoaded', function() {
var productGrids = document.querySelectorAll('.grid.product-grid');
productGrids.forEach(function(productGrid) {
var sectionId = productGrid.getAttribute('data-section-id');
var variantDataMap = window['variantDataMap' + sectionId.replace(/-/g, '_')];
productGrid.addEventListener('change', function(e) {
if (e.target.matches('input[type="radio"][data-section-id="' + sectionId + '"]')) {
var card = e.target.closest(`.card-product-custom-div[data-section-id="${sectionId}"]`);
var variantId = e.target.getAttribute('data-variant-id');
var variantData = variantDataMap[variantId];
if (!variantData) {
console.log('No data found for variant:', variantId);
return;
}
// Update the product image with lazy loading logic
var productImageElement = card.querySelector('.card__media img');
if (productImageElement) {
var lazyLoadAllVariants = card.getAttribute('data-lazy-load-all-variants') === 'true';
if (lazyLoadAllVariants) {
var preloadedImage = card.querySelector(`img[data-variant-id="${variantId}"]`);
if (preloadedImage) {
productImageElement.src = preloadedImage.src;
productImageElement.srcset = preloadedImage.srcset;
} else {
console.log('Matching preloaded image not found for variant:', variantId);
}
} else {
// Update src and srcset for responsive images
var dynamicSrcset = [
variantData.imageUrl + '?width=165 165w',
variantData.imageUrl + '?width=360 360w',
variantData.imageUrl + '?width=533 533w',
variantData.imageUrl + '?width=720 720w',
variantData.imageUrl + '?width=940 940w',
variantData.imageUrl + '?width=1066 1066w'
].join(', ');
productImageElement.srcset = dynamicSrcset;
productImageElement.src = variantData.imageUrl;
}
} else {
console.log('No product image element found in this card.');
}
// Update the product URL
var productLinks = card.querySelectorAll('a[id^="CardLink-"], a[id^="StandardCardNoMediaLink-"]');
productLinks.forEach(function(link) {
link.href = variantData.productUrl;
});
} else {
console.log('Change detected, but not on a swatch.');
}
});
});
});
2. Edit main-collection-product-grid.liquid to Render Variant Swatch Snippet
Add data-section-id="{{ [section.id]({{ card_product.url }}) }}" to the ul element with class grid product-grid
Copy Code
<ul
id="product-grid"
data-id="{{ section.id }}"
class="
grid product-grid grid--{{ section.settings.columns_mobile }}-col-tablet-down
grid--{{ section.settings.columns_desktop }}-col-desktop
"
>
Replace the original card-product code in main-collection-product-grid.liquid with the following card-product-custom code:
Copy Code
{% render 'card-product-custom',
card_product: product,
media_aspect_ratio: section.settings.image_ratio,
image_shape: section.settings.image_shape,
show_secondary_image: section.settings.show_secondary_image,
show_vendor: section.settings.show_vendor,
show_rating: section.settings.show_rating,
lazy_load: lazy_load,
show_quick_add: section.settings.enable_quick_add,
section_id: section.id
%}
Add scripting to the bottom of the file
Copy Code
<script>
var variantDataMap{{ section.id | replace: '-', '_' }} = {
{% assign isFirstVariant = true %}
{% for product in collection.products %}
{% for variant in product.variants %}
{% if variant.featured_media %}
{% unless isFirstVariant %},{% endunless %}
"{{ variant.id }}": {
"imageUrl": "{{ variant.featured_media | img_url: 'master' }}",
"productUrl": "{{ product.url | append: '?variant=' | append: variant.id }}"
}
{% assign isFirstVariant = false %}
{% endif %}
{% endfor %}
{% endfor %}
};
</script>
{{ 'card-product-variant-selection-custom.js' | asset_url | script_tag }}
(Optional) Add Swatches To Featured Collections and Search
Featured Collection Page: update featured-collection.liquid code
Add data-section-id="{{ [section.id]({{ card_product.url }}) }}" to the ul element with class grid product-grid
Copy Code
<ul
id="Slider-{{ section.id }}"
data-section-id="{{ section.id }}"
class="grid product-grid contains-card contains-card--product{% if settings.card_style == 'standard' %} contains-card--standard{% endif %} grid--{{ section.settings.columns_desktop }}-col-desktop{% if section.settings.collection == blank %} grid--2-col-tablet-down{% else %} grid--{{ section.settings.columns_mobile }}-col-tablet-down{% endif %}{% if show_mobile_slider or show_desktop_slider %} slider{% if show_desktop_slider %} slider--desktop{% endif %}{% if show_mobile_slider %} slider--tablet grid--peek{% endif %}{% endif %}"
role="list"
aria-label="{{ 'general.slider.name' | t }}"
>
similar to main-collection-product-grid.liquid, update featured-collection.liquid to update the new version of card-product-custom
Copy Code
{% render 'card-product-custom',
card_product: product,
media_aspect_ratio: section.settings.image_ratio,
image_shape: section.settings.image_shape,
show_secondary_image: section.settings.show_secondary_image,
show_vendor: section.settings.show_vendor,
show_rating: section.settings.show_rating,
show_quick_add: section.settings.enable_quick_add,
section_id: section.id
%}
add scripting at the end of the file:
Copy Code
<script>
var variantDataMap{{ section.id | replace: '-', '_' }} = {
{% assign isFirstVariant = true %}
{% for product in section.settings.collection.products %}
{% for variant in product.variants %}
{% if variant.featured_media %}
{% unless isFirstVariant %},{% endunless %}
"{{ variant.id }}": {
"imageUrl": "{{ variant.featured_media | img_url: 'master' }}",
"productUrl": "{{ product.url | append: '?variant=' | append: variant.id }}"
}
{% assign isFirstVariant = false %}
{% endif %}
{% endfor %}
{% endfor %}
};
</script>
{{ 'card-product-variant-selection-custom.js' | asset_url | script_tag }}
Search Page: update main-search.liquid code
Add data-section-id="{{ [section.id]({{ card_product.url }}) }}" to the ul element with class grid product-grid
Copy Code
<ul class="grid product-grid grid--{{ section.settings.columns_mobile }}-col-tablet-down grid--{{ section.settings.columns_desktop }}-col-desktop"
role="list"
data-section-id="{{ section.id }}"
>
similar to main-collection-product-grid.liquid, update main-search.liquid to update the new version of card-product-custom
Copy Code
{% render 'card-product-custom',
card_product: item,
media_aspect_ratio: section.settings.image_ratio,
image_shape: section.settings.image_shape,
show_secondary_image: section.settings.show_secondary_image,
show_vendor: section.settings.show_vendor,
show_rating: section.settings.show_rating,
lazy_load: lazy_load,
section_id: section.id
%}
add scripting at the end of the file:
Copy Code
<script>
var variantDataMap{{ section.id | replace: '-', '_' }} = {
{% assign isFirstVariant = true %}
{% for item in search.results %}
{% if item.object_type == 'product' %}
{% for variant in item.variants %}
{% if variant.featured_media %}
{% unless isFirstVariant %},{% endunless %}
"{{ variant.id }}": {
"imageUrl": "{{ variant.featured_media | img_url: 'master' }}",
"productUrl": "{% if item.url contains '?' %}{{ item.url | append: '&variant=' | append: variant.id }}{% else %}{{ item.url | append: '?variant=' | append: variant.id }}{% endif %}"
}
{% assign isFirstVariant = false %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
};
</script>
{{ 'card-product-variant-selection-custom.js' | asset_url | script_tag }}
add schema at the top of settings_schema.json
Copy Code
{
"name": "Collection Color swatches",
"settings": [
{
"type": "select",
"id": "CollectionswatchStyle",
"label": "Swatch style",
"options": [
{
"value": "round",
"label": "Round"
},
{
"value": "square",
"label": "Square"
},
{
"value": "square-round-corners",
"label": "Square round corners"
}
],
"default": "round"
},
{
"type": "range",
"id": "CollectionswatchSize",
"min": 20,
"max": 120,
"step": 2,
"unit": "px",
"label": "Swatch size",
"default": 40
}
]
},




