Skip to main content

Ensure content parity between mobile and desktop

Mobile and desktop versions must contain the same core content for Google's mobile-first indexing. Missing or hidden content on mobile can result in lower search rankings, as Google primarily uses the mobile version for indexing and ranking.


Google switched to mobile-first indexing, so the mobile version of your site is the primary version used for ranking. If important content exists only on desktop, Google might not see it, which results in lower rankings for related queries.

Content parity issues create specific problems:

  • Ranking drops: missing mobile content leads to lower rankings for those topics.
  • Indexing gaps: content hidden on mobile might not be indexed at all.
  • User experience inconsistency: users expect the same information regardless of device.
  • Conversion impact: missing CTAs or product details on mobile reduce conversions.
  • SEO signal loss: structured data, links, and text missing on mobile aren't counted.

Common content parity mistakes:

  • Hiding content in collapsed accordions or tabs.
  • Removing sections entirely on mobile.
  • Using different URLs for mobile and desktop.
  • Lazy loading content that never renders.
  • Hiding text behind Read more links.
  • Removing product descriptions or specifications on mobile.

Google's John Mueller confirmed that content hidden in tabs or accordions on mobile is given less weight than visible content. Research shows that 30 to 40 percent of ecommerce sites have significant content differences between mobile and desktop.


Anchor to Audit content differencesAudit content differences

Identify content gaps:

  1. Compare mobile and desktop versions side by side.
  2. Check for missing sections on mobile.
  3. Verify that all text content is present.
  4. Confirm that structured data is identical.
  5. Check that internal links are present.
  6. Verify that images and media are included.

Use Google's tools: use Google Search Console URL Inspection to compare rendered HTML for both mobile and desktop versions.

Anchor to Make hidden content visibleMake hidden content visible

Anti-pattern: content hidden in a collapsed accordion:

<!-- Content hidden by default on mobile -->
<div class="product-description mobile-hidden">
<button onclick="toggleDescription()">Read More</button>
<div id="description" style="display: none;">
{{ product.description }}
</div>
</div>

Recommended: content visible, optionally collapsible:

<!-- Content visible by default, can be collapsed -->
<div class="product-description">
<div class="description-content">
{{ product.description }}
</div>
{% if product.description.size > 500 %}
<button class="description-toggle" onclick="toggleDescription()">
Show Less
</button>
{% endif %}
</div>

<style>
.description-content {
/* Content is visible and indexable */
max-height: none;
overflow: visible;
}

/* Optional: add "Show less" functionality */
.description-content.collapsed {
max-height: 300px;
overflow: hidden;
}
</style>

Anchor to Use proper accordion implementationUse proper accordion implementation

Accessible, indexable accordion:

{%- comment -%}
Content is in the DOM and indexable, but visually collapsed.
{%- endcomment -%}

<div class="accordion">
<h3>
<button class="accordion-header" aria-expanded="true" aria-controls="panel1">
Product details
<span class="accordion-icon">−</span>
</button>
</h3>
<div id="panel1" class="accordion-panel">
{{ product.description }}
</div>

<h3>
<button class="accordion-header" aria-expanded="false" aria-controls="panel2">
Shipping information
<span class="accordion-icon">+</span>
</button>
</h3>
<div id="panel2" class="accordion-panel" hidden>
{{ section.settings.shipping_info }}
</div>
</div>

<style>
.accordion-panel {
/* Content is in the DOM, just visually hidden when collapsed */
overflow: hidden;
transition: max-height 0.3s;
}

.accordion-panel[hidden] {
display: block; /* Keep in the DOM for indexing */
max-height: 0;
visibility: hidden;
}

.accordion-header[aria-expanded="true"] + .accordion-panel {
max-height: 2000px;
visibility: visible;
}
</style>

<script>
document.querySelectorAll('.accordion-header').forEach(button => {
button.addEventListener('click', function() {
const expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !expanded);

const panel = document.getElementById(this.getAttribute('aria-controls'));
panel.hidden = expanded;

this.querySelector('.accordion-icon').textContent = expanded ? '+' : '−';
});
});
</script>

Anchor to Ensure tab content is indexableEnsure tab content is indexable

Proper tab implementation:

{%- comment -%}
All tab content is in the DOM, only display changes.
{%- endcomment -%}

<div class="tabs">
<div class="tab-buttons" role="tablist">
<button class="tab-button active" role="tab" aria-selected="true" aria-controls="tab1">
Description
</button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="tab2">
Specifications
</button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="tab3">
Reviews
</button>
</div>

<div class="tab-panels">
<div id="tab1" class="tab-panel active" role="tabpanel">
{{ product.description }}
</div>

<div id="tab2" class="tab-panel" role="tabpanel">
{% if product.metafields.custom.specs %}
{{ product.metafields.custom.specs.value }}
{% endif %}
</div>

<div id="tab3" class="tab-panel" role="tabpanel">
{% if product.metafields.custom.reviews %}
{{ product.metafields.custom.reviews.value }}
{% endif %}
</div>
</div>
</div>

<style>
.tab-panel {
/* All panels are in the DOM; only visibility changes */
display: block;
opacity: 0;
height: 0;
overflow: hidden;
transition: opacity 0.3s;
}

.tab-panel.active {
opacity: 1;
height: auto;
overflow: visible;
}
</style>

Anchor to Avoid ,[object Object], for important contentAvoid display: none for important content

Anti-pattern: using display: none:

/* Content removed from rendering is not reliably indexed */
@media (max-width: 767px) {
.product-specs {
display: none; /* Google might not index this */
}
}

Recommended: use visibility or positioning:

/* Content is in the DOM, just not visible */
@media (max-width: 767px) {
.product-specs {
/* Option 1: collapse but keep in the DOM */
max-height: 0;
overflow: hidden;
opacity: 0;
}

}

