Noor bought her theme in 2021, and one bullet on its sales page did a lot of work: SEO optimized — schema included. It wasn't a lie. The theme shipped real JSON-LD, valid on the day it was written, and it has been emitting that same markup on every product page, every day, for five years.
Here's what that bullet never mentioned: structured data requirements are a moving target, and theme markup has a copyright date. Since Noor's theme shipped, Google split Product markup into two distinct experiences with different requirements, introduced variant-level markup that didn't exist when her theme was written, promoted shipping and returns data from afterthought to headline recommendation, and — as recently as last month — added new category and sale-duration properties to the merchant listing documentation. Her theme knows about none of it. It's not broken. It's vintage.
This file is the audit that closes the gap: what Google's Product documentation actually asks for today, the specific places 2021-era theme markup falls short, and the fifteen-minute check that dates your own. It's the most code-adjacent file in this series, and everything in it cites the current documentation — because in this subject, undated advice is the whole problem.
Two destinations, two requirement sets
The single biggest change most theme markup predates: Product structured data now feeds two different experiences, with different rules.
Product snippets — the classic rich result (stars, price, availability under a search listing) — for product pages generally, including editorial and review pages where the product isn't sold on-page.
Merchant listings — the shopping experiences for pages where a customer can actually buy: carousels, knowledge panels, the shopping surfaces that dominate commercial queries. This is the class that matters for a store, it has its own required-property set, and it offers far richer options — apparel sizing, shipping details, return policy information.
For a merchant listing, the required core is compact: name, image, and an offers block carrying price, priceCurrency, and availability. But required-only is the floor, not the strategy — the documented recommendation set is where eligibility for the richer experiences lives: sku, gtin, brand, description, and the two blocks almost no 2021 theme has ever heard of, shippingDetails and hasMerchantReturnPolicy. Every additional recommended property extends what your page is eligible for; that's Google's own framing, not enthusiasm.
Two details in the current docs deserve red ink. First: choose the class that matches the page — a purchase page carries merchant-listing markup, and shipping the wrong class (or a confused hybrid) is a policy problem, not a style choice. Second, and this one vindicates FILE 02 in Google's own words: the documentation explicitly recommends putting Product structured data in the initial HTML, warning that JavaScript-generated markup makes Shopping crawls less frequent and less reliable — a serious problem for exactly the fast-changing fields that matter most, availability and price. The delivered-layer standard isn't our editorial position anymore; it's Google's operational guidance.
FIG.02 — One vocabulary, two destinations. Ship the class that matches the page — and put the markup in the initial HTML, which is now Google's own guidance.
The variant revolution your theme predates
FILE 04 made the case that identity, availability, and price are variant-level facts, and that one Offer against thirty variants is thirty answers replaced by none. What a 2021 theme cannot know is that Google's documentation now provides the vocabulary to fix this properly: ProductGroup — a parent entity representing the product line, with individual Product variants underneath it, each carrying its own identifiers, its own offer, its own truth.
This is the single highest-value upgrade available to a varianted Shopify catalog, and it's structurally impossible for markup written before the vocabulary existed. The pattern: a ProductGroup holding the shared facts (brand, description, the varying dimensions like size and color declared as variesBy), and per-variant Product nodes with sku, gtin, size/color values, and a complete Offer each. Machines stop guessing which variant your one declared price belongs to, because the question stops existing.
If you take one implementation task from this entire file, it's this one.
The 2021-vs-now gap, itemized
Put a typical vintage theme's output next to the current documentation and the same gaps repeat:
Availability as a literal. The classic — "availability": "https://schema.org/InStock" hardcoded in the template. The current requirement is availability as a true value from the ItemAvailability enum, and FILE 04 covered why confidently-wrong is worse than absent. The fix in Liquid is binding to the variant's actual state, not editing a string.
Price without ceremony. Current docs are strict on format: price as a plain number string — "19.99", never "$19.99", never a thousands comma — with priceCurrency always present, and priceValidUntil documented for sale pricing (with a current-docs subtlety: it doesn't apply inside PriceSpecification). Vintage themes routinely emit symbol-formatted prices, skip currency, and know nothing of sale-duration markup — which was expanded last month.
One Offer, many variants. Covered above; the ProductGroup gap. Vintage markup declares the first variant and calls it the product.
No shipping, no returns. shippingDetails and hasMerchantReturnPolicy moved from obscurity to headline recommendations for merchant listings — they feed the comparison surfaces where buying decisions happen. A 2021 theme has empty air where this block belongs.
No category, ever. The newest example of drift: a category property letting merchants classify products directly in markup. Nobody's vintage theme has it; that's not a criticism, it's the thesis — the spec moved last month and your theme shipped five years ago.
The AggregateRating trap. The dishonorable mention. A generation of themes and apps shipped AggregateRating markup wired to nothing, or to review widgets that render client-side, or — worst — hardcoded to flattering numbers, because stars in search results convert. The standard is absolute: rating markup exists only where real reviews actually render on the page. Ratings without displayed reviews aren't an optimization; they're a fabricated fact in a published record, in the single most-policed field in the vocabulary. If your theme emits stars your page doesn't show, removing that markup is a fix.
FIG.03 — The 2021 theme against the 2026 spec. The same gaps turn up in every audit, and unearned stars sit in the most-policed field in the vocabulary.
The fifteen-minute theme-dating audit
Both layers, as always: the wire tells you what ships, Google's tools tell you what validates.
STEP 01 / PULL THE MARKUP AT THE WIRE. Your best seller, raw:
curl -s https://yourstore.com/products/your-best-seller \
| grep -o '<script type="application/ld+json">.*</script>' \
| head -c 3000
Zero blocks → FILE 02's gap class 1; stop here and fix delivery first.
STEP 02 / DATE IT. Read what came back against a checklist that doubles as a carbon-dating kit: itemprop= microdata attributes instead of (or tangled with) JSON-LD → pre-2020 DNA. One Offer on a multi-variant product → pre-ProductGroup. No shippingDetails / hasMerchantReturnPolicy → pre-2023 thinking. Symbol-formatted price or missing priceCurrency → never met the format rules. AggregateRating present → scroll the rendered page and count the actual reviews; if the answer is zero, you've found the trap.
STEP 03 / VALIDATE BOTH LAYERS. Run the URL through Google's Rich Results Test for the rendered verdict and required-property errors — remembering the caveat from FILE 02: the test renders JavaScript, so a pass there plus an empty curl result means Google-only markup that every non-rendering machine misses. Passing both layers is the bar.
STEP 04 / FIX IN LEVERAGE ORDER. Move markup server-side into Liquid (Google's own initial-HTML guidance now demands it). Bind availability and price to real objects, format-clean, currency-present. Implement ProductGroup on varianted products. Add shipping and returns blocks from your actual policies. Strip unearned ratings. Then — and only then — decorate with the newer recommendations like category.
FIG.04 — Carbon-date your theme's markup. Fifteen minutes, both layers — the wire and the validator — and passing both is the bar.
The uncomfortable close, and the reason this file exists: none of this stays fixed. The documentation moved last month; it will move again, and a theme — any theme, however good — is a snapshot of the spec on its ship date. The durable answer isn't a better snapshot, it's the discipline this series keeps landing on: verify continuously against the current documentation, at the wire, because "schema included" without a date attached is a promise about the past.
One more piece of the vintage problem remains, and it's darker than missing the new: a lot of live markup targets features that no longer exist at all — rich results Google has retired, types that died with announcements nobody's theme read. The graveyard tour is next.
Sources & further reading
- Google — Intro to Product structured data (the two-class split): https://developers.google.com/search/docs/appearance/structured-data/product
- Google — Merchant listing structured data (required & recommended properties, initial-HTML guidance): https://developers.google.com/search/docs/appearance/structured-data/merchant-listing
- Google — Product variants with ProductGroup: https://developers.google.com/search/docs/appearance/structured-data/product-variants
- Google Merchant Center — Supported structured data attributes: https://support.google.com/merchants/answer/6386198
- Google — Rich Results Test: https://search.google.com/test/rich-results
- Rank Sniper Field Notes — FILE 02: /blog/ai-agent-declared-vs-delivered-product-page · FILE 04: /blog/product-attributes-ai-systems-refuse-to-guess · FILE 06: /blog/ai-visibility-score-eligible-implemented-weighted · FILE 10: /blog/verify-dont-generate-ai-product-content
Rank Sniper — Field Notes. Catalog legibility and AI-visibility verification for Shopify. We verify what AI agents actually receive from your store; we don't generate content and hope. Verifier, not generator.
