George Donayre
All projects

Structured Before/After Results Carousel

Built a metaobject-driven carousel for displaying customer before/after results, letting the merchant add new entries from the Shopify admin without touching the section's code.

LiquidMetaobjectsJavaScriptCSS
Problem

The merchant wanted to feature customer before/after results with a photo, quote, timeframe, and result category, but the existing setup required a developer to edit the template every time a new result was added. Non-technical staff had no way to add, reorder, or remove entries on their own.

Goal

Build a carousel section backed by a reusable content structure so the marketing team can add new before/after entries, reorder them, and tag each with a result category, entirely from the Shopify admin.

My role

I built the frontend: the metaobject field structure for each result entry, the Liquid section that queries and renders those entries, the carousel/slider behavior in JavaScript, and the responsive styling. Content entry (photos, quotes, categories) is handled by the merchant's team, not by me.

Technologies: Shopify Liquid, Shopify Metaobjects, Metaobject definitions, Vanilla JavaScript, CSS scroll-snap, Dawn theme (base).

Implementation

Each before/after entry is stored as a metaobject with fields for before image, after image, customer name, duration (e.g. '3 weeks'), quote, and a result category tag that links to a relevant collection. This keeps the content structured and validated at the admin level, rather than relying on freeform text fields that are easy to enter inconsistently.

The Liquid section queries all published entries of this metaobject type, sorted by the merchant's chosen order, and renders each as a slide. Category tags render as clickable links pointing to the matching collection, so the carousel also functions as a soft navigation aid, not just a testimonial display.

The carousel itself uses native CSS scroll-snap rather than a JavaScript slider library, with a small amount of JavaScript to handle the next/previous button controls and keep the slide counter (e.g. '1 / of 4') in sync with scroll position. This kept the section lightweight and avoided pulling in a dependency for what is fundamentally a horizontal scroll behavior.

The results carousel showing one entry expanded, with before/after image toggle, customer quote, duration, and a linked result-category tag.

The results carousel showing one entry expanded, with before/after image toggle, customer quote, duration, and a linked result-category tag.

JavaScript: syncing slide counter to scroll position

function updateSlideCounter(track, counterEl, totalSlides) {
  const index = Math.round(track.scrollLeft / track.clientWidth);
  counterEl.textContent = `${index + 1} / of ${totalSlides}`;
}
Challenge & Solution

The challenge

Metaobject entries with a missing 'after' image caused the before/after toggle to break, since the toggle assumed both images always existed.

The solution

I added a Liquid conditional that falls back to displaying only the before image with the toggle control hidden entirely when no after image is present, rather than showing a broken or empty state. This kept incomplete entries from looking like a bug.

Result

Insert real outcome here. If you have measured metrics (e.g. engagement with the carousel, click-through to linked collections), include them. If not, describe the qualitative change: what the marketing team can now do independently that they couldn't before, and how that changed their ability to keep this section current.

Lessons learned

When a frontend calculation mirrors a backend rule, keep the source of truth in one place and mirror it exactly rather than approximating. Small rounding or threshold differences show up immediately to shoppers and erode trust in the discount shown.

If you want a testimonial or results section your team can update on their own without waiting on a developer, that's the kind of metaobject-driven build I can set up in your theme.