How can I make my custom 3D viewer appear first on my product page on thinner screens?

Solved

How can I make my custom 3D viewer appear first on my product page on thinner screens?

nhdemas
Tourist
9 1 3

Hi, I currently have a custom 3D viewer that works with variant switching keeping the 3D viewer at the top of the screen on desktop however, on mobile or thinner screens the image appears first. I have linked my code from product-media-gallery.liquid below and a screenshot.

Screenshot 2025-04-24 191700.pngScreenshot 2025-04-24 191707.png

<media-gallery
id="MediaGallery-{{ section.id }}"
role="region"
{% if section.settings.enable_sticky_info %}
class="product__column-sticky"
{% endif %}
aria-label="{{ 'products.product.media.gallery_viewer' | t }}"
data-desktop-layout="{{ section.settings.gallery_layout }}"
>
<div id="GalleryStatus-{{ section.id }}" class="visually-hidden" role="status"></div>
<slider-component id="GalleryViewer-{{ section.id }}" class="slider-mobile-gutter">
<a class="skip-to-content-link button visually-hidden quick-add-hidden" href="#ProductInfo-{{ section.id }}">
{{ 'accessibility.skip_to_product_info' | t }}
</a>
<ul
id="Slider-Gallery-{{ section.id }}"
class="product__media-list contains-media grid grid--peek list-unstyled slider slider--mobile"
role="list"
>

<!-- 3D Viewer integration start -->
{% assign first_variant = product.variants.first %}
{% if first_variant.metafields.custom.viewer %}
<li class="product__media-item grid__item slider__slide" data-media-id="{{ section.id }}-3d-viewer" style="order: -10;">
<div class="product-3d-viewer-container">
<iframe
id="product-viewer-iframe-{{ section.id }}"
src="{{ first_variant.metafields.custom.viewer }}"
class="product-3d-viewer-iframe"
allowfullscreen
loading="lazy"
></iframe>
</div>
</li>
{% endif %}

<script>
document.addEventListener('DOMContentLoaded', function() {
const sectionId = '{{ section.id }}';
const iframe = document.querySelector(`#product-viewer-iframe-${sectionId}`);
const variantInput = document.querySelector(`#product-form-${sectionId} [name="id"]`);

if (!iframe || !variantInput) return;

const variantViewers = {};
{% for variant in product.variants %}
{% if variant.metafields.custom.viewer %}
variantViewers['{{ variant.id }}'] = '{{ variant.metafields.custom.viewer }}';
{% endif %}
{% endfor %}
function updateViewer(variantId) {
const viewerUrl = variantViewers[variantId];
if (viewerUrl) {
iframe.src=viewerUrl;
} else {
console.warn('No viewer URL for this variant');
}
}

updateViewer(variantInput.value);

variantInput.addEventListener('change', function() {
updateViewer(this.value);
});

const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'value') {
updateViewer(variantInput.value);
}
});
});

observer.observe(variantInput, {
attributes: true,
attributeFilter: ['value']
});
});
</script>

<style>
.product-3d-viewer-container {
position: relative;
width: 100%;
max-width: 100%;
aspect-ratio: 1 / 1;
background: transparent;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}

.product-3d-viewer-iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
background: transparent;
}
</style>
<!-- 3D Viewer integration end -->



{%- if product.selected_or_first_available_variant.featured_media != null -%}
{%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}

Accepted Solution (1)
nhdemas
Tourist
9 1 3

This is an accepted solution.

Nevermind, I fixed it!
I replaced:

      {%- if product.selected_or_first_available_variant.featured_media != null -%}

        {%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}

With:
{%- if product.selected_or_first_available_variant.featured_media != null and product.selected_or_first_available_variant.metafields.custom.viewer == blank -%}
{%- assign featured_media = product.selected_or_first_available_variant.metafields.custom.viewer -%}

View solution in original post

Replies 12 (12)

BiDeal-Discount
Shopify Partner
468 54 110

Hi,

 

To make your custom 3D viewer appear first on your product page on thinner/mobile screens while keeping the current desktop order, you need to adjust the rendering order or use CSS to reorder the elements responsively.

Key Approaches

1. Adjust Liquid Rendering Order Conditionally by Screen Size (Less Common)

