Metafield-Driven PDP Content System: Auto-Built Accordions, Need-to-Know Strip and Campaign Widgets
A layered content resolver on the Nudestix product page: legacy description HTML parsed into accordions at render time, three of them overridden per shade from metafields, with icon strips, badges and campaign widgets assembled from 29 metafield keys around them.
A catalog whose product copy lives inside one HTML description cannot give a shade its own ingredients list. We built a layered resolver that parses the existing description into accordions at render time, overrides three of them per variant from metafields, and adds icon strips, badges and collection-scoped campaign widgets around them — so the new PDP shipped with no content migration and the copy moves into structured fields one product at a time.
Fact Strip
- Client: Nudestix
- Surface: Product detail page
- Templates served: 3 — product, bundle and quick view
- Content sources: 29 metafield keys, four metaobject-backed records, and the legacy product description
- Complexity: High
- Attribution: Deploi-authored. The resolver, the parser and the content modules are ours; they run inside the storefront's product section on a Dawn 6.0.2 base, which predates Shopify's theme blocks.
- Status: Live, verified 2026-09-06
- Evidence: Strong
The Problem
A color cosmetics PDP carries a lot of copy, and much of it is not marketing: details, ingredients, how to use, shipping, a need-to-know icon strip, a key selling point banner, awareness and campaign tags, a numbered product highlight panel. Most of it differs by shade.
All of it was maintained in one place: the product description, written as HTML with <h5> subheadings. That single field is the whole content model, and if yours looks like this, you already know the two consequences. A shade cannot have its own ingredients list, because a description belongs to a product and not to a variant. And nothing can be reordered or retitled without editing raw HTML in a rich-text box — a developer's job wearing a merchandiser's hat.
The Constraint
The catalog could not be migrated first. Copy for every product already existed as those <h5>-structured descriptions, and a launch that waits for a full content rewrite is a launch that does not happen. So the new system had to read the old format on day one and keep reading it indefinitely for products nobody has revisited.
The theme base sets the second limit. Dawn 6.0.2 has no theme blocks, so every content module has to be either a section block with its own schema or a metafield read — there is no per-block authoring surface to inherit. And the two audiences want different tools: merchandisers change campaign pieces per campaign and want them in the theme editor, while product and shade copy changes per SKU and belongs in metafields where it can be exported, bulk-edited and eventually migrated.
What We Built
A layered resolver in the theme's product section, main-product.liquid.
Legacy copy is parsed at render time. The description is split on its <h5> headings; each fragment becomes a collapsible accordion whose summary is the heading text and whose body is the remainder. Each body is also emitted as a JSON script tag alongside the rendered HTML, so a client-side shade swap can put an untouched accordion back without asking the server again.
On top of that sit per-variant overrides. custom.variant_details supplies the Details body, custom.variant_ingredients supplies or creates the Ingredients accordion, custom.variant_how_to_use supplies How to Use. When neither a metafield nor an <h5> How to Use section exists, the section synthesises one: it re-scans the description for a heading containing "how to use" and, failing that, falls back to the whole description rather than showing nothing.
Around the accordions sit the metafield-rendered modules: a five-slot need-to-know icon strip driven by numbered image and title fields; product-highlight.liquid, a numbered two-column panel built from the metaobject-backed custom.product_highlight; an additional-description accordion from a title-and-content pair; a key selling point banner from custom.ksp_banner; an awareness tag from custom.awareness_tag; a limited-edition pill from custom.dynamic_tag or a product tag; award badges from custom.allure_badge and its sibling; and a per-variant sticky badge whose label and colors come from a metaobject record.
Campaign messaging is scoped one level up. collection.metafields.custom.guarantee_campaign_widget is read from the collection, not the product, and renders a guarantee widget above the quantity selector on every PDP reached through that collection — so a campaign is configured once for a merchandising group rather than product by product.
The merchandiser-authorable pieces stay as section blocks: a Shipping Information block whose settings are each overridden by a product metafield when one exists, collapsible tab blocks that can pull their body from a Shopify page, and a popup block.
Why This Way
Parsing the existing description meant the new PDP shipped without a content migration in front of it, and the metafield overrides gave the client a path to move copy out of HTML one product at a time, on their own schedule, with no flag day.
The fallback chain is the design: metafield first, then block setting, then parsed description, then product default. A half-migrated catalog renders correctly at every stage. That's the only property that makes a gradual migration survivable, and it's the one thing you can't add later. Emitting each accordion body as JSON next to its rendered HTML is what lets a shade change restore an untouched accordion without asking the server again.
You pay for that in permanence: the theme now understands two content models at once, and will for as long as any product still holds its copy in a description. Every module checks both, the parser is a permanent piece of the render path rather than a migration script that gets deleted, and the day the last product is migrated has to be somebody's decision.
And you pay for it in a convention nothing validates. The parser matches <h5> exactly, which makes a heading level part of the content model without saying so anywhere a merchandiser can read it. A heading typed as <h4>, or a line bolded to look like a heading, does not error and does not warn: it silently produces no accordion, and the only way anyone finds out is by opening the product page. If you take this pattern, that is the cost to weigh hardest — a parser keyed to one tag is a contract with everyone who will ever edit the copy.
Why Not an App
Three purchases could have covered this ground: a PDP tabs-and-accordion app, an icon-strip or trust-badge app, and a page-builder block for the highlight panel. All three install in a week.
None of them can read a product description it doesn't own. That's the whole answer. The copy for this catalog was already sitting in one HTML field, and every one of those apps would have wanted it moved into theirs first — so buying meant running a full content migration before launch, and the launch couldn't wait for it.
What we built instead renders server-side from Shopify metafields and metaobjects. Merchandisers edit it in the admin, it's in the initial HTML for crawlers and answer engines, and it adds no third-party script to a page already carrying reviews and video.
Implementation Notes
- The description-to-accordion parser splits on
<h5>, takes the heading text as the accordion summary and the remainder as the body, and emits each body a second time as a JSON script tag for the client-side swap to read. - Per-variant overrides cover Details, Ingredients and How to Use; on multi-variant products the parsed Ingredients accordion is suppressed so the variant-specific one is the only Ingredients section on the page.
- How to Use is synthesised when it is absent: the section re-splits the description, breaks at the first heading containing "how to use", and falls back to the entire description body.
- The need-to-know strip is a five-slot image-and-caption carousel with progressbar pagination and four to five slides in view across breakpoints, driven entirely by numbered metafields.
product-highlight.liquidrenders a metaobject-backed field as an image plus an auto-numbered list assembled from parallel text and subtext lists, with responsive image widths and a call to action.- Shipping Information is a single-instance section block whose title, description, link text and link URL are each overridden by a product metafield when the product carries one.
- The sticky badge is resolved per variant and also written into the hidden variant inputs, so the client-side swap has the next shade's badge and its colors without a fetch.
Edge Cases
- Every module is wrapped in a non-blank check on both the field and its resolved value, so a metaobject reference that points at nothing renders no empty container.
- The parser matches
<h5>exactly. A description with no<h5>headings renders no parsed accordions at all, and a heading typed as<h4>, or a bolded line standing in for a heading, silently produces no accordion — nothing errors, and nothing in the admin warns that the heading level is load-bearing. - The award badge image falls back to a transparent one-pixel data URI when the field is empty, so the badge row reserves its space and the layout does not shift.
- A cleanup pass on DOM ready hides accordions whose body came back empty after a shade change, catching the case where a variant override exists but resolves to nothing.
- The quick-view template renders the same content system in a modal, so a shopper who never reaches the full PDP still gets the shade-correct ingredients.
Platform Primitives Used
- Metafields — 29 keys carrying per-variant copy, badges, banners, shipping text and campaign configuration.
- Metaobjects — the structured records behind the highlight panel, the sticky badge and the campaign widget.
- Liquid section schema and settings — the section blocks that keep campaign and shipping copy in the theme editor.
- Theme app extensions — the app blocks this section shares the product page with.
Integrations in Play
- PowerReviews — the review platform whose widgets sit in the same product section, alongside the content modules.
- Tolstoy — the shoppable-video player mounted on this page; the theme's lazy-loading work treats it as a heavy element and defers it.
- Wishlist Hero — the wishlist control that coexists in the buy box.
Where It Runs
The product detail page, on three templates: the standard product page, the bundle template and the quick-view template. The same resolver serves all three, so the quick-view modal shows the same shade-specific ingredients as the full page.
What This Demonstrates
- Variant-aware content and media swapping — details, ingredients, how-to and social-proof badges all resolve per shade.
- Metafield-driven PDP content blocks — a full content model assembled from structured fields, with a parser bridging what came before.
How We Know
Three files read directly from the live theme — the product section, its bundle sibling and the highlight snippet — plus four client-side task-register records. Those records document the vendor components in this section, including a video-player fix and the lazy-loading work that followed. The content model itself, the parser and the per-variant override chain, is documented from the code: no client-side record of that decision survives. Metaobject definition handles are held back for the same reason — they are inferred from the access pattern rather than confirmed in the admin.
Related Builds
- Rebuilt 2026 PDP section with merchandiser-composable blocks — the same content problem on LUS Brands, where a newer theme base allowed a block-composed answer instead of a parser.
- Configurable PDP block system on the Palo Alto theme — the third storefront's version, extending a paid premium theme in place.
- Shade swatch variant picker — the control that triggers the shade change this content system responds to.
The Buy-vs-Build Question
The honest version of the buy case here is strong: three app categories, installed in a week, no parser to maintain. It fails on one fact — the copy already existed in a field no app can read, and migrating it first was not an option. Where product content should live, and what it costs to move it, is set out in the metafields and metaobjects architecture decision.
Ready to Get Your Product Copy Out of One HTML Field?
We build content models that read what you already have and let you migrate on your own schedule. Contact us today to talk through your catalog.