Here is the code
{%- comment -%}
Renders list of products in grid layout
Accepts:
product: {Object} product (required)
carousel: {Boolean} Show the product images in carousel. Default: false
image_animation: {String} Type of the image animation
text_animation: {String} Type of the text animation
animation_delay: {Number} Factor based on grid items per row
animation_anchor: {String} Hook for the AOS animation
image_aspect_ratio_setting: {String} image aspect ratio setting from section settings
section_width: {String} Section width setting ( optional )
sizes: {String} Image sizes for image render snippet ( optional )
item_index: (Number) Element index in the forloop
Usage:
{% render ‘product-grid-item’, product: product, carousel: carousel, animation: animation, animation_delay: animation_delay, image_aspect_ratio_setting: image_aspect_ratio_setting %}
{%- endcomment -%}
{%- liquid
assign heading_style = settings.heading_style
assign enable_filters = section.settings.enable_filters
assign columns = columns | default: settings.products_per_row_on_desktop | plus: 0
assign columns_mobile = columns_mobile | default: settings.products_per_row_on_mobile | plus: 0
assign counter_even = mobile_counter | modulo: 2
assign counter_second_tablet = tablet_counter | plus: 1 | modulo: 3
assign counter_third_tablet = tablet_counter | modulo: 3
assign grid_item_class = ‘’
if counter_even == 0
assign grid_item_class = grid_item_class | append: ’ grid-item–even’
endif
if columns > 2
if counter_second_tablet == 0
assign grid_item_class = grid_item_class | append: ’ grid-item–second-tablet’
elsif counter_third_tablet == 0
assign grid_item_class = grid_item_class | append: ’ grid-item–third-tablet’
endif
endif
case columns
when 1
assign size_desktop = ‘one-whole’
when 2
assign size_desktop = ‘one-half’
assign columns_tablet = 2
when 3
assign size_desktop = ‘one-third’
assign columns_tablet = 3
when 4
assign size_desktop = ‘one-quarter’
assign columns_tablet = 3
endcase
case columns_mobile
when 1
assign size_mobile = ‘mobile–one-whole’
when 2
assign size_mobile = ‘mobile–one-half’
endcase
if carousel
assign size_mobile = ‘mobile–one-whole’
assign tabindex_hidden = true
endif
comment
Preload the first couple of rows of product grid items (only the featured images, no hover or variant ones)
endcomment
assign limit = settings.products_per_row_on_desktop | times: 2
if template.name == ‘collection’ and item_index < limit
assign loading = ‘eager’
assign fetchpriority = ‘high’
assign preload = true
endif
assign on_sale = false
assign sold_out = false
assign custom_badge_metafield = false
assign single_variant = false
if product.variants.size == 1 and product.selling_plan_groups.size == 0
assign single_variant = true
endif
if product.metafields.theme.badge != blank and product.metafields.theme.badge.type == ‘single_line_text_field’
assign custom_badge_metafield = true
assign custom_badge_metafield_text = product.metafields.theme.badge.value
endif
if product.compare_at_price > product.price
assign on_sale = true
endif
unless product.available
assign sold_out = true
endunless
assign quick_buy = settings.quick_buy
assign current_variant = product.selected_or_first_available_variant
assign featured_media = product.featured_media
assign product_grid_hover = settings.product_grid_hover
assign product_grid_hover_animation = settings.product_grid_hover_animation
if product.media.size <= 1 or product_grid_hover == ‘none’
assign product_grid_hover = false
endif
capture item_unique
echo ‘product-item–’ | append: section.id | append: ‘-’ | append: product.id
endcapture
assign animations_enabled = settings.animations_enabled
assign animation_delay = animation_delay | default: 0
assign aos_delay_default = animation_delay | times: 150
assign aos_delay = aos_delay | default: aos_delay_default
assign image_animation = image_animation | default: ‘zoom-out’
assign aos_anchor_default = item_unique | prepend: ‘#’
assign aos_anchor = aos_anchor | default: animation_anchor | default: aos_anchor_default
assign text_animation = text_animation | default: ‘fade’
assign currency_code_enable = settings.currency_code_enable
assign alignment_inline = false
assign product_alignment_class = ’ product-grid-item__info–’ | append: settings.content_alignment
if settings.content_alignment == ‘inline’
assign alignment_inline = true
endif
assign has_colors = false
if settings.enable_color_swatches_collection and product.has_only_default_variant == false
capture swatch_translation
echo ‘general.swatches.color’ | t
endcapture
assign swatch_labels = swatch_translation | remove: ’ ’ | replace: ', ‘, ‘,’ | replace: ’ ,’, ‘,’ | split: ‘,’
for label in swatch_labels
assign sanitized_label = label | strip
if product.options_by_name[sanitized_label].values.size > 0
assign color_label = label
assign has_colors = true
break
endif
endfor
endif
assign color_option = product.options_by_name[color_label].values
capture cart_icon
case settings.cart_icon
when ‘bag’
render ‘icon-shopping-bag’
when ‘shopping_cart’
render ‘icon-shopping-cart’
endcase
endcapture
-%}
{%- capture product_title -%}
<a class=“product-grid-item__title {{ heading_style }}” href=“{{ product.url | within: collection }}” aria-label=“{{ product.title | strip_html | escape }}”{% if index > 1 %} tabindex=“-1”{% endif %} data-grid-link>
{{- product.title | strip_html | escape -}}
{%- endcapture -%}
{%- capture info_separator -%}
{%- endcapture -%}
{%- capture product_info_price -%}
<a class=“product-grid-item__price price” href=“{{ product.url | within: collection }}”{% if index > 1 %} tabindex=“-1”{% endif %} data-grid-link>
{%- liquid
if product.price == 0 and product.price_varies == false
assign product_price = ‘products.product.free’ | t
assign product_price_min = ‘products.product.free’ | t
elsif currency_code_enable
assign product_price = product.price | money_with_currency
assign product_price_min = product.price_min | money_with_currency
else
assign product_price = product.price | money
assign product_price_min = product.price_min | money
endif
-%}
{%- if product.price_varies -%}
{{- ‘products.general.from_text_html’ | t: price: product_price_min -}}
{%- else -%}
{%- if on_sale -%}
{{ product_price }}
{%- if currency_code_enable -%}
{{- product.compare_at_price | money_with_currency -}}
{%- else -%}
{{- product.compare_at_price | money -}}
{%- endif -%}
{%- else -%}
{{- product_price -}}
{%- endif -%}
{%- endif -%}
{%- if current_variant.unit_price_measurement -%}
{%- capture unit_price_separator -%}
/ {{ ‘general.accessibility.unit_price_separator’ | t }}
{%- endcapture -%}
{{ current_variant.unit_price | money }}
{{ unit_price_separator }}
{%- if current_variant.unit_price_measurement.reference_value != 1 -%}
{{- current_variant.unit_price_measurement.reference_value -}}
{%- endif -%}
{{ current_variant.unit_price_measurement.reference_unit }}
{%- endif -%}
{%- endcapture -%}
{%- comment -%} Image {%- endcomment -%}
{%- liquid
assign grid_image_size = settings.image_size
assign image = featured_media.preview_image
assign image_aspect_ratio = image.aspect_ratio | default: 1
assign image_container_class = 'product__media__container'
if image_aspect_ratio_setting == blank
assign image_aspect_ratio_setting = settings.image_aspect_ratio
endif
if grid_image_size == ‘contain’
assign container_aspect_ratio = 1 | divided_by: image_aspect_ratio_setting
if image_aspect_ratio < container_aspect_ratio
assign image_container_class = image_container_class | append: ’ product__media__container–portrait’
else
assign image_container_class = image_container_class | append: ’ product__media__container–landscape’
endif
endif
comment
Images sizes
endcomment
assign gutter_space = columns | minus: 1 | times: 20 | append: ‘px’
assign gutter_space_tablet = columns_tablet | minus: 1 | times: 20 | append: ‘px’
assign gutter_space_mobile = 20 | append: ‘px’
if columns_mobile == 1
assign gutter_space_mobile = 0 | append: ‘px’
endif
assign wrapper_width_xlg = ‘100vw - 120px’
if section_width == ‘wrapper’
assign wrapper_width_xlg = ‘1440px - 120px’
endif
assign wrapper_width_lg = ‘100vw - 120px’
assign wrapper_width_md = ‘100vw - 40px’
assign wrapper_width_sm = ‘100vw - 40px’
if enable_filters
assign wrapper_width_xlg = wrapper_width_xlg | append: ’ - 330px’
assign wrapper_width_lg = wrapper_width_lg | append: ’ - 270px’
assign wrapper_width_md = wrapper_width_md | append: ’ - 240px’
endif
assign img_width_xlg = ‘calc((’ | append: wrapper_width_xlg | append: ’ - ’ | append: gutter_space | append: ') / ’ | append: columns | append: ‘)’
assign img_width_lg = ‘calc((’ | append: wrapper_width_lg | append: ’ - ’ | append: gutter_space | append: ') / ’ | append: columns | append: ‘)’
assign img_width_md = ‘calc((’ | append: wrapper_width_md | append: ’ - ’ | append: gutter_space_tablet | append: ') / ’ | append: columns_tablet | append: ‘)’
assign img_width_sm = ‘calc((’ | append: wrapper_width_sm | append: ’ - ’ | append: gutter_space_mobile | append: ') / ’ | append: columns_mobile | append: ‘)’
assign sizes_default = '(min-width: 1440px) ’ | append: img_width_xlg | append: ', (min-width: 1024px) ’ | append: img_width_lg | append: ', (min-width: 768px) ’ | append: img_width_md | append: ', ’ | append: img_width_sm
assign sizes = sizes | default: sizes_default
case settings.image_size
when ‘cover’
assign image_cover = true
when ‘contain’
assign image_cover = false
endcase
assign aspect_ratio = 1 | divided_by: image_aspect_ratio_setting
-%}
1 %} tabindex="-1"{% endif %} >
{%- if product.media.size > 0 -%}
{%- capture image_attributes -%}
data-product-image
data-grid-image
{%- endcapture -%}
{%- render ‘image-fill’,
cover: image_cover,
is_background: true,
img_object: image,
image_attributes: image_attributes,
sizes: sizes,
aspect_ratio: aspect_ratio,
loading: loading,
fetchpriority: fetchpriority,
preload: preload,
classes: ‘product__media product__media–featured-visible’ -%}
{%- if has_colors -%}
{%- for option in color_option -%}
{%- liquid
for variant in product.variants
if variant.options contains option
assign swatch_variant = variant
break
endif
endfor
-%}
{%- if swatch_variant.featured_image != blank -%}
{%- capture image_variant_attributes -%}
data-grid-image
data-variant-id="{{ swatch_variant.id }}"
{%- endcapture -%}
{%- capture image_secondary_variant_attributes -%}
data-variant-secondary-id="{{ swatch_variant.id }}"
{%- endcapture -%}
{%- render 'image-fill',
cover: image_cover,
is_background: true,
img_object: swatch_variant.featured_image,
image_attributes: image_variant_attributes,
sizes: sizes,
aspect_ratio: aspect_ratio,
classes: 'product__media' -%}
{%- render 'image-fill',
cover: image_cover,
is_background: true,
img_object: swatch_variant.featured_image,
image_attributes: image_secondary_variant_attributes,
sizes: sizes,
aspect_ratio: aspect_ratio,
classes: 'product__media product__media--featured-secondary' -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{{ image.alt | default: product.title | strip_html | escape }}
{%- else -%}
{%- render 'image-fill',
cover: image_cover,
is_background: true,
img_object: product.featured_image,
classes: 'product__media',
aspect_ratio: aspect_ratio,
loading: loading,
fetchpriority: fetchpriority,
preload: preload,
placeholder_svg: 'product-1' -%}
{%- endif -%}
{%- if product_grid_hover == ‘slideshow’ and product.media.size > 1 -%}
{%- for media in product.media limit: 4 -%}
{%- if media != featured_media -%}
{%- assign image = media.preview_image -%}
{%- render 'image-fill',
cover: image_cover,
is_background: true,
img_object: image,
sizes: sizes,
aspect_ratio: aspect_ratio,
classes: 'product__media' -%}
{%- endif -%}
{%- endfor -%}
{%- if product.media.size > 2 -%}
{%- endif -%}
{%- elsif product_grid_hover == 'image' and product.media[1].preview_image != blank -%}
{%- assign image = product.media[1].preview_image -%}
{%- render 'image-fill',
cover: image_cover,
is_background: true,
img_object: image,
sizes: sizes,
image_attributes: 'data-product-image-hover',
aspect_ratio: aspect_ratio,
classes: 'product__media__hover-img product__media__hover-img--visible product__media' -%}
{%- if has_colors -%}
{%- for variant in product.variants -%}
{%- assign featured_image_index = variant.featured_image.position -%}
{%- if featured_image_index > 1 -%}
{%- assign swatch_image_hover = product.media[featured_image_index].preview_image -%}
{%- if swatch_image_hover -%}
{%- capture image_attributes -%}
data-variant-id="{{ variant.id }}"
data-product-image-hover
{%- endcapture -%}
{%- render ‘image-fill’,
cover: image_cover,
is_background: true,
img_object: swatch_image_hover,
image_attributes: image_attributes,
sizes: sizes,
loading: ‘eager’,
aspect_ratio: aspect_ratio,
classes: ‘product__media product__media__hover-img’ -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- endif -%}
{%- liquid
assign show_sale_badge = settings.show_sale_badge
assign show_custom_badge = settings.show_custom_badge
assign show_sold_badge = settings.show_sold_badge
assign show_saving_badge = settings.show_saving_badge
assign sold_out_text = 'products.product.sold_out' | t
assign sale_text = 'products.product.sale' | t
if settings.show_saving_badge
assign price = current_variant.price
assign price_compare = current_variant.compare_at_price
assign price_difference = price_compare | minus: price
assign test_variants = product.variants | where: ‘compare_at_price’ | where: ‘available’, true
if test_variants.size > 0
for variant in test_variants
assign variant_price_difference = variant.compare_at_price | minus: variant.price
if variant_price_difference > price_difference
assign price = variant.price
assign price_compare = variant.compare_at_price
assign price_difference = variant_price_difference
endif
endfor
endif
if settings.currency_code_enable
assign discount = price_difference | money_with_currency | remove: ‘.00’ | remove: ‘,00’
else
assign discount = price_difference | money_without_trailing_zeros
endif
if settings.saving_badge_type == ‘percentage’
assign percent_off = price_difference | times: 1.00 | divided_by: price_compare | times: 100
assign discount = percent_off | floor | append: ‘%’
endif
if product.variants.size == 1 and price_difference > 0
assign saving_badge_text = ‘products.product.save_badge_html’ | t: discount: discount
endif
if product.variants.size > 1 and price_difference > 0
assign saving_badge_text = ‘products.product.save_badge_up_to_html’ | t: discount: discount
endif
if saving_badge_text == blank
assign show_saving_badge = false
endif
endif
-%}
{%- capture product_badges -%}
{%- if custom_badge_metafield and show_custom_badge -%}
{{ custom_badge_metafield_text }}
{%- endif -%}
{%- if sold_out and show_sold_badge -%}
{{ sold_out_text }}
{%- endif -%}
{%- if on_sale and show_sale_badge and sold_out == false and show_saving_badge == false -%}
{{ sale_text }}
{%- endif -%}
{%- if show_saving_badge -%}
{{ saving_badge_text }}
{%- endif -%}
{%- endcapture -%}
{%- unless product_badges == blank -%}
{{- product_badges -}}
{%- endunless -%}{%- unless quick_buy == 'none' -%}
{%- if single_variant -%}
{%- capture unique -%}{{ section.id }}-{{ product.id }}{%- endcapture -%}
{%- form ‘product’, product, data-product-form: ‘’, data-quickbuy-form: ‘’, id: unique, class: ‘quick__form’ -%}
<button type=“submit” name=“add” class=“btn–quick {{ settings.button_style }}” data-add-to-cart data-atc-trigger{% if index > 1 %} tabindex=“-1”{% endif %}>
{{ cart_icon }}
{{ ‘products.product.quick_view’ | t }}
{%- endform -%}
{%- else -%}
1 %} tabindex="-1"{% endif %}>
{{ cart_icon }}
{{ 'products.product.quick_view' | t }}
{%- endif -%}
{%- endunless -%}
{%- comment -%} Product info {%- endcomment -%}
{%- if has_colors -%}
{%- for option in color_option -%}
{%- liquid
for variant in product.variants
if variant.options contains option
assign swatch_variant = variant
break
endif
endfor
assign swatch_image_src=swatch_variant.featured_media.preview_image.src
if swatch_image_src
assign swatch_image_src=swatch_image_src | image_url: width: 600 | replace: ‘&width=600’, ‘’
else
assign swatch_image_src=‘’
endif
-%}
{%- endfor -%}
{%- if color_option.size > 5 -%}
{%- assign more_colors = color_option.size | minus: 4 -%}
{{ ‘general.swatches.more_colors’ | t: number: more_colors }}
{%- endif -%}
{%- endif -%}
{%- unless alignment_inline -%}
{% comment %} Title {% endcomment %}
{{ product_title }}
{%- else -%}
{% comment %} Title - Price {% endcomment %}
{{ product_title }} {{ product_info_price }}
{%- endunless -%}
{%- comment -%} Product tagline {%- endcomment -%}
{%- assign cutline_color = settings.cutline_color -%}{%- if product.metafields.theme.cutline != blank and product.metafields.theme.cutline.type == ‘single_line_text_field’ -%}
{%- liquid
capture style
case cutline_color
when ‘body’
echo ‘color: var(–text);’
when ‘accent’
echo ‘color: var(–accent);’
endcase
endcapture
-%}
{{ product.metafields.theme.cutline.value }}
{%- endif -%}
{%- unless alignment_inline -%}
{%- comment -%} Price {%- endcomment -%}
{{ product_info_price }}
{%- endunless -%}
{%- if settings.show_rating and product.metafields.reviews != empty -%}
<a href=“{{ product.url | within: collection }}” class=“product-grid-item__rating”{% if index > 1 %} tabindex=“-1”{% endif %} data-grid-link>
{%- render ‘rating’, reviews: product.metafields.reviews, show_rating_count: settings.show_rating_count, tabindex_hidden: tabindex_hidden -%}
{%- endif -%}