Liquid templates are rendered server-side and cannot detect screen size, so you cannot directly reorder elements in Liquid based on device width. However, you can:
  • Render the 3D viewer markup before other media in the product-media-gallery.liquid file so it appears first in the DOM.
  • Use CSS or JavaScript to reorder or hide/show elements differently on desktop vs mobile.

2. Use CSS Flexbox or Grid with order Property to Reorder on Mobile

This is the most common and effective approach. You keep the HTML order as is (3D viewer below images on desktop), but use CSS to reorder on smaller screens.

Example CSS snippet to add to your theme’s stylesheet or custom CSS section:
@media only screen and (max-width: 767px) {
/* Container that holds product media items */
 .product__media-list {
   display: flex;
   flex-direction: column;
 }

/* Target the 3D viewer item and move it to the top */
 .product__media-item--3dviewer {
   order: -1; /* Moves this item to the front */
 }
}​
  • You need to add a unique class (e.g., .product__media-item--3dviewer) to the 3D viewer container in your Liquid code so CSS can target it specifically.
  • This CSS moves the 3D viewer to appear first on screens narrower than 768px (typical mobile breakpoint).

3. JavaScript DOM Manipulation (If CSS Not Enough)

If your theme’s structure or slider library complicates CSS reordering, you can use JavaScript to move the 3D viewer element to the top on page load for mobile devices:
document.addEventListener('DOMContentLoaded', function () {
  if (window.innerWidth <= 767) {
    const mediaList = document.querySelector('.product__media-list');
    const viewer = document.querySelector('.product__media-item--3dviewer');
    if (mediaList && viewer) {
      mediaList.prepend(viewer);
    }
  }
});
 

Practical Steps for Your Case

1. Add a unique class to your 3D viewer container in product-media-gallery.liquid:

<li class="product__media-item product__media-item--3dviewer grid__item slider__slide" data-media-id="{{ section.id }}-3dviewer">
  <!-- Your 3D viewer embed code here -->
</li>

2. Add the CSS above to your theme’s CSS file or custom CSS section to reorder on mobile.

3. Test on desktop and mobile to ensure the 3D viewer stays first on mobile but remains in the original position on desktop.

 

This approach will make your custom 3D viewer appear first on thinner/mobile screens while preserving the desktop order and variant switching functionality.
- Helpful? Like & Accept solution!
- BiDeal Bundle Volume Discounts: Upsell with bundles, quantity breaks, volume discounts. AOV+ with gifts, shipping & progressive cart
- Bify app: Shopify automatic discount solutions
- Contact me? support@bify.app or WhatsApp: +84974709330
nhdemas
Tourist
9 1 3

Hi, I followed your steps but it still doesn't work.😥 Ive attached my code below which is updated if you could take a look.

<!-- 3D Viewer integration start -->
{% assign first_variant = product.variants.first %}
{% if first_variant.metafields.custom.viewer %}
<li class="product__media-item product__media-item--3dviewer grid__item slider__slide is-active" data-media-id="{{ section.id }}-3d-viewer" style="order: -10;">
<div class="product-3d-viewer-container">
<iframe
id="product-viewer-iframe-{{ section.id }}"
src="{{ first_variant.metafields.custom.viewer }}"
class="product-3d-viewer-iframe"
allowfullscreen
loading="lazy"
></iframe>
</div>
</li>
{% endif %}

<script>
document.addEventListener('DOMContentLoaded', function() {
const sectionId = '{{ section.id }}';
const iframe = document.querySelector(`#product-viewer-iframe-${sectionId}`);
const variantInput = document.querySelector(`#product-form-${sectionId} [name="id"]`);

if (window.innerWidth <= 767) {
const mediaList = document.querySelector('.product__media-list');
const viewer = document.querySelector('.product__media-item--3dviewer');
if (mediaList && viewer) {
mediaList.prepend(viewer);
}
}

if (!iframe || !variantInput) return;

const variantViewers = {};
{% for variant in product.variants %}
{% if variant.metafields.custom.viewer %}
variantViewers['{{ variant.id }}'] = '{{ variant.metafields.custom.viewer }}';
{% endif %}
{% endfor %}
function updateViewer(variantId) {
const viewerUrl = variantViewers[variantId];
if (viewerUrl) {
iframe.src=viewerUrl;
} else {
console.warn('No viewer URL for this variant');
}
}

updateViewer(variantInput.value);

variantInput.addEventListener('change', function() {
updateViewer(this.value);
});

const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'value') {
updateViewer(variantInput.value);
}
});
});

