Just below the portion of the collection section that refreshes when facets are changed - it’s no use up at the top of the file - insert the following code:
{% comment %}
FILTER WORKING START
work out what filters we have to satisfy: tags are product level so no further work is needed, but colour, material and size are variant level and will require extra filtering to disqualify the variants that Shopify wants to show
{% endcomment %}
{%- liquid
comment
initialise the variables we'll need to track what each variant has to satisfy in order to be displayed on the page
endcomment
assign colour_match_required = false
assign material_match_required = false
assign size_match_required = false
assign colour_match_possibilities = ""
assign material_match_possibilities = ""
assign size_match_possibilities = ""
-%}
{% comment %}
iterate through all active filters and work out what our requirements are - what kind of matches are required, and what possibilities each one has
{% endcomment %}
{%- for filter in collection.filters -%}
{% for filter_value in filter.active_values %}
{%- liquid
if filter_value.param_name == "filter.v.m.custom.wildsmith_colour_family"
assign colour_match_required = true
assign colour_match_possibilities = colour_match_possibilities | append: filter_value.label | append: "|"
endif
if filter_value.param_name == "filter.v.m.custom.wildsmith_material"
assign material_match_required = true
assign material_match_possibilities = material_match_possibilities | append: filter_value.label | append: "|"
endif
if filter_value.param_name == "filter.v.t.shopify.shoe-size"
assign size_match_required = true
assign size_match_possibilities = size_match_possibilities | append: filter_value.label | append: "|"
endif
-%}
{% endfor %}
{%- endfor -%}
{%- liquid
comment
turn the strings we created above into arrays, for later matching
endcomment
assign colour_match_possibilities = colour_match_possibilities | remove_last: "|"
assign colour_match_possibilities = colour_match_possibilities | split: "|"
assign material_match_possibilities = material_match_possibilities | remove_last: "|"
assign material_match_possibilities = material_match_possibilities | split: "|"
assign size_match_possibilities = size_match_possibilities | remove_last: "|"
assign size_match_possibilities = size_match_possibilities | split: "|"
-%}
{% comment %}
FILTER WORKING END
{% endcomment %}
{% comment %}
VARIANT WORKING START
work out what variants we should show - one per colour if the colour qualifies
{% endcomment %}
{%- liquid
assign qualifying_variants = ""
assign qualifying_product_colours = ""
-%}
{% comment %}
iterate over products and variants - for each colour-level, we will check the size variants for each one. If all matches are satisifed, the 'winning' variant ID will be saved, for later checking
{% endcomment %}
{%- for product in collection.products -%}
{%- liquid
comment
tracking variable for which colour we are currently looping over - although from Shopify's admin you'd think that variants are hierarchical, in liquid land it's just a big flat array - but it is at least ordered, so different colours aren't scattered, they are in single blocks
endcomment
assign current_colour = ""
-%}
{%- for variant in product.variants -%}
{% comment %}
if at least one of the bottom-level size variants for each colour fulfils all the variant-level filter requirements, we can show that entire colour variant in the grid
it doesn't matter which size we pick - the collection page just needs a colour variant to display in an adapted version of a product card snippet which takes a variant argument to get the appropriate image and colour name
{% endcomment %}
{% comment %}
we are only interested in available variants - if unavailable it can't contribute to the success of the colour-level
{% endcomment %}
{%- if variant.available -%}
{% comment %}
The colour has changed block starts here
{% endcomment %}
{% if variant.option1 != current_colour %}
{% assign current_colour = variant.option1 %}
{% comment %}
this is a new colour - it has changed
{% endcomment %}
{%- liquid
comment
a tracking var so that if a colour qualifies we don't have to carry on checking sub-variants
endcomment
assign this_colour_qualified = false
comment
for this colour level variant, work out whether we need to satisfy the three variant level requirements
if a particular requirement is not active, it gets a free pass as below
endcomment
assign current_colour_colour_match_found = false
if colour_match_required == false
assign current_colour_colour_match_found = true
endif
assign current_colour_material_match_found = false
if material_match_required == false
assign current_colour_material_match_found = true
endif
assign current_colour_size_match_found = false
if size_match_required == false
assign current_colour_size_match_found = true
endif
-%}
{% endif %}
{% comment %}
The colour has changed block ends here
{% endcomment %}
{% comment %}
Now we check our requirements for each variant - note that each check is only performed once per colour, if we had a match earlier in the section of the variants array for the current colour, then we don't try again: efficiency!
{%endcomment %}
{% comment %}
BEGIN colour checking for this variant
foreach each colour that the user ticked in facets, we check if the lower case of that colour is equal to or contained within the lower case colour name of the variant
if yes for any variant of this colour, then that's a hit
{%endcomment %}
{% if colour_match_required and current_colour_colour_match_found == false %}
{% for colour_match_string in colour_match_possibilities %}
{%- liquid
assign dCaseRequiredColourName = colour_match_string | downcase
assign dCaseVariantColourName = variant.option1 | downcase
if dCaseVariantColourName == dCaseRequiredColourName or dCaseVariantColourName contains dCaseRequiredColourName
assign current_colour_colour_match_found = true
endif
%}
{% endfor %}
{% endif %}
{% comment %}
END colour checking for this variant
{%endcomment %}
{% comment %}
BEGIN material checking for this variant
same checks as colour, but slightly tweaked based on what I know we have as material names within the colour
{%endcomment %}
{% if material_match_required and current_colour_material_match_found == false %}
{% for material_match_string in material_match_possibilities %}
{%- liquid
assign dCaseRequiredMaterialName = material_match_string | downcase
assign dCaseVariantMaterialName = variant.option1 | downcase
case dCaseRequiredMaterialName
when "suede leather"
if dCaseVariantMaterialName contains "suede"
assign current_colour_material_match_found = true
endif
when "leather"
if dCaseVariantMaterialName contains "calf" or dCaseVariantMaterialName contains "polished" or dCaseVariantMaterialName contains "patent" or dCaseVariantMaterialName contains "grain"
assign current_colour_material_match_found = true
endif
else
if dCaseVariantMaterialName == dCaseRequiredMaterialName or dCaseVariantMaterialName contains dCaseRequiredMaterialName
assign current_colour_material_match_found = true
endif
endcase
%}
{% endfor %}
{% endif %}
{% comment %}
END colour checking for this variant
{%endcomment %}
{% comment %}
BEGIN size checking for this variant
a simple check - if the available variant has the required size string, then bingo
{%endcomment %}
{% if size_match_required and current_colour_size_match_found == false %}
{% for size_match_string in size_match_possibilities %}
{%- liquid
assign dCaseRequiredSizeName = size_match_string | downcase
assign dCaseVariantSizeName = variant.option2 | downcase
if dCaseVariantSizeName == dCaseRequiredSizeName or dCaseVariantSizeName contains dCaseRequiredSizeName
assign current_colour_size_match_found = true
endif
%}
{% endfor %}
{% endif %}
{% comment %}
END size checking for this variant
{% endcomment %}
{% comment %}
after all that, do we have all required matches for this colour variant to qualify to be shown?
{% endcomment %}
{%- liquid
if this_colour_qualified == false
if current_colour_colour_match_found and current_colour_material_match_found and current_colour_size_match_found
comment
yes, all three matched - we can add this colour to the list of variants we will display
endcomment
assign this_colour_qualified = true
comment
this is just a string to help with debugging and concept-proving
endcomment
assign qualifying_product_colours = qualifying_product_colours | append: product.title | append: " in " | append: variant.option1 | append: ", "
comment
this is the important bit - for each colour we are able to display, we need a representative variant ID to use later when we're looping through product variants deciding which we should show
endcomment
assign qualifying_variants = qualifying_variants | append: variant.id | append: "|"
endif
endif
%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
{%- liquid
comment
and turn the string of variant ids into an array
endcomment
assign qualifying_variants = qualifying_variants | remove_last: "|"
assign qualifying_variants = qualifying_variants | split: "|"
-%}
{% comment %}
VARIANT WORKING END
{% endcomment %}
{% comment %}
this is just a little bit of debugging output
{% endcomment %}
<p>colour_match_possibilities: {{ colour_match_possibilities | json }}<br />
material_match_possibilities: {{ material_match_possibilities | json }}<br />
size_match_possibilities: {{ size_match_possibilities | json }}</p>
<p>qualifying_product_colours: {{ qualifying_product_colours }}<br />
Qualifying variants: {{ qualifying_variants | json }}</p>
Your logic for whether to show a product or not then becomes very simple. Somewhere down in your liquid for the grid section will be something like:
{%- for product in collection.products -%}
//stuff
//then something like
{% render 'card-product',
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,
skip_styles: skip_card_product_styles,
quick_add: section.settings.quick_add,
section_id: section.id
%}
//more stuff
{% endfor %}
Adapt that to loop over variants too, but ONLY call the snippet if the variant ID is in our list of allowed variants. Remember to turn it into a string, because that’s what your array from above contains.
Also, create a snippet based on card-product that also takes a variant as an argument, and within it get the correct image and title etc. Use that instead of the product-card snippet
{%- for product in collection.products -%}
{% for variant in product.variants %}
{% assign variant_id_str = "" | append: variant.id | append: "" %}
{% if qualifying_variants contains variant_id_str %}
//stuff
//then something like
{% render 'card-product-variant',
card_product: product,
card_variant: variant,
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,
skip_styles: skip_card_product_styles,
quick_add: section.settings.quick_add,
section_id: section.id
%}
//more stuff
{% endif %}
{% endfor %}
{% endfor %}
I hope this provides someone else with the solution to a similar problem.
G