Don't move content off-screen with position: absolute; left: -10000px to satisfy content parity. Content that no buyer can see is weighted less than visible content, so an off-screen copy doesn't restore parity. It only hides the gap. Render the content where buyers can reach it, such as in an expandable accordion that keeps the text in the DOM.

Anchor to Maintain the same structured dataMaintain the same structured data

Make sure schema markup is identical on mobile and desktop:

{%- comment -%}
Use the same structured data on mobile and desktop.
{%- endcomment -%}

<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"image": {{ product.featured_image | image_url: width: 1200 | json }},
"description": {{ product.description | strip_html | json }},
"sku": {{ product.selected_or_first_available_variant.sku | json }},
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"offers": {
"@type": "Offer",
"url": "{{ shop.url }}{{ product.url }}",
"priceCurrency": "{{ cart.currency.iso_code }}",
"price": "{{ product.selected_or_first_available_variant.price | divided_by: 100.0 }}",
"availability": "{% if product.available %}https://schema.org/InStock{% else %}https://schema.org/OutOfStock{% endif %}"
}
}
</script>

Same navigation and links on mobile:

{%- comment -%}
The mobile nav contains all desktop links.
{%- endcomment -%}

<nav class="site-nav">
<!-- Mobile menu -->
<div class="mobile-nav">
<button class="menu-toggle">Menu</button>
<ul class="mobile-menu">
{% for link in linklists.main-menu.links %}
<li>
<a href="{{ link.url }}">{{ link.title }}</a>
{% if link.links.size > 0 %}
<ul class="submenu">
{% for sublink in link.links %}
<li><a href="{{ sublink.url }}">{{ sublink.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</div>

<!-- Desktop nav, same links -->
<ul class="desktop-nav">
{% for link in linklists.main-menu.links %}
<li>
<a href="{{ link.url }}">{{ link.title }}</a>
{% if link.links.size > 0 %}
<ul class="dropdown">
{% for sublink in link.links %}
<li><a href="{{ sublink.url }}">{{ sublink.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>

Anchor to Responsive images with the same contentResponsive images with the same content

Serve appropriately sized images, but the same content:

{%- comment -%}
Serve appropriately sized images, but the same content.
{%- endcomment -%}

<img
src="{{ product.featured_image | image_url: width: 800 }}"
srcset="
{{ product.featured_image | image_url: width: 400 }} 400w,
{{ product.featured_image | image_url: width: 800 }} 800w,
{{ product.featured_image | image_url: width: 1200 }} 1200w,
{{ product.featured_image | image_url: width: 1600 }} 1600w
"
sizes="(max-width: 768px) 100vw, 50vw"
alt="{{ product.title | escape }}"
>

Anchor to Product description with content parityProduct description with content parity

<div class="product-info">
<h1>{{ product.title }}</h1>

<div class="product-price">
{{ product.price | money }}
</div>

<!-- Full description visible on both mobile and desktop -->
<div class="product-description">
<h2>Description</h2>
<div class="description-content">
{{ product.description }}
</div>
</div>

<!-- Specifications visible on both -->
{% if product.metafields.custom.specs %}
<div class="product-specs">
<h2>Specifications</h2>
<div class="specs-content">
{{ product.metafields.custom.specs.value }}
</div>
</div>
{% endif %}
</div>

<style>
/* Content visible on all screen sizes */
.product-description,
.product-specs {
margin: 32px 0;
}

/* Adjust layout, not content visibility */
@media (max-width: 767px) {
.product-info {
padding: 20px;
}
}

@media (min-width: 768px) {
.product-info {
max-width: 1200px;
margin: 0 auto;
padding: 40px;
}
}
</style>

Anchor to Collection page with the same productsCollection page with the same products

{%- comment -%}
Show the same products on mobile and desktop; adjust the layout only.
{%- endcomment -%}

<div class="collection-grid">
{% for product in collection.products %}
<div class="product-card">
<a href="{{ product.url }}">
<img
src="{{ product.featured_image | image_url: width: 600 }}"
srcset="
{{ product.featured_image | image_url: width: 300 }} 300w,
{{ product.featured_image | image_url: width: 600 }} 600w,
{{ product.featured_image | image_url: width: 900 }} 900w
"
sizes="(max-width: 767px) 100vw, (max-width: 1023px) 50vw, 33vw"
alt="{{ product.title | escape }}"
>
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</a>
</div>
{% endfor %}
</div>

<style>
.collection-grid {
display: grid;
gap: 20px;
padding: 20px;
}

/* Mobile: 1 column */
@media (max-width: 767px) {
.collection-grid {
grid-template-columns: 1fr;
}
}

/* Tablet: 2 columns */
@media (min-width: 768px) and (max-width: 1023px) {
.collection-grid {
grid-template-columns: repeat(2, 1fr);
}
}

/* Desktop: 3 columns */
@media (min-width: 1024px) {
.collection-grid {
grid-template-columns: repeat(3, 1fr);
}
}
</style>

  1. Visual comparison: view the site on mobile and desktop side by side. Check for missing sections, verify that all text is present, and confirm that images are included.

  2. Google Search Console: use the URL Inspection tool to compare mobile and desktop rendered HTML. Check for content differences and verify that structured data is identical.

  3. Device emulation: use Chrome DevTools device mode to render the page at a mobile viewport. Compare the DOM against the desktop render and check for content that's present in one and missing from the other.

  4. Crawl comparison: use Screaming Frog or a similar tool with mobile and desktop user agents. Compare word counts and check for missing links.

  5. Structured data testing: use the Rich Results Test for both mobile and desktop. Verify that schema markup is identical.

  6. Manual testing: test that accordions and tabs expand properly. Verify that all content is accessible and all internal links work.



Was this page helpful?