PDP Template Build Brief — V3.0
Contents
- Objective
- Reference file
- Architecture decision
- Page layout & structure
- Mobile above-fold budget
- Section inventory
- The repeating purchase block
- Metaobject definitions
- Product metafield definitions
- Product-specific decisions
- Technical requirements
- CSS strategy
- Schema requirements
- Build order
- Pre-build checks
- Do not refactor
- Deliverables
- Out of scope
1. Objective
Convert the approved PDP mockup (V3.0) into a reusable, admin-editable Shopify PDP template built from Liquid sections and metaobjects. Apply the resulting template to the top 20–30 PDPs by revenue/sessions. Long-tail SKUs remain on the current default PDP template.
The template presents as a traditional Shopify PDP optimized for mobile-first buying: decision-critical elements (image, title, rating, price, pack selector, Add to Cart) visible in the first viewport on a 375×812 screen, with narrative content below the fold and skippable via scanner chips.
Explicitly out of scope
- Page builders (PageFly, Shogun, GemPages)
- AI-generated content for metafields/metaobjects
- Rebuilding all 200+ SKUs
- A new page-builder tool
Success criteria
- On mobile (375×812), the Add to Cart button visible without scrolling
- Content for the top 20–30 PDPs editable in Shopify admin with no developer involvement per update
- Shared content (pain cards, steps, FAQ, trust badges) stored once and referenced by many products
- Theme editor can rearrange/disable sections per product without code changes
- V3 conversion measured against current PDP baseline (Stain Remover PDP: 6.76% AU traffic; Black Spot PDP: 7.35%)
2. Reference file
The approved mockup is pdp-v2-shopify.html (V3.0). It is an annotated, self-contained HTML file scoped under .pdp-v2. Every editable element carries a colored annotation tag showing exactly which field feeds it:
| Tag color | Meaning | Content source |
|---|---|---|
| Blue | Section setting | section.settings.* |
| Green | Metafield (per-product) |
custom.pdp_* or product object metafields |
| Purple | Metaobject reference | References to shared metaobject entries |
| Orange | Product object | Native Shopify product data (title, price, images) |
| Red | Block (repeatable) | Repeatable blocks in the section schema |
The mockup has two toggle buttons in its top bar. "Toggle labels" hides/shows the annotations. "Variant: Without description" switches between the two layout variants Nat reviewed. Neither toggle goes into production — they are review aids only.
3. Architecture decision
Option 1 (Sections + Metaobjects) is confirmed. Do not use Option 2 (product metafields only).
Rationale: several modules are shared across products (how-to steps, FAQ, pain cards, trust badges). Metafields would require duplicating this content on every product record. Metaobjects store it once and let products reference it.
Pattern
- Shared content → metaobjects, referenced via product metafields
- Product-specific content → product metafields directly
- Global/theme-level content → section settings
4. Page layout & structure
The template is a mobile-first hybrid: decision-critical content above the fold, narrative content below with scanner chips to make it skippable, and the purchase block repeated at natural decision points.
Above the fold (mobile 375×812)
- Header (theme-level — search collapsed to icon on PDP only, see section 11)
- Campaign banner (one line, gold bottom border)
- Hero image (270px cap on mobile)
- Thumbnail strip (no tags)
- Hero tags (Australian Made pill)
- Product title (H1)
- Star rating + review count
- Price (now / was / save)
- Pack selector (40/60 grid, bundle pre-selected)
- Add to Cart button
- Free-ship callout
Below the fold
- Trust block (benefit bullets + 4 trust badges)
- Scanner chips (navy bar, 4 anchor chips)
- How it works (4 step cards)
- Before / after (6-image grid)
- Purchase block repeat #1 (after proof)
- Stain Nightmare (4 pain cards)
- Warning callout (Swim Season Warning)
- Instructions (application guide)
- Comparison table
- Cross-sell (Oxi Shock follow-up)
- Reviews (3 manual cards + Okendo carousel placeholder)
- Purchase block repeat #2 (after reviews)
- FAQ (4-item accordion)
- Final CTA band (singular 3+1 offer, no pack selector)
5. Mobile above-fold budget
Target: Add to Cart fully visible at 375×812. Verified positions:
| Product | Add to Cart position | Status |
|---|---|---|
| Stain Remover 1kg | ~769px | ✓ Clear of fold |
| Black Spot | ~792px | ✓ Clear of fold |
These were measured with the mockup header rendered. Nim must re-verify against the live theme header height at 375px. If the actual header is taller, the fold targets may not hold.
What made the target work
- Hero image capped at 270px on mobile (was 300px)
- Campaign banner is one line and sits above the hero
- No sticky ATC bar consuming viewport height
- Price → pack → ATC merged into one continuous block with no section boundary
- Short description hidden by default (base variant)
6. Section inventory
Build these as individual .liquid files under /sections/. Each must have its own {% schema %} and appear in the theme editor.
| # | Section file | Purpose | Content source |
|---|---|---|---|
| 1 | pdp-campaign-banner.liquid |
One-line seasonal hook | Section settings |
| 2 | pdp-hero.liquid |
Gallery, title, rating, purchase block | Product object + metafields + blocks |
| 3 | pdp-scanner-chips.liquid |
Anchor chip row | Section blocks (repeatable) |
| 4 | pdp-how-it-works.liquid |
Science / mechanism steps | Metaobject reference list |
| 5 | pdp-before-after.liquid |
6-image results grid | Metaobject reference list |
| 6 | pdp-buy-box.liquid |
Standalone purchase block (used for repeats) | Blocks + section settings |
| 7 | pdp-pain-cards.liquid |
Stain Nightmare 4-card grid | Metaobject reference list |
| 8 | pdp-warning-callout.liquid |
Swim Season Warning | Section settings |
| 9 | pdp-instructions.liquid |
Application guide | Product metafield (rich text) |
| 10 | pdp-comparison.liquid |
Comparison table | Section blocks (repeatable rows) |
| 11 | pdp-cross-sell.liquid |
Oxi Shock follow-up | Product reference metafield |
| 12 | pdp-reviews.liquid |
3 manual cards + Okendo carousel | Blocks + app block embed |
| 13 | pdp-faq.liquid |
Accordion FAQ | Metaobject reference list |
| 14 | pdp-final-cta.liquid |
Stock Up and Save band, singular 3+1 offer | Section settings + fixed bundle |
7. The repeating purchase block
The purchase block appears four times in the template JSON. This is deliberate — it replaces the sticky ATC bar's role of keeping buy one tap away on a long page.
Four instances
- Primary — inside the hero, above the fold
- Repeat #1 — after the Before / After section (after proof)
- Repeat #2 — after the Reviews section
-
Final — inside the
pdp-final-ctaband, singular 3+1 offer
Template JSON structure
{
"sections": {
"campaign": { "type": "pdp-campaign-banner" },
"hero": { "type": "pdp-hero" },
"scanner": { "type": "pdp-scanner-chips" },
"how_it_works": { "type": "pdp-how-it-works" },
"before_after": { "type": "pdp-before-after" },
"buy_box_1": { "type": "pdp-buy-box" },
"pain_cards": { "type": "pdp-pain-cards" },
"warning": { "type": "pdp-warning-callout" },
"instructions": { "type": "pdp-instructions" },
"comparison": { "type": "pdp-comparison" },
"cross_sell": { "type": "pdp-cross-sell" },
"reviews": { "type": "pdp-reviews" },
"buy_box_2": { "type": "pdp-buy-box" },
"faq": { "type": "pdp-faq" },
"final_cta": { "type": "pdp-final-cta" }
},
"order": [
"campaign", "hero", "scanner", "how_it_works", "before_after",
"buy_box_1", "pain_cards", "warning", "instructions", "comparison",
"cross_sell", "reviews", "buy_box_2", "faq", "final_cta"
]
}
Critical for Nim: the pdp-buy-box section must be fully idempotent. Every DOM ID inside must be instance-scoped (use {{ section.id }} as a prefix). Every JS handler must bind to the section's own container, not to a global selector. The ATC form in each instance must read its own pack selector state. Testing with all three pdp-buy-box instances on one page and changing the pack in one must not affect the others.
What the primary purchase block contains
- Price row (now / was / save badge)
- Pack selector: 2 blocks (
single,bundle) in a 40/60 grid on mobile - Bundle block:
name,sub("4th FREE · $44.99 each"),ship("✓ Free shipping"),pill("Best Value · Selected") - Add to Cart button
- Free-ship callout (dynamic — swaps text based on selected pack)
What the repeats contain
- Same pack selector and ATC
- Free-ship callout
- No price row (the price is already established above)
- Same pill logic and dynamic callout
What the final CTA contains
- No pack selector. Singular 3+1 offer.
- Fixed price: $179.97 with $239.96 compare-at and Save $59.99 badge
- Fixed shipping line: "✓ Free shipping unlocked · In stock, ships today"
- Single button: "Add 3 to Cart (4th Bottle FREE)"
- Nim's note: the $179.97 hardcoded price in the mockup should read from the bundle variant so it stays in sync if the price changes.
8. Metaobject definitions
Create these in Settings → Custom data → Metaobjects. Enable Storefront access on every definition.
| Metaobject type | Fields | Used by |
|---|---|---|
pdp_step |
step_number (number), title (text), description (rich text) |
How it works |
pdp_pain_card |
icon (file), title (text), description (rich text), accent (boolean) |
Stain Nightmare |
pdp_faq |
question (text), answer (rich text) |
FAQ |
pdp_trust_badge |
icon (text/SVG name), label (text) |
Trust block, hero quick benefits |
9. Product metafield definitions
Create these in Settings → Custom data → Products. Enable Storefront access.
| Namespace.key | Type | Purpose |
|---|---|---|
custom.pdp_short_description |
Rich text | Hero short description (hidden by default in base variant) |
custom.pdp_badge_1 |
Text | Hero tag (e.g. "Australian Made") |
custom.pdp_trust_badges |
List of pdp_trust_badge references |
Trust badges in buy box |
custom.pdp_benefit_bullets |
List of single-line text | Buy box benefit bullets |
custom.pdp_steps |
List of pdp_step references |
How it works |
custom.pdp_before_after_images |
List of file references | Before / after grid |
custom.pdp_pain_cards |
List of pdp_pain_card references |
Stain Nightmare |
custom.pdp_instructions |
Rich text | Application instructions |
custom.pdp_cross_sell_product |
Product reference | Cross-sell card |
custom.pdp_faqs |
List of pdp_faq references |
FAQ accordion |
10. Product-specific decisions
No size selector — single-size products
The buy box goes straight from price to pack selector. Multi-variant products handled via a separate optional block if needed later.
Bundle defaults to selected
On page load, the 3+1 bundle is pre-selected. "BEST VALUE" pill only reads "· Selected" when the bundle is active. When single is active, the bundle pill drops the "· Selected" suffix and the single option gains its own navy "Selected" pill.
Mobile pack selector is 40/60 grid
At 599px and below, the pack selector is a two-column grid: 40% for single bottle, 60% for the bundle. Give the pack we want to sell the bigger share. Font sizes scale down on mobile so text fits.
Dynamic free-ship callout
Below the ATC. Bundle selected: "✓ Free shipping unlocked · Ships today". Single selected: "✓ In stock · Ships today". This is JS-driven in the mockup, should be Liquid-driven in the real build.
Campaign banner is one line
"Winter left its marks. Treat them now." Sits above the hero. No B3G1 badge underneath — the offer appears in the pack selector and ATC button, both within a screen.
Thumbnail strip has no tags
No Before / After / Rust pills. The images themselves carry the distinction. Removing them keeps the strip clean and avoids redundancy.
No sticky ATC bar
Removed permanently. The purchase block repeats replace it. Do not build a fixed-position bar.
Base variant: without description
The hero short description is hidden by default. Nim should set the base layout without the description paragraph. The variant toggle in the mockup is a review aid only — it does not go into the production build.
Reviews: 3 manual + Okendo carousel
Three manually curated reviews (driveway, pebblecrete, fibreglass) sit above the Okendo carousel. Below them, the Okendo app block renders the full review feed. No curation pass — all verified reviews flow through.
Okendo carousel placeholder
In the mockup it's a dashed-border block labelled "Okendo Carousel renders here". Nim replaces it with the actual Okendo app block embed via theme app extension.
11. Technical requirements
Header search conditional
On PDP templates, the theme header's search bar collapses to an icon. Everywhere else, it stays full-width. This is a theme-level change, not a section-level one.
{% if template.name == 'product' %}
<!-- search icon only -->
{% else %}
<!-- full search bar -->
{% endif %}
The mockup shows the target state visually. Nim builds the actual conditional.
Liquid access patterns
- Use
metaobjects.type.handlesyntax (the oldershop.metaobjects.type.handleform is deprecated) - Read metafields once outside loops, store in a variable, reuse inside the loop. Never access
product.metafields.*inside an iteration — it triggers a backend query per iteration and can add seconds to TTFB - Every section that renders metaobject content must include an existence check (
{% if block.settings.metaobject_ref != blank %}) with a graceful fallback
Known limits
-
metaobjects.type.valuesreturns a maximum of 50 entries per page. Paginate or split if any FAQ or comparison module exceeds this -
product.variantsis capped at 250. Useproduct.options_with_valuesfor pickers if needed - Both the metaobject definition and the metafield reference must have Storefront access enabled
Purchase block — instance scoping
- Every DOM ID prefixed with
{{ section.id }} - Every JS handler bound to the section's own container
- Pack selector state is per-instance
- ATC form reads its own state — not a global
- Test with all three
pdp-buy-boxinstances rendering simultaneously
Buy box form
- Wrap the ATC in
{% form 'product', product %}with the selected variant ID as a hidden field - JS updates the hidden variant ID when the pack selector changes
- Bundle math (RRP, savings, per-unit) is presentation logic — keep in section code with configurable numbers, not in metafields
Final CTA price sync
The final CTA band shows the bundle price ($179.97). Read this from the bundle variant rather than hardcoding, so if the price changes in admin the final CTA stays in sync.
Cross-sell
The fetchProductData() JS hits /products/{handle}.js for stock and price. Keep it scoped to this section only.
Reviews
The Okendo app block renders below the three manual cards. If the app block isn't present, render only the static review cards and hide the placeholder.
12. CSS strategy
Move shared styles out of the inline <style> tag into a single asset: assets/pdp-modules.css. Link from theme.liquid conditionally when the PDP template is active.
Each section may carry a small <style> block for styles unique to that section only, scoped with a section-specific class prefix. Do not duplicate shared styles.
Use CSS custom properties already defined in the theme's :root for colors, spacing, typography.
Do not port the annotation styles. The .tag, .labeled, .label-strip, .section-badge, .toggle-bar, .header-ref and .header-ref-note classes are mockup aids only. They don't go into the production theme.
13. Schema requirements
Every section must include a {% schema %} block with:
- A clear
name - A
presetso it can be added to other templates later -
settingsfor anything a non-developer should change -
blockswhere the module is repeatable -
max_blockswhere appropriate
For the buy box:
- Block types
singleandbundlewithmax_blocks: 2 - Each block stores:
variant_id,quantity,label,price_cents,badge_text,description,ship_text - A
headingsetting on the section
14. Build order
- Define metaobjects and metafields — content team can start entering copy immediately
- Build simple sections first: campaign banner, scanner chips
- Build the hero — gallery, title, rating, and the primary purchase block
-
Build the standalone
pdp-buy-boxsection for repeats. Test with two instances on one page before proceeding - Build content sections: how it works, before/after, pain cards, warning, instructions, comparison
- Build cross-sell and reviews (with Okendo embed)
- Build FAQ and the final CTA band
- Assemble the template JSON with all 15 sections in order. Test on one product
- Verify above-fold position on a 375×812 device against the live theme header
- Migrate the top 20–30 products
15. Pre-build checks
- Theme is Online Store 2.0 compatible
- Audit existing product metafields for naming collisions
- Confirm Storefront access enabled on every metaobject definition and metafield reference
- Confirm Okendo is available as a theme app extension block
-
Check theme's cart drawer behavior — confirm
cart:refreshevent is supported - Coordinate with theme owner on search collapse — header change, not a section change
- Confirm pack selector defaults to 3+1 — verify the pre-selected state renders on load, not just in the mockup
- Measure real theme header height at 375px — the above-fold target depends on it
16. Do not refactor
The following elements are working and must be carried into the production build unchanged:
- The science section (organic acid blend, 4-step mechanism)
- The step-by-step application guide with surface-specific instructions (concrete / fibreglass / vinyl / driveway)
- The comparison table
- The Oxi Shock cross-sell
- The FAQ block — contents and order
- The three surface-specific reviews (driveway, pebblecrete, fibreglass)
- The Swim Season Warning cost-consequence block
- The Stain Nightmare section — restored in V2.9 after being accidentally cut. Open question whether it's needed on PDPs, but restore until decided
17. Deliverables
- 14 Liquid section files under
/sections/ - Template JSON under
/templates/(e.g.product.pdp.json) with all sections in order -
assets/pdp-modules.csswith shared styles - Metaobject definitions created and documented
- Product metafield definitions created and documented
- One product fully populated and rendering correctly as a reference example
- Above-fold measurement screenshot on 375×812 confirming the ATC is visible without scrolling
- Short handover doc for the content team: which metafields to fill per product, which metaobjects to reference for shared content
18. Out of scope
- Page builders
- AI-generated copy
- Rebuilding long-tail SKUs
- Changes to the existing default PDP template
- Checkout or cart modifications beyond what the purchase block already does
- Variant selector for multi-variant products
- Sticky ATC bar (deliberately removed)
