Horizon

Hello,

Horizon theme - Show only images of selected color variant

I use the Horizon theme on Shopify.

I want customers to see only the images of the selected color variant.

Example:

- If they click Black, only black images should appear.

- If they click White, only white images should appear.

I don’t want to use any paid app.

Is there a free code or solution that works with the Horizon theme?

Thank you.

Did you check the settings? Grid shows a grid, Carousel shows a carousel.

Part of this is already free and built in. In the product admin, open each color variant and assign its image as the variant image; Horizon then swaps the main image automatically when a shopper picks Black or White. What it will not do natively is hide the other color’s thumbnails — that filtering needs either an app or a code edit to the product media gallery. One free first-party option exists: Shopify Combined Listings gives each color its own product and media set, but it requires Plus.

Thanks for the explanation. Is there a free code solution for the Horizon theme that filters the gallery so only the selected variant’s images are shown, without using a paid app or Shopify Plus?

You just assign the variant image to the variant in Admin product page, then select carousel in the media block settings in Editor. Should only show the variant selected.

This shows only the variant image assigned to the variant selected. You can press the arrows to scroll though the images, and that changes the color selection. It is super easy.

If you are looking for some weird theme code edit to have a grid but hide the grid like the last guy, here is the answer to that:

Hi @Firas_Mohamed

Yes, this can be done with custom code. Could you please send me your store URL? I’ll check your theme and then update you with the best solution.

Best regards,
Devcoder :laptop:

I fixed the following and tested on a Horizon store.

Result

  • Pick Black and only the Black images show. Pick White and it swaps to White.
  • Untagged images (size chart) stay visible for every colour.
  • Nothing is hardcoded, so it keeps working when you add or remove images.
  • Works with the grid gallery, which is Horizon’s default product layout.

The reason behind your issue:

Horizon only swaps the one image you assign to a variant. Any other image counts as shared, so it keeps showing for every colour.

There is no native setting that filters a colour’s full set of images. You can do it for free with Alt text plus a small block.

Fix:

Step 1 - tag each image

  • Products > your product > click an image.
  • In Alt text, type the colour that image belongs to (Black, White …).
  • Do this for every colour image. Leave shared images untagged.

Step 2 - add the filter

  • Online Store > Themes > Customize.
  • Open the product template, then Add block > Custom Liquid in the product section.
  • Paste this and Save:
{%- liquid
  assign vf_options = closest.product.options_with_values
-%}
<script>
(function () {
  if (window.__variantMediaFilter) return;
  window.__variantMediaFilter = true;

  var OPTIONS = [
    {%- for o in vf_options -%}
      { "values": [ {%- for v in o.values -%}{%- assign vn = v.name | default: v -%}{{ vn | json }}{%- unless forloop.last -%},{%- endunless -%}{%- endfor -%} ] }{%- unless forloop.last -%},{%- endunless -%}
    {%- endfor -%}
  ];
  if (!OPTIONS.length) return;

  var norm = function (s) { return (s || '').toLowerCase().trim(); };

  function gallerySection() {
    var g = document.querySelector('media-gallery');
    return (g && g.closest('.shopify-section')) || document.body;
  }

  function pickOption(section) {
    var alts = [].map.call(section.querySelectorAll('.product-media img'), function (i) {
      return norm(i.getAttribute('alt'));
    });
    var best = null, bestScore = 0;
    OPTIONS.forEach(function (o) {
      var score = o.values.filter(function (v) {
        var lv = norm(v);
        return lv && alts.some(function (a) { return a.indexOf(lv) > -1; });
      }).length;
      if (score > bestScore) { bestScore = score; best = o; }
    });
    return best;
  }

  function selectedValue(section, values) {
    var vals = values.map(norm);
    var r = [].filter.call(section.querySelectorAll('input:checked'), function (i) {
      return vals.indexOf(norm(i.value)) > -1;
    })[0];
    if (r) return norm(r.value);
    var s = [].filter.call(section.querySelectorAll('select'), function (el) {
      return vals.indexOf(norm(el.value)) > -1;
    })[0];
    return s ? norm(s.value) : null;
  }

  function box(m) { return m.closest('slideshow-slide') || m.closest('li') || m; }

  function apply() {
    var section = gallerySection();
    var opt = pickOption(section);
    if (!opt) return;
    var values = opt.values.map(norm);
    var selected = selectedValue(section, opt.values);
    if (!selected) return;

    var medias = section.querySelectorAll('.product-media');
    var visible = 0;
    [].forEach.call(medias, function (m) {
      var img = m.querySelector('img');
      var alt = norm(img && img.getAttribute('alt'));
      var mentions = values.filter(function (v) { return v && alt.indexOf(v) > -1; });
      var show = mentions.length === 0 || mentions.indexOf(selected) > -1;
      var el = box(m);
      el.style.display = show ? '' : 'none';
      el.hidden = !show;
      if (show) visible++;
    });
    if (!visible) {
      [].forEach.call(medias, function (m) { var el = box(m); el.style.display = ''; el.hidden = false; });
    }
  }

  function boot() {
    var section = gallerySection();
    apply();
    section.addEventListener('change', function () { setTimeout(apply, 60); });
    new MutationObserver(function (muts) {
      for (var i = 0; i < muts.length; i++) {
        if (muts[i].addedNodes && muts[i].addedNodes.length) { apply(); return; }
      }
    }).observe(section, { childList: true, subtree: true });
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
  else boot();
})();
</script>

If one image per colour is enough, you do not need code. Open the Product media block settings and turn on Hide unselected variant media. That hides the other colours’ assigned images, but any unassigned image still shows, which is why the block above exists.

Hi, thank you for sharing this solution. I tried to follow the steps, but I couldn’t get it working. Would you be able to help me implement it on my Horizon theme? I’d really appreciate it. Thank you!

share your store url with password

You can enable this option by going to Edit Theme > Product Template to do that.

Best regards,
Dan from Ryviu Product Reviews & Loyalty app.