50+ features across 10 modules. One cartridge.config.ts file controls everything. Zero API tokens required. Deployed in under an hour.
Every product, collection, and page from your Shopify store, rendered in a custom luxury theme with sub-30ms page loads. No Shopify template limitations.
Press Cmd+K (Mac) or Ctrl+K (Windows) on any page. A Spotlight-style search modal opens instantly. Type to filter products and collections in real-time. Arrow keys to navigate, Enter to select, Escape to close. Results show product thumbnails, titles, prices, and vendors. Data fetched on first open and cached client-side, so there is no API call per keystroke.
Hover the main product image on any product detail page. A 2.5x magnified view follows your cursor, showing fabric texture, stitching detail, sign finishes, the kind of detail that matters for $500+ products. Desktop only; mobile uses native pinch-to-zoom. "Hover to zoom" hint badge in the corner. Zero npm dependencies, just CSS background-image positioning.
Hover any product card on any collection page and an eye icon appears in the top-right corner. Click it to open a modal with the product image, title, price, variant selector, and Add to Cart button, without navigating away from the collection. The modal includes a "View full details" link for customers who want more. Built with Framer Motion spring physics.
Heart icons on every product card (top-left). Click to save, click again to remove. Persisted to localStorage, survives page reloads and browser sessions. Dedicated /wishlist page showing all saved items in a product grid. Heart badge with count in the site header. Works across all product grids (homepage, collections, recommendations, quick view).
On /collections/all, a horizontal scrollable filter chip bar appears above the product grid. Filter by product type (Signs, Rugs, Art, etc.) and price range (Under $100, $100-500, $500+). Client-side filtering, no API calls, instant results. "Clear filters" button with product count. Filter labels configurable in cartridge.config.ts.
Numbers on the About page and landing pages count up from zero when scrolled into view. "30+" becomes 0, 1, 5, 12, 22, 30+ over 1.5 seconds with easeOut easing. Built with Framer Motion's useMotionValue + animate. Fires once (useInView with once: true). Suffix support: "+", "%", "K".
Share buttons on every product detail page, next to the price: Copy Link (with "Copied!" feedback for 2 seconds), Native Share (uses navigator.share on mobile, hidden on desktop if unsupported), and WhatsApp deep link. Share URL uses the canonical site URL from config.
On mobile viewports (<768px), when the main Add to Cart button scrolls out of view, a fixed bottom bar appears with the product title (truncated), price, and action button. Uses Framer Motion's useInView on the main CTA ref. Disappears when you scroll back up to the real button. For inquiry items, shows "Inquire" instead of "Add to Cart".
Below the Add to Cart button on every product page: three expandable accordion sections. Shipping, Returns, and Custom Orders. Content reads from brand.shipping config (free shipping threshold, flat rate, custom item note, return days, custom refundability, lead times). Animated expand/collapse with Framer Motion's AnimatePresence.
Every product detail page shows a colored availability badge above the price: ● Ready to ship (priced + available), ● Hand-crafted in 4-6 weeks ($0 / quote items), or ● By consultation only (unavailable). Lead time reads from config.
Every product detail page has a "You Might Also Like" horizontal scroll strip below the description. Deterministic scoring algorithm: vendor match (+5), product type match (+4), shared tags (+2 each), similar price range (+2). Server-rendered with Suspense + shimmer loading skeleton. Snap-scroll on mobile. Shows up to 8 related products.
A Claude-powered chat assistant on every page that knows your entire product catalog, captures leads, and handles custom order intake automatically.
White circle button (bottom-right of every page). Expands to a 400×600px panel on desktop, full-screen sheet on mobile. "Ask our AI concierge" hint bubble appears after 8 seconds on first visit. Dark zinc theme matching the storefront aesthetic. Framer Motion spring animations for open/close.
The full product catalog (titles, handles, vendors, prices, types, tags, descriptions) is injected into the Claude system prompt on every chat request. Rebuilt from Shopify's public JSON API every 5 minutes (in-memory cache). With 92 products, this is ~15-20K tokens. well within Claude's context window. No RAG, no vector store, no database.
searchProducts: Searches the product catalog by keyword with scored matching (title +3, vendor +2, type +2, tag +2, word match +1). Returns top 6 results with links and prices.
createQuoteRequest: When a customer provides their email and describes what they want, creates a HubSpot lead with full details (car info, product interest, notes). Returns "Quote request submitted."
captureEmail: Lightweight email capture when a customer shares their email casually in conversation. Creates a HubSpot subscriber contact.
Uses Vercel AI SDK v6 streamText with Claude Sonnet 4. Responses stream in real-time. first token appears in <1 second. toUIMessageStreamResponse() consumed by useChat hook on the client. Three-dot typing indicator while waiting for first token.
In-memory sliding window: 20 requests per minute per IP. Returns 429 with Retry-After header. Periodic cleanup of expired entries. Adequate for current traffic; upgrade to Redis for high-volume production.
HubSpot integration that turns every form submission, email signup, and checkout attempt into a trackable CRM contact with lifecycle stage management.
The contact page form (name, email, interest, message) submits to /api/hubspot/contact. Creates or updates a HubSpot contact with lifecycle stage "lead", lead status "NEW", name split into first/last, and product interest property. After submission, pushes HubSpot identity for session stitching.
Email input in the site footer on every page. Submits to /api/hubspot/newsletter. Creates contact with lifecycle stage "subscriber" and source tag (e.g., "footer_signup"). Success state: "You're in. Welcome to the collector community."
When a customer clicks "Checkout" in the cart drawer, a modal asks for their email before redirecting to Shopify checkout. Creates contact with lifecycle stage "opportunity" and cart contents in notes. "Skip" button for immediate redirect. Shows only once per session (localStorage). Captures the lead even if they don't complete checkout.
Appears after 20 seconds on first visit. "Join the collector community, get early access to new pieces and exclusive offers." Email goes to HubSpot as a subscriber. Dismissed state persisted to localStorage so it never shows again. Framer Motion slide-up from bottom-right. Close button or backdrop click to dismiss.
instrumentation-client.ts exports onRouterTransitionStart which pushes setPath + trackPageView to HubSpot's tracking queue on every client-side navigation. Without this, HubSpot only sees the initial page load. Centralized tracking helpers in hubspot-tracking.ts.
Loaded on every page via Next.js <Script strategy="afterInteractive">. Non-blocking, loads after hydration. Tracks page views, sessions, and behavioral data. Portal ID from environment variable. Conditionally rendered: if no portal ID is set, no script is loaded.
An embeddable product catalog that any partner, affiliate, chapter, or distributor can add to their own website with one line of HTML.
Partners paste one line: <iframe src="https://yourstore.com/embed/store">. Gets a fully functional product grid with images, prices, and "Order Now" buttons linking to Shopify checkout. Self-contained HTML page with no header/footer from the main site, no CSS conflicts with the partner's site.
?theme=light or ?theme=dark. All colors, backgrounds, button styles, and text colors switch. ?cols=2|3|4 controls the product grid column count. Responsive on mobile regardless of setting.
?region=milwaukee. Filters products by Shopify tag matching the region name. If no region-specific products exist, falls back to showing all products in the collection. Each region gets its own URL, its own filtered view.
The embed page sends a postMessage to the parent window with the current document.body.scrollHeight on every resize. Partners add a small script to listen for the message and adjust the iframe height, eliminating the scrollbar. Uses ResizeObserver for efficient updates.
Structured intake forms for businesses that sell custom, bespoke, or quote-based products. Replaces "email us" with guided data collection.
Define forms in cartridge.config.ts with a slug, title, subtitle, and array of fields (text, email, tel, textarea, select). Each form renders at /custom/[slug]. Fields, labels, placeholders, and options all from config. Pricing hints and lead times displayed alongside the form.
Products with $0 price or unavailable status get smart "Request Quote" buttons that link to the appropriate custom form based on product title/type matching. Rug products to rug form. Display pad products to configurator form. Everything else to inline expanding name+email form. Zero dead-end "Sold Out" buttons.
Form submissions POST to /api/hubspot/custom-order. Creates or updates a HubSpot contact with lifecycle stage "lead", lead status "NEW", product interest, and all form field values in structured notes. Founder name and response time in the confirmation message, read from config.
Branded landing pages for trade shows, car shows, conferences, and live events.
Define events in config with slug, name, tagline, and location. Each generates a page at /event/[slug]. Centered layout with event branding, name+email capture form, and quick links to key product categories. Any unknown slug gets a generic branded fallback page. Print a QR code to attendees scan to land on your branded page to you capture their email.
Every email captured from an event page is tagged in HubSpot with the event name and slug as the source. "Event Lead: Road America 2026" appears in HubSpot contact properties. Enables post-event follow-up segmentation and attribution reporting.
Schema.org Product markup on every product page: name, description, images, brand, offers (price, currency, availability), URL. Schema.org Organization on every page: name, URL, description, founding date, founder. Validated against Google's Rich Results Test.
/sitemap.xml generated at build time with all products, collections, and static pages. Product URLs include lastModified from Shopify's published_at. Change frequency and priority set per page type. Base URL reads from config.
Four API routes ready for ChatGPT's custom GPT builder: GET /api/gpt/products (search with query, collection, vendor, price filters), GET /api/gpt/collections (list all), POST /api/gpt/quote-request (create lead), GET /api/gpt/openapi.json (OpenAPI 3.1.0 schema). CORS headers included. Create a "Chat with [Your Brand]" GPT in minutes.
Allows all crawlers, disallows /api/. Points to sitemap URL from config.
A full merchant command center built into every Cartridge deployment. Six pages of analytics, catalog intelligence, and actionable recommendations. Not a third-party integration. Not a separate tool. Built into the same Next.js app, at /admin.
The morning view. Five stat cards across the top: revenue today, new leads, AI chat sessions, embed widget loads, and total products. Below that, 7-day SVG sparkline charts for revenue, orders, and leads. Then two columns: an action items panel (prioritized list of things to fix, each linked to the resolution) and a live activity feed (recent orders, leads, form submissions, chat conversations with timestamps).
Revenue by product in a sortable table: product name, total revenue, order count, view count, and conversion rate. 7-day revenue trend sparkline. Average order value. Revenue data comes from Shopify order webhooks with source attribution, so you can see which channel (direct, AI chat, embed widget, newsletter) drove each sale.
Pulls directly from HubSpot's read API. Shows a visual funnel: subscribers, leads, opportunities, customers, with counts at each stage. Below that, a table of recent contacts with email, name, lifecycle stage, product interest, and date created. Color-coded by stage. Surfaces stale leads that haven't been contacted.
A composite health score from 0 to 100, rendered as a circular SVG gauge. Scores are computed from: image presence, image resolution, description completeness, pricing, variant configuration, and availability. Issues are categorized as critical (products at $0 with no quote form, no images), warning (missing descriptions, low-res images, no SEO titles), and informational (unavailable products, duplicate titles). Every product listed in a health table with issue flags.
Chat volume (today and 7-day), lead captures from chat, and conversion rate (percentage of conversations that result in an email capture or quote request). Top search terms ranked by frequency, so you know what customers are looking for. Tool usage breakdown: how often the AI searches products vs. creates quote requests vs. captures emails. Performance insight generated from the conversion rate with a recommendation to adjust the system prompt if conversion is low.
Table of partner domains with embed load counts. Tracks which external sites are loading your embed widget, how often, and with what parameters (region, theme). 7-day volume sparkline. Includes a copy-paste embed code reference so you can share the snippet directly from the admin. Designed for businesses with 5 to 100+ partner sites distributing their products.
Client-side event tracking via a lightweight trackEvent() function that batches events and flushes every 5 seconds (or on page unload via visibilitychange). Ten event types tracked: page views, searches, add-to-cart, checkout starts, chat messages, chat tool calls, embed loads, embed clicks, form submissions, and completed orders. Server-side event store with daily rollups, product-level stats, search term frequency, and a capped activity feed. Shopify order webhook handler that receives orders, attributes them to source, and updates the HubSpot contact to customer stage.
A persistent storage abstraction that gives Cartridge memory. Customer profiles, server-side cart state, and revenue attribution survive deploys and server restarts.
A unified key-value interface with get/set/del, atomic counters, lists, and hash maps. In development, it uses an in-memory Map. In production, it uses Vercel KV (Redis-compatible). The interface is identical. Code that works in dev works in production without changes. TTL support for session data. Capped lists for activity feeds.
Every identified visitor gets a server-side profile tracking: first seen timestamp, last seen timestamp, total orders, total lifetime spend, last 20 viewed products, last 20 carted products, purchased product history, chat session count, and acquisition source. Profiles are keyed by email (after identification via any capture point). This data powers personalized AI conversations, product recommendations, and churn detection.
Client-side carts (localStorage) are mirrored to the server with a 30-day TTL, keyed by session ID. If a visitor returns on a different device, their cart is recoverable. Cart state feeds into abandoned cart detection and pre-checkout email sequences.
Every order from Shopify webhooks is matched to its source: AI chat conversation, embed widget click, newsletter signup, popup capture, event page, or direct browse. Attribution data is stored persistently and rolled up into daily/weekly/monthly aggregations. The admin dashboard reads this data to show "Your AI concierge influenced $X in revenue this month."
CSS custom properties generated at build time from your config file. Real visual differentiation without forking the codebase or losing the upgrade path.
The theme engine reads your config and generates a complete set of design tokens: background colors, text colors (primary, secondary, muted, dim), border colors, accent color with RGB decomposition for alpha compositing, card backgrounds, input backgrounds, overlay colors. Dark and light palettes with full token sets. Injected as an inline <style> tag in the root layout.
Beyond colors, the config controls structural design decisions: font families (sans and mono), border radius (none, sm, md, lg, xl, full), card style (flat with no border, bordered with subtle ring, raised with shadow, glass with backdrop blur), button style (rounded, pill, square), hero style (gradient, image, split, minimal), product card style (contain, cover, square, portrait), header behavior (fixed, sticky, static). Each option generates different CSS custom property values. Switching from "glass" to "flat" cards changes the entire visual feel without touching a component.
The accent color from config is decomposed into RGB components at build time, enabling CSS constructions like rgba(var(--c-accent-rgb), 0.1) for subtle tinted backgrounds. This means hover states, focus rings, badges, and gradient accents all derive from a single hex value in the config. Change one line, every accent surface updates.
A dedicated cart page that maximizes conversion before the Shopify checkout redirect. Every element designed to reduce abandonment and capture data.
Not a drawer. A dedicated /cart page with product images, titles, variant labels, per-item and per-line pricing, quantity controls (increment, decrement, remove), and a "Continue shopping" link back to the collection. Items link to their product detail pages. The cart page is where serious buyers review before committing.
Sticky sidebar on desktop with: subtotal, item count, shipping estimate (free if over threshold, flat rate otherwise, both from config), tax note, and a total. Below the total: the checkout button with trust signals (lock icon, "Secure checkout", "Powered by Shopify"). Below that: shipping and returns quick info pulled from the config's shipping section.
If brand.shipping.freeAbove is set, the cart page shows a progress bar: "Add $X more for free shipping." The bar fills proportionally. When the threshold is met, the shipping estimate changes to "Free." Simple nudge that increases average order value.
Input field in the order summary that validates against the promo code in the config (brand.embed.promoCode). If the code matches, a green "Applied" badge appears. The actual discount is applied by Shopify at checkout, but the validation gives the customer confidence before the redirect.
An email input integrated into the order summary: "Enter your email for order updates." On submission, creates a HubSpot contact with lifecycle stage "opportunity" and the full cart contents in notes. Even if the customer abandons at Shopify's checkout, you have their email and know what they were about to buy.
Comprehensive automated testing with Vitest. Coverage thresholds enforced on every build. No untested module ships.
Every core library module has a dedicated test file: Store abstraction (18 tests for get/set/del, incr, lists, hashes, singleton behavior), Customer profiles (12 tests for creation, updates, views, carts, purchases, server cart), Theme engine (5 tests for CSS generation and syntax validation), Analytics server (11 tests for all 10 event types), Config validation (13 tests verifying every config section), Rate limiter (5 tests for allow/block/isolation), Recommendations (6 tests for scoring, ranking, limits), Structured data (8 tests for Product and Organization JSON-LD), System prompt (5 tests for generation and injection), and HubSpot integration (7 tests for graceful degradation when not configured).
Enforced minimums: 85% line coverage, 80% function coverage, 75% statement coverage, 65% branch coverage. Current actual coverage exceeds all thresholds. External API modules (Shopify, HubSpot live API, browser-only analytics) are excluded from coverage since they require live connections, but are tested for graceful fallback behavior.
Unit tests for logic. Integration tests for data flow. Graceful degradation tests for external services. Every module that can run without a network connection is tested in isolation. Every module that depends on an external service is tested for its "not configured" path. The test suite runs in under 700ms.
One file controls everything. Fully typed with TypeScript. IDE autocomplete for every field.
App Router, server components, static generation with ISR (2-min revalidation), streaming, dynamic routes.
Server + client components, Suspense boundaries, streaming SSR.
Strict mode. Full type coverage on Shopify API responses, config schema, and component props.
Utility-first styling with @tailwindcss/typography for product descriptions.
Page transitions, scroll animations, modal springs, accordion expand/collapse, counter tweens.
useChat hook, streamText, tool calling with Zod schemas, UIMessage streaming.
Sonnet 4 for chat. Streaming responses. Full catalog in system prompt. 3 callable tools.
Zero-auth product and collection data. No API tokens, no OAuth, no Shopify Partner account.
Contact create/update via Private App token. Create-first pattern (1 API call happy path).
Deployment target. Global edge CDN, automatic SSL, preview deployments, serverless functions.
Stop fighting Shopify templates. Get a storefront that matches the quality of what you sell.