Skip to main content

Command Palette

Search for a command to run...

Shopify product page variant option image

Updated
17 min read
Shopify product page variant option image
R

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
}
]
},

More from this blog

Rakeshraj Mahakud

36 posts