A physical/digital trading-card-pack collectibles platform — built as a Next.js 16 storefront on top of a Medusa v2 + Mercur marketplace backend.
The experience: pack drops, slot-machine pack opening, a vault, and a leaderboard, in a clean, full-bleed, dark-mode codebase.
- Storefront (
src/) — Next.js 16 (App Router, React 19, TypeScript strict), ~30 routes including the home page,/slots(slot-machine pack opening),/how-it-works,/leaderboard(Ranks — standings plus the Weekly Pulled Value Challenge), and an account area (vault, orders, transactions, settings, referrals). - Backend (
backend/) — a Medusa v2 + Mercur (multi-vendor) commerce API atbackend/packages/api, plus an admin dashboard atbackend/apps/admin. - Credit economy — top-up, per-customer credit charging, public profiles, a client-side demo spin, forgot-password, a card vault, two-tier buyback, stock-aware pack pulls, and a DB-aggregated leaderboard.
| Layer | Choice |
|---|---|
| Storefront | Next.js 16 · React 19 · TypeScript (strict) |
| Styling | Tailwind CSS v4 · hardcoded dark neutrals |
| UI primitives | shadcn-style components (Tailwind-only, src/components/ui) · Lucide icons |
| Animation | Framer Motion (motion) + a custom scroll-reveal engine (src/lib/use-reveal.ts) |
| Backend | Medusa v2 + Mercur (multi-vendor marketplace) |
| Data | PostgreSQL · Redis (via Docker) |
| Deploy | DigitalOcean App Platform + Spaces (media) |
Fonts: Nekst Black (self-hosted) for headings, Geist for body. The site is full-bleed by design — no max-w-* caps; the .px-fluid gutter scales padding continuously from mobile to 4K.
- Node.js 24+
- Docker (Postgres + Redis for the backend)
corepack(the backend uses Yarn via corepack)
npm install
npm run dev # http://localhost:3000Verify against a production build, not
next dev.next devserves images slowly on some machines and makes a correct build look broken.next.config.tssetsoutput: 'standalone', which makesnpx next startunusable — serve the standalone bundle instead:npm run build pwsh scripts/serve-standalone.ps1 -Port 4000
First time on a machine, create the shared dev containers (Postgres 16 with
user/password/db all medusa, Redis 7):
docker run -d --name pokenic-postgres --restart unless-stopped \
-e POSTGRES_USER=medusa -e POSTGRES_PASSWORD=medusa -e POSTGRES_DB=medusa \
-p 5432:5432 postgres:16
docker run -d --name pokenic-redis --restart unless-stopped -p 6379:6379 redis:7Thereafter they restart themselves (--restart unless-stopped); a plain
docker start pokenic-postgres pokenic-redis also works.
# Postgres + Redis stay up via Docker (--restart unless-stopped)
cd backend/packages/api && corepack yarn dev # Medusa API on :9000 (health: /health)
cd backend/apps/admin && node ../../node_modules/vite/bin/vite.js # Admin on :7000To run the backend integration tests locally (including the money-loop smoke
subset), see backend/packages/api/README.md.
npm run dev # Start dev server
npm run build # Production build
npm run start # next start (prefer serve-standalone.ps1 — see above)
npm run lint # ESLint
npm run typecheck # tsc --noEmit
npm run check # lint + typecheck + build
npm run format # Prettier write (src, scripts)
npm run test # Vitestdocker compose up app --build # production build
docker compose up dev --build # dev mode on port 3001- Section composition + scroll-in animation is the core pattern.
src/app/page.tsxstacks section components, wrapping most in<Reveal>(fade-up on scroll-into-view). The engine —useInView+usePrefersReducedMotion(src/lib/use-reveal.ts) — honorsprefers-reduced-motioneverywhere. Sections with their own internal scroll animation (HowItWorksSection,LeaderboardSection) are intentionally not double-wrapped. - Server/client split. Route
page.tsxfiles stay server components and exportmetadata; interactivity moves to a sibling'use client'component (canonical example:slots/[slug]/page.tsx→slots/[slug]/PackDetailClient.tsx). - Global shell.
src/app/layout.tsxforces dark mode and wraps every page inSiteHeader+SiteFooter. The palette is hardcoded Tailwind neutrals, not the shadcn oklch tokens.
UI work is measurement-driven, not eyeballed. The scripts/*.mjs Playwright scripts read computed styles and getBoundingClientRect, dumping screenshots and JSON into docs/research/. Per-component specs (exact computed CSS, states, content, responsive breakpoints) live in docs/research/components/*.spec.md. Verify with the Playwright scripts (screenshots → docs/research/*.png), not ad-hoc browser sessions. (docs/research/ is a gitignored, local-only output dir — it is not shipped in a clone; regenerate it by running the scripts.)
src/
app/ # Next.js routes (~36)
components/
ui/ # shadcn-style primitives (Tailwind-only)
lib/ # cn(), use-reveal.ts, utilities
hooks/ # custom React hooks
public/
fonts/ images/ videos/ seo/
backend/
packages/api/ # Medusa v2 + Mercur commerce API
apps/admin/ # Admin dashboard (Vite)
docs/
superpowers/ # tracked plans + specs
research/ # LOCAL-ONLY (gitignored) — measurement output, component specs
scripts/ # Playwright capture/measure/QA + serve-standalone.ps1
# AI-agent config (AGENTS.md, CLAUDE.md) is local-only — gitignored; see .gitignore "Private".
Deploys to DigitalOcean App Platform (managed via the doctl CLI). Both the storefront and backend build from git master with deploy_on_push, so code changes ship via commit + push. Media is served from a DigitalOcean Spaces bucket + CDN. See .do/README.md for the deploy specs and operational details.
MIT — see LICENSE.