B. Gewijzigd hoe productafbeeldingen worden weergegeven

Goal

To show additional images under the Featured or Variant image on the product page, should there be any.

Hoe wordt bepaald wat er wordt getoond

  1. Look at the selected product. If that product has a list of files (images) added in the metafield: Global Images, it will display these under the Featured image.

  2. If the metafield is empty, it displays nothing.

Wat je aanmaakt in Shopify-beheer

Voer deze stappen één keer uit, als ze nog niet zijn aangemaakt

Add metafield definitions for Products
• Settings → Custom data → Products → Add definition
• Name: global_images
• Type: List of Files
• Namespace and key: custom.global_images

Voeg de content toe

You will do this in each product that needs additional images. Scroll down to the metafield called Global Images, add each image you want to display inside the metafield and save your work.

Testchecklist

Run these after you add content.

  1. Product level cards only
    • Add images in the metafield
    • Expected: the extra images appear

Final fallback
• If there are no images added in the metafield it will seem as if nothing has changed.

Voor nieuwsgierigen

• The code uses a newly build snippets/product-gallery-global.liquid and then uses this code in the main-product.liquid file alongside the regular product-gallery.liquid snippet.

Here the code used in the Snippet:

{%- assign global_images = product.metafields.custom.global_images -%}
{%- if global_images != blank -%}
<div class="product-gallery product-gallery--custom-global-images">
<div class="image-slider" style="position: relative; overflow: hidden;">
<div class="slider-track" style="display: flex; transition: transform 0.3s ease;">
{%- for media_file in global_images.value -%}
{%- if media_file.media_type == 'image' -%}
<div class="slider-item" style="min-width: 100%; flex-shrink: 0;">
<img
src="{{ media_file | image_url: width: 589 }}"
alt="{{ media_file.alt | escape | default: product.title }}"
loading="lazy"
class="product-gallery__media-image rounded"
style="display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;"
/>
</div>
{%- endif -%}
{%- endfor -%}
</div>

<!-- Navigation Arrows -->
<button class="slider-prev" style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); background-color: rgb(0, 0, 0); color: white; border: 0px solid rgba(255, 255, 255, 0.12); padding: 0; cursor: pointer; border-radius: 9999px; z-index: 10; font-size: 24px; font-weight: bold; display: flex; align-items: center; justify-content: center; width: 44.8px; height: 44.8px;"></button>
<button class="slider-next" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background-color: rgb(0, 0, 0); color: white; border: 0px solid rgba(255, 255, 255, 0.12); padding: 0; cursor: pointer; border-radius: 9999px; z-index: 10; font-size: 24px; font-weight: bold; display: flex; align-items: center; justify-content: center; width: 44.8px; height: 44.8px;"></button>

<!-- Pagination Dots -->
<div class="slider-dots" style="display: flex; justify-content: center; gap: 8px; margin-top: 16px;"></div>
</div>
</div>
<script>
(function() {
const track = document.querySelector('.slider-track');
const items = document.querySelectorAll('.slider-item');
const prevBtn = document.querySelector('.slider-prev');
const nextBtn = document.querySelector('.slider-next');
const dotsContainer = document.querySelector('.slider-dots');
let currentIndex = 0;
// Create dots
items.forEach((_, index) => {
const dot = document.createElement('button');
dot.style.cssText = 'width: 10px; height: 10px; border-radius: 50%; border: none; cursor: pointer; background: #ccc;';
if (index === 0) dot.style.background = '#333';
dot.addEventListener('click', () => goToSlide(index));
dotsContainer.appendChild(dot);
});
function updateSlider() {
track.style.transform = `translateX(-${currentIndex * 100}%)`;
document.querySelectorAll('.slider-dots button').forEach((dot, index) => {
dot.style.background = index === currentIndex ? '#333' : '#ccc';
});
}
function goToSlide(index) {
currentIndex = index;
updateSlider();
}
prevBtn.addEventListener('click', () => {
currentIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
updateSlider();
});
nextBtn.addEventListener('click', () => {
currentIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
updateSlider();
});
// Touch support
let startX = 0;
track.addEventListener('touchstart', (e) => startX = e.touches[0].clientX);
track.addEventListener('touchend', (e) => {
const endX = e.changedTouches[0].clientX;
if (startX - endX > 50) nextBtn.click();
if (endX - startX > 50) prevBtn.click();
});
})();
</script>
{%- endif -%}

Here the code in the main-product.liquid file:

<!-- Code for Global Images -->
<div {% render 'section-properties', tight: true %}>
<product-rerender
id="product-info-{{ product.id }}-{{ section.id }}"
observe-form="{{ product_form_id }}"
allow-partial-rerender
>
<div class="product product--stacked-galleries">
<!-- LEFT COLUMN: Both galleries stacked vertically -->
<div class="product__media-stack">
{%- if product.media.size > 0 -%}
{%- render 'product-gallery', product: product, product_form_id: product_form_id -%}
{%- render 'product-gallery-global', product: product -%}
{%- endif -%}
</div>
<!-- RIGHT COLUMN: Product Info -->
<div class="product__info-stack">
{%- render 'product-info',
product: product,
update_url: true,
product_form_id: product_form_id,
context: 'main_product'
-%}
</div>
</div>
</product-rerender>
{%- render 'recommended-products' -%}
</div>
<style>
/* Create 2-column layout: galleries left, info right */
.product--stacked-galleries {
display: grid !important;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 4rem;
align-items: start; /* aligns product info to the top */
}
/* Stack the two galleries vertically */
.product__media-stack {
display: flex;
flex-direction: column;
gap: 2rem;
justify-content: flex-start;
}
.product__media-stack .product-gallery {
width: 100%;
display: block !important;
}
/* Make sure info column is top aligned */
.product__info-stack {
align-self: start;
}
/* Responsive layout for smaller screens */
@media (max-width: 999px) {
.product--stacked-galleries {
grid-template-columns: 1fr;
}
.product__media-stack {
flex-direction: column;
}
}
</style>
<!-- End Code for Global Images -->