Riquid is a multi-module product suite that covers the commerce lifecycle end to end — selling, fulfilment, tracking and marketing. It is built for a B2B commerce operation that runs its own storefront while also managing logistics and reporting internally. Rather than one monolithic application, the suite is three separate front-end codebases — a React + Vite operations core, a Next.js storefront, and a Next.js business site — that share a common design system.
Problem
A commerce operation of any real size outgrows a single storefront quickly. Selling is only one surface: orders have to be fulfilled, shipments have to be located and tracked, sales have to be measured, and the business needs a public face that speaks the same visual language as the product. The obvious path is to bolt each of these onto whatever codebase already exists, and the predictable result is a single application carrying a checkout flow, a Mapbox integration, a charting library and a marketing landing page all in one bundle — every surface competing for the same release train. The alternative failure mode is to split them and let each drift, so that a customer moving from the marketing site to the storefront to a tracking view feels three unrelated products. The problem was to get the separation without the drift: distinct apps with distinct release cadences that still read as one system.
System Design
The suite is three deployable front ends. The operations core (riquid-website) is a React 18 application on Vite 4, routed with React Router v6. It carries the two integrations that would be dead weight in a storefront bundle: Mapbox GL — wrapped by react-map-gl — for location and logistics views, and Chart.js via react-chartjs-2 for sales analytics. Motion and presentation are handled with Framer Motion, AOS and Swiper, with react-intersection-observer driving scroll-triggered reveals and react-countup animating figures. Tailwind CSS with PostCSS and Autoprefixer supplies the styling layer, and ESLint runs with --max-warnings 0, so the lint gate is a hard gate rather than advisory.
The storefront (riquid-store) is a Next.js 14 App Router project in TypeScript, deployed to Vercel via a checked-in vercel.json and a .env.production. Cart and client state live in Zustand v5 — a store rather than prop-drilled context, which keeps the cart addressable from anywhere in the tree without a provider cascade. Payments run through Stripe's React bindings, @stripe/react-stripe-js over @stripe/stripe-js, so card entry happens inside Stripe-hosted Elements iframes and raw card data never touches application state. Server data is fetched with axios and cached and revalidated with SWR, giving stale-while-revalidate behaviour on catalogue and order reads. The component layer is shadcn/ui on top of @base-ui/react, with class-variance-authority, clsx and tailwind-merge composing variant-driven class names, lucide-react for iconography, and Framer Motion v12 for transitions. It is the only module with a test harness: Vitest with jsdom, Testing Library (react, jest-dom, user-event) and a vitest.setup.ts — the module that handles money is the module that gets tests.
The business site (riquid-business) is a deliberately lean Next.js 14 TypeScript app, also on Vercel, running on port 4601 in development so it can sit alongside the storefront locally without a port clash. It shares the storefront's styling and utility vocabulary — Tailwind, tailwindcss-animate, clsx, tailwind-merge, lucide-react, Zustand, axios — but carries none of its payment, testing or mapping weight. That shared vocabulary is what holds the suite together: three codebases converging on the same Tailwind configuration, the same class-composition helpers and the same icon set, so a component written for one surface is legible on any other. Nothing forces the modules into lockstep; each has its own package.json, its own lint config, and its own deploy.
A shared commerce core serves several module frontends — storefront, business, operations and logistics — over one API and data tier.
High-level architecture — abstracted for confidentiality; no internal endpoints, hostnames, or credentials are shown.
How It Works
01/03
01
Storefront Checkout
A customer moves from catalogue browse to a Stripe-confirmed payment without card data ever entering application state.
1The catalogue renders from the Next.js App Router, with product and inventory reads fetched over axios and cached by SWR.
2Adding an item dispatches to the Zustand cart store, which holds line items and quantities as client state readable from any component.
3The cart view derives totals from that store and reflects changes immediately, with no round trip for quantity edits.
4Checkout mounts Stripe Elements through @stripe/react-stripe-js, so card fields render inside Stripe-controlled iframes.
5The payment is confirmed against Stripe using the publishable key from the production environment file; card details never enter the Zustand store or the application bundle.
6On confirmation, the cart store is cleared and the order surface re-fetches through SWR to pick up the newly created order.
02
Logistics and Analytics in the Operations Core
The React + Vite core resolves a route to either a Mapbox-backed logistics view or a Chart.js reporting surface over the same sales data.
1The operations core loads under React Router, resolving the requested route to a logistics or analytics view.
2Logistics views mount react-map-gl over Mapbox GL, initialising a map with the token supplied through the app's environment configuration.
3Location and shipment data is projected onto the map as markers and layers, giving positional context for fulfilment and tracking.
4Analytics views feed the same underlying sales data into react-chartjs-2, which renders Chart.js canvases for the reporting surfaces.
5react-intersection-observer triggers reveals and react-countup animates summary figures as each panel scrolls into view.
03
Independent Module Deployment
Each of the three front ends builds, lints and deploys on its own cadence while converging on a shared design vocabulary.
1Each module is its own repository with its own package.json, lockfile and lint configuration.
2A change to the storefront runs next lint and the Vitest suite; a change to the operations core runs ESLint under --max-warnings 0, which fails the build on any warning.
3The storefront and business site build with next build and deploy to Vercel under their own vercel.json; the operations core builds with vite build.
4Because the shared design vocabulary lives in the Tailwind configuration and the class-composition utilities rather than in a runtime dependency, one module can deploy without rebuilding or redeploying the others.
Key Features
Next.js storefront with Stripe checkout
Zustand-powered cart & state
Mapbox logistics & location tracking
Chart.js sales analytics
Shared design system across modules
Marketing & business landing site
Outcomes
A cohesive multi-app suite instead of disconnected tools
Independent deploys per module
Consistent brand across every surface
Gallery
Click any image to open it full-size — arrow keys move between shots.