Before/After Customer Results Carousel With an Attached Product Card
One slide per customer result: two labeled photos, a quote with its attribution, and the product that was used — rendered as the storefront's own product card, with the live price and a working add-to-bag, on a page template where Shopify hands the theme no product at all.
A Shopify page template puts no product in scope, and a block's text fields hold only what a merchandiser types. This results carousel gives each slide a before/after photo pair with authored badge labels, a quote with attribution, and a product picker whose product renders through the storefront's own card — so price, availability and add-to-bag come from the live catalog, and nothing about the product is stored in the section.
The Problem
Shopify gives a section two ways to hold content, and they don't meet. A product setting hands the section a live product object: price, variants, availability, media. A text setting holds whatever a merchandiser typed. A customer-results module needs both at once — an editorial half only the brand can write, and a commerce half only the catalog should own. The moment a price or a sold-out state is typed into a block, it's a second copy of the truth with nobody to keep it current.
The Nudeskin page that shows customer results needed each one to be one unit: a before photo and an after photo with their labels, the customer's words and who said them, and the product they used — addable from the slide rather than from a product page two clicks away.
The Constraint
Before and after photos can't rely on position to say which is which. The label has to be authored text rendered over the image, and it has to be per image, because a slide may carry one photo without the other.
The product's price, availability and add-to-bag could not be section content. Nudestix already has a card renderer, card-single-product, that carries the storefront's price formatting, sold-out and sale badges, variant handling and the add-to-bag form. A second card built for this section would drift from it the first time either changed, so the slide had to render the store's card, not describe one.
The section sits on a page.* template, where Shopify puts no product in scope, so the product has to arrive through a block-level picker. And the desktop layout puts the heading inside each slide while the phone layout wants one heading above the slider — two headings, only one visible at a time.
What We Built
guarantee-review-section.liquid and its stylesheet, guarantee-review-section.css. One block type, review, with everything a slide needs: a product picker; title, desc, author_name and author_detail; a g_pd_title label for the product; and two image pickers, image1 and image2, each paired with its own badge text, badge_text1 and badge_text2. The section itself has one setting, title1, the phone heading.
Each block becomes one swiper-slide laid out as a two-column row. The left column is the photo pair: custom-review-before and custom-review-after, each a square box (padding-top: 100%) with the photo object-fit: cover inside it, and the badge text rendered bottom-left over the image in a translucent white strip as .before-badge and .after-badge. Two uploads of different shapes sit as two equal squares.
The right column is the editorial half and then the commerce half. The block's title renders as a <p class="h1" role="heading" aria-level="2"> — the page's h1 type scale, level two in the document outline. The description, author name and author detail follow, then g_pd_title as a level-three heading. Below it, if the block has a product, the section renders card-single-product with card_product: block.settings.product and section_id: section.id. That snippet is the storefront's card: the product's featured image, sold-out and sale badges computed from the product object, the shared price snippet with use_variant, the theme's variant picker where the product has more than one variant, and a <product-form> whose variant input and add-to-bag button come from the same live object — the button disabled when the selected variant isn't available. Nothing about the product is typed into the block except which product it is.
The section's title1 renders above the slider as mobile-section-title, shown below 768px, while each slide's desktop-section-title is hidden there; above that breakpoint the roles flip. Swiper supplies the slide movement, clickable pagination bullets and the arrow pair, .g-review-prev and .g-review-next, which sit outside the content column from 1350px up and become translucent round overlay controls on the photos on phones.
Why This Way
The product picker is the whole decision. Because the slide renders card-single-product against a product object, the card shows what the catalog says at render time: today's price, the current sold-out state, the add-to-bag the rest of the store uses. A discontinued product shows as sold out; a price change on the product shows on the page with no edit to the section. The editorial half — the photos, the labels, the quote — is the part only a merchandiser can supply, and it's the only part the block stores.
The two-heading split is what it costs. Desktop titles are per slide and can differ; the phone heading is one string for the whole section, so a carousel whose slides want different headings reads as one heading on a phone.
Why Not an App
A photo-review app collects images from customers and shows them in its own widget, in its own order, with its own product association. The page needed something the widget's settings don't expose: a specific pair of photos with brand-authored labels, a specific quote, and the store's own product card beside them, in a carousel that's part of the page's layout rather than an embed inside it.
This section also isn't review collection. Nothing is submitted, moderated or synced; a merchandiser chooses the result, writes the labels and picks the product. That's merchandising, and it belongs in the theme, where the product card and the cart form already live. An app that owned this layout would have to render a card of its own next to the theme's, and the product tile would stop being the one the rest of the store shows.
Implementation Notes
- One block type,
review, and one section setting. Everything a slide shows, apart from the phone heading, is a block field, so results are reordered by reordering blocks. The section ships as a preset in the editor's ADVANCED LAYOUT category. - The badge over each photo is a text setting per image,
badge_text1andbadge_text2, not a hard-coded "Before" and "After" — the wording is the merchandiser's, and a slide with one photo carries one label. - The card is rendered with
section_id: section.id, so the card's form id is composed from this section's id and the product id. - Both headings are
<p role="heading">with an explicitaria-level: the slide title at level two, the "products used" label at level three. Theh1class sets the type scale; the ARIA sets the outline level, and the two are set independently. - The photo pair is a flex row of two square boxes capped at half the row each, so the before and after photos are always the same size on screen regardless of the aspect ratio of the uploads.
- On phones
.real-people-wrapswitches tocolumn-reverse, so the attribution sits above the quote visually while the DOM keeps quote first and attribution second at every width. - The arrow pair is the section's own elements,
.g-review-prevand.g-review-next, styled by the section's stylesheet rather than Swiper's default arrow rules.
Edge Cases
- Every block field is blank-guarded individually, so a slide can be photos with no quote, a quote with no photos, or either without a product card.
- The after photo and its badge render independently of the before photo; a block with only
image2set shows one labeled square, not an empty box beside it. - If the picked product is no longer available to the online store,
block.settings.productis blank and the slide renders without a card rather than with a broken one. - A sold-out product still renders its card: the sold-out badge comes from the product object and the add-to-bag button is disabled and reads sold out, so the result stays on the page with its state visible.
- A product with only the default variant renders no variant picker; the card goes straight from price to the add-to-bag form.
- A blank
title1skips the phone heading entirely; the desktop titles, being per block, are unaffected.
Platform Primitives Used
theme-blocks— each customer result is onereviewblock, so the archive is ordered, added to and trimmed in the theme editor without a developer.liquid-schema-settings— aproductpicker, two image pickers, two badge fields and the quote fields per block; one section-wide setting for the phone heading.
Integrations in Play
- Swiper — the slider library. Slide movement, the pagination bullets and the arrow behavior come from it; the section supplies the markup and the arrow styling.
Where It Runs
Landing pages. One page template mounts it, page.exploration, directly below the why-it-works section. One live page on the storefront renders it, and that's where we checked it: three slides, each with its photo pair, its quote and a product card drawn from the catalog.
What This Demonstrates
- Testimonials, press and social proof modules — the primary capability: a merchandiser-authored results carousel whose proof is content the brand chose and worded, built at theme level because the layout holds a live product card.
- Before-and-after results modules — the badge-over-photo pairing with an attached buyable product card, the pattern this capability is named for.
How We Know
One section file and its stylesheet, roughly 470 lines between them, plus the JSON template that places the section. Documented from the code; no client-side record of the decision survives. The reasoning above — the product picker, the shared card, the heading split — is reconstructed from what the files do. We checked the section on the live storefront on 2026-09-07 and found it mounted, populated and rendering its product cards from the catalog.
Related Builds
- Kids featured collection: a custom element that builds its Swiper on approach and wraps the theme's own product cards — a different storefront making the same decision in a product rail: render the theme's card rather than a card of its own.
- Featured tutorial with an adjacent shoppable product rail — the same storefront, products beside editorial content, with the same
role="heading"treatment for a section title. - Press / trust logo strip with scroll-snap carousel and no JavaScript — the other kind of proof module: press logos as blocks, on a storefront that decided a row of images needed no slider at all.
- Tabbed collection product slider and the shared merchandising card — a third storefront's slider whose tiles are the shared card, for the comparison across theme bases.
The Buy-vs-Build Question
A testimonials app buys a widget that holds the quotes and renders them its way; the layout is the app's, and so is the script. Building it in the theme bought a slide that holds the store's own product card next to the customer's photos, and cost a section that's ours to maintain. Where that trade lands is the subject of testimonials and press wall: buy or build?.
Provenance & Evidence
- Client: Nudestix — nudestix.com
- Surface: Landing pages
- Templates served: one —
page.exploration, on one live page - Complexity: Low
- Scale: roughly 470 lines across two files
- Attribution: Deploi-authored. The section, its block schema and its stylesheet are ours. It runs in a theme built on Dawn 6.0.2 and deliberately renders the storefront's shared
card-single-productsnippet for the product tile rather than a card of its own. - Status: Live, verified 2026-09-07
- Evidence: one section file and its stylesheet from the theme, the template that places it, and a live check of the one page that renders it
- Confidence: Moderate — documented from the code; no client-side record of the decision survives
- Primary capability: Testimonials, press and social proof modules
Ready to Put the Product Next to the Proof?
You dream it. We build it. If your results page shows a customer's photos and then sends the shopper off to find the product, Contact us today and we'll put the store's own card on the slide — live price, live stock, one tap.