SizeSport Virtual Fitting Room
A Shopify custom app that recommends apparel and footwear sizes for a multi-brand sports retailer, backed by an authoritative, provenance-tracked size-chart dataset scraped exclusively from official brand websites.
// AI capabilities
- Generative virtual try-on (photo of the shopper composited onto the product image via a swappable provider adapter)
Overview
SizeSport is a Virtual Fitting Room built as a Shopify custom app for Mike Sport, a multi-brand sports retailer. Shoppers enter height, weight, age, and optional body or foot measurements and get a recommended size, a fit label (from "Tight" to "Loose"), cross-standard size equivalences (EU, US, UK, alpha), and a brand size guide, all rendered through a theme app extension so the storefront theme itself is never touched. A companion admin app (Polaris, embedded in Shopify Admin) lets the merchant manage brand size charts, toggle features, target specific products or collections, and configure a generative "Try It On" experience with per-user usage caps.
Problem
Sports retailers carry dozens of brands, each with its own size logic, measurement columns, and regional label systems (EU/US/UK/alpha/CM), and most brands publish this data only on their own official sites, many of which actively block scraping. Any recommendation feature is only as trustworthy as the data behind it, so the project's hard constraint was: real, verifiable, per-brand sizing data only, with zero invented or reseller-sourced numbers, and zero edits to the client's live theme.
Approach
Two halves built in tandem: a data pipeline and the app that consumes it.
Data: a free-first, escalating scrape strategy defeats brand-site anti-bot blocks without paid unlockers: server-rendered locale pages render full tables where the flagship .com page is JS-only; archived snapshots recover pages that now fail on the live site; a scrape endpoint reliably bypasses the blocks that stop plain fetchers. Every accepted number is traced back to a saved raw capture and a source URL, scrape date, method, and status (verified or partial). Brands with no usable official chart are explicitly dropped rather than filled with reseller or invented data, and one mid-scrape drift onto retailer sites was caught, the tainted raw data deleted, and the brand dropped.
App: a standard Shopify custom app (Remix/React Router, Prisma, Postgres) with three surfaces: a theme app extension that ships the recommendation button and modal as the extension's own Liquid block (the merchant's only touch is a one-time app-embed toggle), a Polaris admin app for settings and a brand size-chart manager, and a backend sizing engine that reads the seeded chart data.
Architecture
size-charts/: the permanent source of truth, one JSON file per brand, validated in CI against_schema/brand.schema.json;_index.jsonis a deterministically generated registry of brand status, category and row counts.scraper/: per-brand Python scrape/build/validate scripts plusraw/<brand>/archives that back every number in the dataset.app/: the Remix/React Router Shopify app;engine.server.tsruns the size-matching logic against Postgres (seeded from the size-charts JSON via Prisma),tryon.server.tsproxies the generative try-on call.extensions/sizesport-embed/: the theme app extension (Liquid block plus JS asset) that renders the storefront widget without any theme file edits.nginx/plus a VPS setup doc plus a GitHub Actions deploy workflow: a CI/CD pipeline to a self-managed VPS (SSH, git pull, prisma migrate deploy, PM2, Nginx/Let's Encrypt).
Tech stack
- Remix (React Router) on Node, TypeScript
- Shopify App Bridge + Polaris (admin UI), Shopify App Remix + Prisma session storage
- Prisma ORM over PostgreSQL
- Shopify theme app extension (Liquid + vanilla JS) for the storefront embed
- Python scraper suite (per-brand parsers, archived-snapshot recovery) with JSON Schema validation
- Swappable generative try-on provider adapter
- PM2, Nginx, Let's Encrypt, GitHub Actions for CI/CD to a self-managed VPS
Engineering highlights
- Non-negotiable provenance rule enforced end to end: every category in every brand file carries
source.url,scrapedAt,method, andstatus, checked by an automated source-domain audit that flags any non-official (retailer/reseller) domain. - "Flag, don't fake" data policy: brands with no verifiable official chart are dropped and documented with the exact URLs checked, rather than backfilled with guessed or reseller numbers; a live policy violation (drift onto a retailer site) was caught mid-run, purged, and logged.
- Sizing engine does measurement-key overlap scoring to pick the right chart for a product (so a sports-bra chart cannot hijack a t-shirt recommendation), computes a continuous fit ratio to derive "Tight" through "Loose" labels, and falls back to a labels-only conversion mode when a brand publishes no body measurements.
- Coverage-benchmark report cross-references the scraped brand corpus against a live sample of the retailer's actual product catalog (by vendor and product volume) to quantify real coverage rather than reporting on paper coverage alone.
- Try-on usage metering (IP-hash plus device-token keyed) enforces a default tries cap without ever persisting a user's photo, and the admin never stores the provider API key in the database, only reads it from a server environment variable.
- Zero theme edits: the entire storefront surface ships as the app's own theme app extension; the only merchant action is flipping the app-embed toggle once in the Theme Editor.
Want to dig deeper?
Ask my AI agent anything about how this was built, what tradeoffs I made, or how it could fit your team.
Ask my AI →// related projects
Dynamic Bundle Builder
An embedded Shopify app that mines a store's real order history for co-purchase patterns and automatically surfaces "frequently bought together" bundles on the storefront, with Claude writing the shopper-facing names and pitches under a hard spend cap.
Homepage Curating Agent for Shopify
A planned Shopify app that rebuilds the storefront homepage and merchandising every morning around live demand and inventory, using a Perceive, Decide, Act, Learn loop with a staged autonomy ramp from shadow proposals to autonomous publishing.
Shopify Product Label App
A private, single-store Shopify app that shows rule-driven marketing labels on product cards in collection and search pages, built first for the Crocs/mikesport store and then cloned and re-skinned as an isolated app for MikeSport Lebanon.