Tabbed Collection Product Slider and the Shared Merchandising Card
One section renders every configured collection at once and switches between them with a class, so moving from Serums to Cleansers waits on nothing — and one card snippet backs the shelf, the blog and the rewards page.
A curated product shelf usually costs either a fetch on every tab switch or a second card renderer per context. This section server-renders every configured collection and toggles one active class, and it renders the same card snippet everywhere it appears — with the add-to-cart control carrying its own line-item property payload, so a slider add matches a product-page add.
Fact Strip
- Client: Three Ships — clean skincare DTC, threeshipsbeauty.ca
- Surfaces: Landing pages, kit product pages, article pages, the rewards page
- Templates served: Ten
- Complexity: Medium — roughly 800 lines across the section, three card snippets and the stylesheet they share
- Attribution: Deploi-authored. The section, the card snippets and the shared stylesheet are ours. They sit inside Palo Alto 5.8.0 by Presidio Creative, a paid premium theme the client licenses — we did not write that theme and we do not claim it.
- Status: Live — verified 2026-09-06
- Evidence: Moderate confidence — documented from the code
- Platform primitives: 6
The Problem
A storefront built on kits and routines needs the same module in a dozen places: a curated set of products, usually grouped by category — serums, creams, cleansers — with a route out to the full collection and a card that looks and behaves the same wherever it lands. Shopify's featured-collection pattern renders exactly one collection, and a theme ships one card partial designed for the collection grid. Neither covers the case where the module is the category navigation. And a catalogue with staged launches and retired SKUs carries products that exist but are deliberately not for browsing, so any module that reads a collection directly has to know which products that collection is not allowed to show.
The Constraint
Two things were fixed before a line of this section was written. Products flagged as hidden must never surface in a curated module, and the flag is a tag applied in two spellings — hidden and #hidden — because the convention accumulated over years and nobody normalized it. A filter that handles one spelling leaks a product onto a merchandised shelf.
The second is the theme. Palo Alto 5.8.0 is licensed vendor code with its own card partial, its own stylesheet conventions and its own upgrade path, so a merchandising card with this much in it had to live alongside the vendor's card rather than take its place. And the module appears on landing pages, kit product pages, the blog and the rewards page, each wanting a slightly different card — an editorial one with no buy control, a points one for loyalty. Three cards that drift apart is the default outcome, and it was the thing to design against.
What We Built
product-slider.liquid takes a repeatable collection block — one block per tab — and renders a product-slider__group for every one of them. All of them, server-side, in the same response. Only the first carries the active class. The desktop tab bar is keyed on data-handle, so a tab click is a class swap against a group that is already in the document; on mobile the tab bar gives way to a progress indicator, because a horizontal strip of category names is not a mobile control.
Each group renders product-card.liquid per product. The card switches identity by context: product-card-points.liquid takes over when the page handle contains rewards or the template is account, so a shelf on the loyalty page shows what a product costs in points rather than in money. product-card-for-article.liquid is the third variant, rendered by main-article.liquid for the blog — a separate snippet rather than a third branch inside the main card.
The shared card is where the merchandising vocabulary lives. It resolves a badge through a chain that starts at the my_fields.product_card_badge metafield, so a merchandiser labels a product on the product record rather than in the section. It carries a benefit line from my_fields.target — the one-line reason this product is in this shelf — and a Yotpo star mount for the review row.
The buy control is an inline js-add-to-cart element carrying data-id and a data-properties JSON payload. That payload includes _product_meta, the component contents of a kit, which is what makes an add from a shelf indistinguishable downstream from an add on the product page itself. The metadata rides on the card, not in the handler.
Styles live in section-product-slider.css.liquid, and the same stylesheet is pulled in by the article section and the skin-quiz section — one visual contract for the card wherever it renders.
Why This Way
Rendering every tab and toggling a class is a straight trade of bytes for latency. A shopper who opens the third tab gets it instantly because it was never absent; the price is that every configured collection's products sit in the document whether or not anyone looks at them. That bounds the section at two to four collections, and it makes adding a fifth a page-weight decision rather than a merchandising one — which is a constraint a merchandiser has to be told about, because nothing in the theme editor stops them.
Putting the line-item property on the card's data-properties rather than building it in the click handler keeps the metadata next to the product that owns it. The cost lands in the markup contract: any card rendered outside this section has to carry the attribute itself, or the add silently loses its payload. That is a convention held by discipline, not by the schema.
Implementation Notes
- The tab bar renders only when the section has more than one
collectionblock. A single-collection slider drops the tabs entirely and shows just the heading and its shop-all link, which is the configuration most kit product pages use. - The mobile heading falls back to
Shop <collection title>when a block has no title set, so an unnamed shelf still reads as a destination rather than as an anonymous row of products. - A
show_learnsetting swaps the card's add-to-cart control for a "Learn More" link. That single toggle is what lets the same section be editorial on an article page and transactional on a landing page. - The hidden-product filter is one
{% unless product.tags contains 'hidden' or product.tags contains '#hidden' %}wrapping the card call, covering both tag spellings the catalogue actually uses instead of assuming the tidy one. section-product-slider.css.liquidcarries the.css.liquidextension but contains no Liquid at all. It is referenced assection-product-slider.cssby the slider, the article section and the skin-quiz section.- The blog gets its own card snippet rather than a flag on the main card, because the article context has no buy control at all and branching for that inside the shared card would have put a rendering mode in every call site that never uses it.
Edge Cases
- Sold-out products render a disabled "Sold Out" control in place of the add button, so a card never offers an action that cannot succeed.
- A rewards or account context switches the card to the points variant, so the loyalty page never shows a dollar price where a points price belongs.
- A single configured collection renders with no tab bar at all, rather than a one-item tab strip that looks like a control and isn't.
- A block with no title still produces a mobile heading, assembled from the collection's own title.
- Editorial mode removes the buy control rather than disabling it, so a card inside an article never presents a dead button.
Platform Primitives Used
- Liquid section schema and settings — the
collectionblock type is the entire merchandising vocabulary: pick a collection, order the blocks, and the tabs follow. - Theme blocks — one block per tab, so re-ordering tabs is a drag in the theme editor rather than a deploy.
- Metafields —
my_fields.product_card_badgeandmy_fields.targetput the badge and the benefit line under per-product merchandiser control. - Cart AJAX API — the in-card control posts through the theme's own AJAX cart instead of navigating to the product page.
- Line-item properties — the
_product_metapayload is what makes a kit added from a shelf carry the same component metadata as one added from its own page. - Liquid snippets — one card partial with two context variants is the reason a shelf, an article and the rewards page agree on what a product card is.
Integrations in Play
- Yotpo — the card places the vendor's star mount rather than standing up a second ratings surface. We have not checked on the live page what that mount renders inside a slider, so we are not claiming a working star row on these cards.
Where It Runs
Ten templates mount it: three page templates — a holiday landing page, the rewards page and a minis shop — and seven kit product templates, where the shelf works as a cross-sell under the buy box. The blog reaches the same card through its own article section, and the skin-quiz section pulls in the same stylesheet.
What This Demonstrates
Primarily an AJAX cart drawer and line-item rules without an app build — the interesting part isn't the carousel, it's that a card adds a kit with its component metadata attached and nothing else in the theme needs to know the shelf exists.
It's also a collection merchandising build: merchandiser-configured rails with one card renderer behind them.
How We Know
Five theme files read straight off the live theme — the section, three card snippets and the stylesheet they share — plus the ten templates that mount them, which is where the surface count comes from. Documented from the code; no client-side record of the decision survives. Nobody wrote down why the tabs pre-render rather than fetch, so this page describes the mechanism and leaves the intent where we found it. What the files do is checkable. Why is our reading.
Related Builds
- Merchandisable homepage product carousel with a tag-driven badge engine and inline add-to-bag — LUS Brands. The same instinct on a different storefront, with one collection and a rail instead of tabs.
- Product card with award badges, per-variant content payload and per-card structured data — Nudestix. One card renderer carrying a badge vocabulary and a per-variant payload, taken considerably further.
- Main collection grid: merchandisable promo tiles, load-more and a filter blocklist — Nudestix. The same merchandiser control applied inside a paginated grid.
The Buy-vs-Build Question
The bought version of this is a slider app plus its own add-to-cart script, which means a second system that believes it owns the cart on a theme that already has one. The real question isn't the carousel — carousels are cheap — it's whether the shelf's buy control belongs to the theme's cart or to the module that drew it. We've written up where that line sits in Slide cart and sticky add to cart: buy or build?
Ready to Make One Shelf Work on Ten Templates?
If every curated row on your site is a different section with a different card, you're maintaining the same merchandising decision in five places and losing one of them every release. Contact us today to talk about what one shared card would take on your theme.