Rebuilt 2026 PDP Section With Merchandiser-Composable Blocks and Per-Product ATC Color
A custom product section for LUS Brands that declares 26 block types beyond Dawn's set, resolves title and subtitle copy per variant, and paints the add-to-cart button from a product metafield.
Dawn's stock product section declares no blocks for ingredient rails, UGC, bundles or shoppable video, and no way to give one product its own button color. We built a new product section that declares 26 block types beyond Dawn's set, plus a per-product brand color read from custom.atc_background into a --atc-button-color CSS custom property. Four product templates mount it, and a product launch became a merchandising task.
Fact Strip
- Client: LUS Brands
- Surface: Product detail page
- Templates served: 4
- Block types declared: 26 beyond Dawn's set
- Complexity: High
- Attribution: Deploi-authored. The section is ours. It runs inside Dawn 15.4.1's
<product-info>custom element, and Dawn's volume pricing, quantity rules, media modal and model viewer are carried over unchanged. - Status: Live, verified 2026-09-06
- Evidence: Strong — code plus client working-session records
The Problem
Shopify's theme editor shows a merchandiser exactly what a section's schema declares and nothing else. Dawn's stock product section declares a short list: title, price, variant picker, quantity, buy buttons, description, share. There is no block for an ingredient rail, a real-results rail, a bundle rail, a complementary-product rail, shoppable video or an icon grid — and none for the eyebrow label a rebranded catalog wanted above every product name.
Per-product color is a second, separate gap. Button color lives in the theme stylesheet, so a product that wants its own needs a developer, a deploy and a new CSS rule — three things your launch calendar doesn't have room for. The rebrand needed a buy box merchandisers could compose themselves and a page that could name and color itself per product, with no Liquid edit per launch.
The Constraint
The theme editor's block model sets the shape of any answer. Every merchandiser-controllable element has to be a schema block with its own settings, and blocks render in one flat loop dispatched by type — no nesting, no conditional registration, no way to say "offer this block only on kits."
Three separate purchase-option apps can inject widgets into the same buy box, so the section cannot assume it owns that DOM; it has to decide what is visible. Client working sessions also fixed a position, not just a feature: the subscription option had to sit between the size and formulation selectors — a slot an app block appended to the end of a section structurally cannot reach.
All copy variation had to come from metafields, because one section serves four product templates: the standard product page, the new-design page, the kids page and the waiting-list page. Forking the section per template would have made every future change a four-file edit.
What We Built
de_main_product.liquid — roughly 3,400 lines — renders inside Dawn's <product-info> element and keeps Dawn's element ids, so Dawn's own JavaScript keeps working. It declares 26 block types beyond Dawn's set, among them product_type, highlighted_text, short_description, key_ingredients, real_results_images, bundle_and_save, pairs_well_with, see_it_in_action and simple_icon_grid, all dispatched from one {% case block.type %}.
Per-product theming is the piece the client asked for by name. A working session settled that the add-to-cart color should be programmable at the product level from inside the theme editor. That decision is now product.metafields.custom.atc_background, read at render time and emitted as --atc-button-color in a section-scoped {% style %} block with a brand default. The button rule in section-main-product.css consumes the token, and nothing is inlined on the button element, so the sticky mobile bar and the kids-page override reuse the same value without a second rule.
Product identity resolves per variant. The title block prefers variant.metafields.custom.variant_title over product.title; the subtitle block prefers variant.metafields.custom.highlighted_text and falls back to the product-level value; the eyebrow comes from custom.product_type_label through product-type.liquid. That eyebrow got its own metafield rather than reusing the product-type field, again out of a client working session — the merchandising label and the catalog taxonomy were never the same idea.
The short-description block takes its metafield namespace and two keys as theme-editor text settings, so a merchandiser can repoint it without a developer, and renders preview plus expanded copy behind a Learn more toggle from product-short-description.liquid.
Scripts load conditionally, because this section is on every product page. The shoppable-video script loads only when a see_it_in_action block carries a carousel id; volume-pricing scripts only when quantity price breaks are configured; magnify only on hover zoom; the model viewer only when a 3D model exists.
Why This Way
Everything that varies is either a block setting or a metafield. That's the whole argument: a launch becomes a merchandising task instead of a deploy, and the four templates diverge in settings rather than in code.
Reading the color into a CSS custom property rather than inlining it keeps one styling rule in the stylesheet and gives the sticky bar and the kids override a token to reuse. Conditional script tags exist because a payload on a product that uses none of those features is still a payload.
The cost is concentration. This is one section of roughly 3,400 lines that four product templates depend on, and the flat block loop grows every time a module is added. A mistake in that loop is a mistake on every product page at once. The settings surface is also large enough to need its own internal documentation — composability bought with configuration is still configuration someone has to learn.
The sharper cost is the widget suppression. It is a stylesheet rule, not a change at the app end, so if anyone re-enables one of those apps the theme goes on hiding it and nothing says so — a purchase option missing from the buy box, with no error to explain it. We would rather name that than call the rule careful scoping.
Why Not an App
The block set covers ground that is normally bought: ingredient and benefit widgets, UGC galleries, bundle rails, complementary-product rails, per-product theming. Built as native theme blocks, they stay inside Dawn's section-rendering lifecycle, so they re-render on variant change with everything else instead of each widget re-syncing itself after paint. Each bought widget would also arrive with its own script tag on a page that already loads a subscription picker and a video carousel. The composition problem would remain too: an app block can be added to a section, but it cannot be placed between the variant picker and the add-to-cart button — exactly where the client needed one of these modules.
Implementation Notes
- The section renders inside Dawn's
<product-info>element and preserves Dawn's ids —ProductInfo-,Quantity-,Price-Per-Item-— so Dawn's stock JavaScript continues to bind without patching. --atc-button-coloris declared on the section wrapper element, and the submit-button rule insection-main-product.cssreads it with the brand default supplied inline as the fallback value.- The
buy_buttonsblock carries a toggle that mounts the Skio plan picker with the product and the selected variant serialized as escaped JSON attributes, plus a selector that wires the app's price into the theme's own price node. - Competing purchase-option widget containers are hidden inside this section with a
display: none !importantrule, rather than being scoped or uninstalled at the app end, so the buy box shows one plan picker instead of three stacked ones. - Volume pricing, quantity rules and per-item price are Dawn 15.4.1 code carried over unchanged, and Dawn's media modal, product modal and model viewer are reused as they ship.
- All four product templates mount the section with the same 16 blocks and differ only in a handful of settings: the kids template drops the quiz FAQ banner, and two templates drop the custom save badge.
- Related-products and multicolumn blocks are present but disabled in all four templates, kept in place so a merchandiser can switch them on without a deploy.
Edge Cases
- The button color falls back to the brand default when the metafield is absent or empty — a two-step check, because an empty string is not a missing value.
- Title and subtitle fall through variant, then product, then
product.title, so a half-populated catalog still renders a complete page. - The inventory block handles three separate states: inventory not tracked by Shopify, a continue-selling policy, and genuine zero stock.
- Products with only a default variant skip loading the variant-picker markup and the swatch CSS entirely, so a single-SKU page does not pay for option UI it will never show.
- A dedicated layout branch handles products with zero media, widening the buy box rather than rendering an empty gallery frame beside it.
- Theme-editor mode loads the editor script so blocks re-initialize after the editor re-renders a section. Without it, carousels and toggles die the moment a merchandiser starts composing.
Platform Primitives Used
- Metafields — carry the per-product color, the variant title and subtitle, and the eyebrow label.
- Liquid section schema and settings — the 26 block types and their settings are the entire merchandiser interface.
- Section Rendering API — Dawn's variant-change lifecycle re-renders the section. Native blocks come back updated; bolted-on widgets have to re-sync themselves.
- Theme app extensions — the app blocks the buy box has to accommodate.
- Custom elements — the section lives inside Dawn's
<product-info>element and mounts the plan picker as an element of its own.
Integrations in Play
- Skio — the subscription plan picker the buy box mounts; the section wraps it and wires its price into the theme's price node.
- LyveCom — supplies the shoppable-video carousel the
see_it_in_actionblock loads, only when a block carries a carousel id. - Yotpo — the source of the rating and rating-count values the section reads from product metafields.
Where It Runs
The product detail page, on four product templates: the standard product page, the new-design page, the kids page and the waiting-list page. All four mount this one section, which is the point — the difference between a kids product page and an adult one is a set of block settings, not a second copy of the code.
What This Demonstrates
- Variant-aware content and media swapping — title, subtitle and eyebrow resolve per variant inside Dawn's re-render lifecycle.
- Metafield-driven PDP content blocks — the color, the copy and the module content all come from structured fields in the Shopify admin.
How We Know
Eight files read directly from the live theme — the section, two snippets it renders, and the Dawn modal and model-viewer assets it reuses — plus fifteen client-side records: working-session transcripts, product metafield exports and one meeting record. The exports are what make this one strong rather than moderate: custom.atc_background and custom.product_type_label appear in the client's own exported field list and in the section's Liquid, so the decision and the implementation match rather than being inferred from code alone.
Related Builds
- Metafield-driven product information tabs — the content system directly below this buy box, on the same four templates.
- Metafield-driven PDP content system — the same problem on Nudestix, solved against a Dawn 6.0.2 base with no theme blocks available.
- Configurable PDP block system on the Palo Alto theme — the same problem on a paid premium theme, where the answer was extension, not a new section.
The Buy-vs-Build Question
Per-product theming and buy-box modules are a well-populated app category, and buying them is a legitimate answer for a storefront that will not touch its PDP again for two years. This one was mid-rebrand with four templates in flight, so the composition and the ordering mattered more than the install time. Our reasoning across all three storefronts is set out in the metafields and metaobjects architecture decision.
Ready to Rebuild Your Product Page?
We build product pages your merchandisers can run without us. Contact us today to talk about what your PDP should let your team change on its own.