Back to portfolio

Platform app · 5 min read

Cardify: restyling a storefront's product cards without touching its code

A drop-in app for the Ratio platform, live on 16 storefronts. A merchant flips one switch and the storefront's product cards are replaced by one of five designs, safely and with no reload.

  • Lit
  • Fastify
  • MySQL
  • React
  • Playwright

Source code (lite version)

Merchants on a commerce platform often want a different look for their product cards, but changing card markup means touching every widget that renders one: collection grids, carousels, search results and related products. I built Cardify as an app that does it from the outside. The merchant picks a design in an admin screen, and the storefront's own cards are swapped live.

How a card gets replacedThe storefront only exposes a few data attributes. Everything else is loaded on demand.

Setup

  1. Merchant admin
    React + Vite. Switch on, pick a design and colours
  2. Settings API
    Fastify + MySQL. Admin writes are key-protected
  3. Public config
    Read-only endpoint the storefront loader calls

On the storefront

  1. Tiny loader
    0.5 KB gzipped. Fetches the config
  2. Config says off?
    Do nothing. Native cards stay
  3. Load widget
    About 10 KB gzipped, imported lazily
  4. Overlay
    Hide each native card, mount a Cardify card in its cell

When a shopper clicks

  1. Cardify card
    Click or quick-buy
  2. Hidden native link
    The click is delegated to the store's own element
  3. Store behaviour runs
    Routing, quick-buy popup and analytics, unchanged

Design choices that matter

  • Purely cosmetic. A card uses exactly the data the native card already renders, so there is no new data contract for the storefront to maintain.
  • Native cards are hidden, never removed. The overlay mounts inside the same cell, so the store's grid and carousel layout is untouched, and a MutationObserver picks up cards that appear later.
  • Behaviour is delegated back to the host. Clicking a Cardify card clicks the hidden native link, so the store's own routing, popups and tracking keep running.
  • It fails safe. If the backend is down, the config call errors or the bundle is blocked, the shopper simply sees the original cards.
  • No polling. The config refreshes on page load and when the tab regains focus. A timer would make every visitor's tab hit the backend.
  • The server refuses to start without an admin key, so a missing setting can never mean an open admin.

Five designs, one structure

Classic, minimal, overlay, hover-reveal and editorial all share one DOM structure and differ only in CSS. That is why click and quick-buy bridging works for every design for free. Merchants can tune accent, text and background colours, corner radius, image shape and toggles for the badge, quick-buy, rating and compare-at price, all validated with a shared zod schema. Adding a sixth design is a registry entry plus one CSS block.

Status: live in production on 16 storefronts on the Ratio platform, backed by unit tests and a headless-Chromium end-to-end suite.