observer.observe(variantInput, {
attributes: true,
attributeFilter: ['value']
});
});
</script>

<style>
.product-3d-viewer-container {
position: relative;
width: 100%;
max-width: 100%;
aspect-ratio: 1 / 1;
background: transparent;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}

.product-3d-viewer-iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
background: transparent;
}
</style>
<!-- 3D Viewer integration end -->

BiDeal-Discount
Shopify Partner
468 54 110

what did rendering on storefront? you can try to add log to document event then check whether is it running or not?

- Helpful? Like & Accept solution!
- BiDeal Bundle Volume Discounts: Upsell with bundles, quantity breaks, volume discounts. AOV+ with gifts, shipping & progressive cart
- Bify app: Shopify automatic discount solutions
- Contact me? support@bify.app or WhatsApp: +84974709330
nhdemas
Tourist
9 1 3

Yes my 3D viewer is rendering on my store front, it is also the first media on the product page because I gave it a very low sort order. For some reason though on thin screens the image pops up first and I have to scroll back to see the 3D viewer.

BiDeal-Discount
Shopify Partner
468 54 110

Did you resize screen and reload page?

 

If yes, the code seem not working

if (window.innerWidth <= 767) {
  const mediaList = document.querySelector('.product__media-list');
  const viewer = document.querySelector('.product__media-item--3dviewer');
  if (mediaList && viewer) {
    mediaList.prepend(viewer);
  }
}
- Helpful? Like & Accept solution!
- BiDeal Bundle Volume Discounts: Upsell with bundles, quantity breaks, volume discounts. AOV+ with gifts, shipping & progressive cart
- Bify app: Shopify automatic discount solutions
- Contact me? support@bify.app or WhatsApp: +84974709330
nhdemas
Tourist
9 1 3

Yea, I did.

BiDeal-Discount
Shopify Partner
468 54 110

can you inspect browser to see whether product__media-item--3dviewer selector above product__media-list selector on mobile?

- Helpful? Like & Accept solution!
- BiDeal Bundle Volume Discounts: Upsell with bundles, quantity breaks, volume discounts. AOV+ with gifts, shipping & progressive cart
- Bify app: Shopify automatic discount solutions
- Contact me? support@bify.app or WhatsApp: +84974709330
nhdemas
Tourist
9 1 3

product__media-item--3dviewer is below roduct__media-list. Could you also inspect my website? https://wznhfa-vh.myshopify.com/

BiDeal-Discount
Shopify Partner
468 54 110

Hi ,

On my phone, I see the 3d viewer display only

 

holoform3d – My Store.jpeg

- Helpful? Like & Accept solution!
- BiDeal Bundle Volume Discounts: Upsell with bundles, quantity breaks, volume discounts. AOV+ with gifts, shipping & progressive cart
- Bify app: Shopify automatic discount solutions
- Contact me? support@bify.app or WhatsApp: +84974709330
nhdemas
Tourist
9 1 3

Hmm have you tried changing variants? Also, I had another thought. Is it possible to disable scroll to featured image when changing variants in Shopify's script? I think that might fix my problem.

nhdemas
Tourist
9 1 3

This is an accepted solution.

Nevermind, I fixed it!
I replaced:

      {%- if product.selected_or_first_available_variant.featured_media != null -%}

        {%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}

With:
{%- if product.selected_or_first_available_variant.featured_media != null and product.selected_or_first_available_variant.metafields.custom.viewer == blank -%}
{%- assign featured_media = product.selected_or_first_available_variant.metafields.custom.viewer -%}

PaulNewton
Shopify Partner
7746 679 1610

@nhdemas be aware images loading/appearing first in place of 3D models or videos is done on purpose  to avoid bogging down mobile devices and thus decreasing conversion rates; READ: you loose money with bad load performance.

Test the performance of such changes excruciatingly carefully .

Contact paull.newton+shopifyforum@gmail.com for the solutions you need


Save time & money ,Ask Questions The Smart Way


Problem Solved? ✔Accept and Like solutions to help future merchants

Answers powered by coffee Thank Paul with a Coffee for more answers or donate to eff.org