Shopify themes, liquid, logos, and UX
Hi everyone,
I'm using the Palo Alto Shopify Theme (Vibrant Style) and facing an issue with variant-specific images.
When I select a color (e.g., red or black), all product images are still shown instead of filtering to show only the images related to the selected variant. I’ve added alt text to match the variant media and tried some custom JS, but it’s still not working.
https://txl918zpmaxmm73h-72155496758.shopifypreview.com/products/testing-product
Has anyone successfully implemented variant-based image filtering in this theme?
Any help would be greatly appreciated!
Thanks!
Hi @MK7861 ,
Go to Online Store > Themes > Edit Code
Open product.js or theme.js (depending on the Palo Alto version) or include this code at the bottom of your main-product.liquid or product-template.liquid inside <script> tags.
Please use the code below for rectifying the problem :-
<script>
document.addEventListener("DOMContentLoaded", function () {
const productForm = document.querySelector("form[action^='/cart/add']");
if (!productForm) return;
const variantSelector = productForm.querySelector('[name="options[Color]"]');
const allImages = document.querySelectorAll('[data-media-id]');
const productGallery = document.querySelector('.product__media-list');
function filterImagesByVariant(variantValue) {
if (!productGallery) return;
allImages.forEach(img => {
const altText = img.querySelector('img')?.alt || '';
if (altText.toLowerCase() === variantValue.toLowerCase()) {
img.style.display = '';
} else {
img.style.display = 'none';
}
});
}
// Initial load
if (variantSelector) {
filterImagesByVariant(variantSelector.value);
variantSelector.addEventListener("change", function () {
filterImagesByVariant(this.value);
});
}
});
</script>
Please use this code and let me know if you need any further clarifications.
Thanks !
Hi @StevenT_A7 ,
Thanks for your suggestion!
I added the code below the product.js file as recommended, but unfortunately, it didn’t have any effect. I also tried placing it at the bottom of theme.js, but still no change.
Is there anything else I might be missing, or should I try placing it somewhere else?
Appreciate your help!
This is the media.liquid code, and I can only confirm that the product detail thumbnails loop is coming from the media.liquid file:
{%- comment -%}
Renders a media element for the product gallery.
Media types include: image, video, external_video and model.
Accepts:
- media: {Object} Media Liquid object (required)
- featured_media: {Object} Media Liquid object (required) - featured media of a given product or variant
- img_sizes: {String} - A set of media conditions for `img` sizes attribute (Optional)
- image_width: {Number} - Image width on desktop based on section settings
- unique: {String} Section id
- hide_controls: {Boolean} True or false - Hide video controls
Usage:
{%- for media in product.media -%}
{% render 'media', media: media, featured_media: featured_media, unique: unique, hide_controls: hide_controls %}
{%- endfor -%}
{%- endcomment -%}
{%- liquid
assign img_sizes = img_sizes | default: ''
assign image_widths = image_widths | default: '245, 320, 365, 410, 490, 640, 753, 848, 980, 1024, 1280, 1506, 1696, 1960'
assign image_size = '1024x1024'
assign image_zoom_size = '2048x2048'
assign view_string = 'general.accessibility.view' | t
capture media_wrapper_id
echo 'FeaturedMedia-' | append: unique | append: '-' | append: media.id
endcapture
assign media_aspect_ratio = media.aspect_ratio
unless media_aspect_ratio
assign media_aspect_ratio = 1
endunless
assign media_padding_top = 1 | divided_by: media_aspect_ratio | times: 100 | round: 1
if section.settings.image_aspect_ratio
assign media_padding_top = section.settings.image_aspect_ratio | times: 100 | round: 1
endif
assign controls = true
if hide_controls
assign controls = false
endif
capture image
assign loading = loading | default: 'lazy'
assign preload = nil
assign fetchpriority = nil
assign image_alt = media.alt | strip_html | escape
if template.name == 'product' and media == featured_media
assign loading = 'eager'
assign fetchpriority = 'high'
assign preload = true
endif
assign sizes = '(min-width: 1400px) ' | append: image_width | append: ', (min-width: 768px) calc((100vw - 40px) * 0.6 - 30px), calc(100vw - 40px)'
if section_type == 'quickview'
case settings.quick_buy_image_layout
when 'large'
assign sizes = '(min-width: 768px) 315px, calc((100vw - 50px) / 2 - 17px)'
when 'small'
assign sizes = '(min-width: 768px) 163px, calc((100vw - 50px) / 2 - 17px)'
endcase
assign image_widths = '150, 300, 315, 330, 360, 480, 540, 630, 720'
assign loading = 'eager'
assign fetchpriority = 'high'
endif
if img_sizes != ''
assign sizes = img_sizes
endif
assign image_id = media.id
assign is_simple_img = false
if media.media_type != 'image'
assign image_id = nil
assign is_simple_img = true
endif
render 'image', image: media.preview_image, simple: is_simple_img, widths: image_widths, sizes: sizes, loading: loading, fetchpriority: fetchpriority, preload: preload, id: image_id, alt: image_alt
endcapture
capture media_display
case media.media_type
when 'external_video'
if media.host == 'youtube'
echo media | external_video_url: autoplay: true, mute: true, playsinline: true, controls: controls, loop: enable_video_looping, playlist: media.external_id | external_video_tag: loading: 'lazy'
else
echo media | external_video_url: autoplay: true, muted: true, playsinline: true, controls: controls, loop: enable_video_looping | external_video_tag: loading: 'lazy'
endif
when 'video'
echo media | media_tag: image_size: image_size, class: 'media-video', autoplay: true, muted: true, loop: enable_video_looping, controls: controls
when 'model'
echo media | model_viewer_tag: image_size: image_size, toggleable: true, data-model-id: media.id
else
echo media | media_tag: class: 'media-item', image_size: image_size
endcase
endcapture
capture deferred_media_classes
echo 'product-gallery__media deferred-media'
echo ' product-gallery__media--' | append: media.id
if media.media_type == 'external_video' or media.media_type == 'video'
echo ' product-gallery__media--video'
endif
if media.media_type == 'model'
echo ' product-gallery__media--model'
endif
endcapture
-%}
{%- capture interaction_markup -%}
<button type="button"
class="deferred-media__poster"
aria-label="{{ view_string }} {{ image_alt }}"
data-deferred-media-button
>
<span class="deferred-media__poster-button">
{%- if media.media_type == 'model' -%}
{%- render 'icon-media-model' -%}
{%- else -%}
{%- render 'icon-media-video' -%}
{%- endif -%}
</span>
{{ image }}
</button>
{%- endcapture -%}
{% assign media_variant_ids = '' %}
{% for variant in product.variants %}
{% if variant.featured_media != null and variant.featured_media.id == media.id %}
{% assign media_variant_ids = media_variant_ids | append: variant.id | append: ' ' %}
{% endif %}
{% endfor %}
<div id="{{ media_wrapper_id }}"
class="product-gallery__media-slide{% unless featured_media == media %} media--hidden{% endunless %}"
data-product-slide
data-id="{{ media.id }}"
data-variant-ids="{{ media_variant_ids | strip }}"
data-aspectratio="{{ media_aspect_ratio }}"
data-media-id="{{ unique }}-{{ media.id }}"
data-type="{{ media.media_type }}"
{% if media.media_type == 'video' or media.media_type == 'external_video' %}
data-video
data-video-id="{{ media.id }}"
data-enable-video-looping="{{ enable_video_looping }}"
{% endif %}
{% if media.media_type == 'model' %}
data-model
data-model-id="{{ media.id }}"
{% endif %}
{% if media.media_type == 'external_video' %}
data-youtube-id="{{ media.external_id }}"
{% endif %}
data-product-single-media-wrapper
>
{%- if media.media_type == 'image' -%}
<div class="product-gallery__media product-gallery__media--image">
<div class="product-gallery__media-space" style="--media-padding-top: {{ media_padding_top }}%;"></div>
{{ image }}
{%- if enable_zoom and section_type != 'quickview' -%}
<a href="{{ media | product_img_url: image_zoom_size }}"
class="product-gallery__media-link"
rel="lightbox"
data-zoom-wrapper
data-image-width="{{ media.width }}"
data-image-height="{{ media.height }}">
<span class="visually-hidden">{{ 'general.accessibility.product_image_lightbox' | t }}</span>
</a>
{%- endif -%}
</div>
{%- else -%}
{%- case media.media_type -%}
{%- when 'external_video' or 'video' or 'model' -%}
<deferred-media class="{{ deferred_media_classes }}"
data-deferred-media
style="padding-top: {{ media_padding_top }}%;"
{% if media.media_type != 'external_video' %} data-loop="{{ enable_video_looping }}"{% endif %}
>
{{ interaction_markup }}
<template>
{{ media_display }}
</template>
</deferred-media>
{%- else -%}
<div class="product-gallery__media" style="padding-top: {{ media_padding_top }}%;">
{{ media_display }}
</div>
{%- endcase -%}
{%- endif -%}
</div>
Best regards,
When I used this code in the media.liquid product thumbnails loop, it worked fine
{% if product.selected_or_first_available_variant.featured_media.alt != media.alt and product.selected_or_first_available_variant.featured_media.alt != blank %}
style="display: none;"
{% endif %}
thumbnail-alt="{{ media.alt }}"
But when I click on the variant color, it doesn't change. Maybe I need some JavaScript code or CSS.
Have you seen/implemented/verified this -- https://palo-alto.presidiocreative.com/products/palo-alto-product-pages/variant-images ?
Yes, I checked, but it's not what I'm looking for. I want the thumbnails to display only when I click on a particular variant color—only the related thumbnails, not all of them. It's basically simple in the free theme, but in this theme, it's not working. I don’t know why.
I want exactly like this example: Without Shopify APP
https://variant-image-wizard.myshopify.com/products/open-front-poncho-cape?variant=39437642203249
When I used this code in the media.liquid product thumbnails loop, it worked fine
{% if product.selected_or_first_available_variant.featured_media.alt != media.alt and product.selected_or_first_available_variant.featured_media.alt != blank %}
style="display: none;"
{% endif %}
thumbnail-alt="{{ media.alt }}"
But when I click on the variant color, it doesn't change. Maybe I need some JavaScript code or CSS.
June brought summer energy to our community. Members jumped in with solutions, clicked ...
By JasonH Jun 5, 2025Learn how to build powerful custom workflows in Shopify Flow with expert guidance from ...
By Jacqui May 7, 2025Did You Know? May is named after Maia, the Roman goddess of growth and flourishing! ...
By JasonH May 2, 2025