Nigeria-first multi-brand gadget storefront — phones, laptops, tablets, audio, and accessories with gallery-style product presentation.
Features · Quick start · Scripts · Project structure · Catalog imports · Deploy
A React + Vite storefront for browsing and buying gadgets in Nigeria. Prices are shown in NGN, catalog data is centralized (no hardcoded product values in components), and the UI follows a product-as-sculpture gallery approach: calm surfaces, progressive disclosure, and deliberate commerce flows.
The design system and implementation rules live in design.md and the existing product README conventions in src/config.js.
- Shop experience — home hero, category rails, campaign banners, product shelves, search, filters, and product detail pages
- Cart & checkout — bag drawer, multi-step demo checkout (delivery → payment → review), order confirmation (no real payments)
- Nigeria-first catalog — NGN pricing, nationwide delivery copy, brand-new / UK used / refurbished conditions
- Catalog tooling — import phones from DummyJSON (demo) or MobileAPI.dev; marketplace fixture import scripts
- Design system — Poppins only, light/dark themes, Squircle shapes, Hugeicons, Motion for state-driven animation
- SPA routing — React Router with Vercel rewrite support for client-side routes
| Layer | Choice |
|---|---|
| UI | React 19, React Router 7 |
| Build | Vite 8 |
| Styling | Tailwind CSS 4 |
| Motion | Motion (motion/react) |
| Icons | Hugeicons |
| Shapes | Squircle.js |
| Hosting | Vercel (vercel.json SPA rewrites) |
Requirements: Node.js 20+ recommended, npm.
git clone https://github.com/jvchi/gadget-store.git
cd gadget-store
npm install
npm run devOpen the URL Vite prints (usually http://localhost:5173).
npm run build
npm run preview| Command | Description |
|---|---|
npm run dev |
Start the Vite dev server |
npm run build |
Production build to dist/ |
npm run preview |
Serve the production build locally |
npm run lint |
Run ESLint |
npm run catalog:phones |
Import phones (uses .env.local if present; MobileAPI when configured) |
npm run catalog:phones:demo |
Import demo phones from DummyJSON (no API key) |
Copy .env.example when you want live phone catalog imports:
# Optional: use MobileAPI.dev instead of DummyJSON fixtures
MOBILEAPI_KEY=
PHONE_CATALOG_SOURCE=mobileapi
# Keep imports small enough for local UI testing
PHONE_IMPORT_LIMIT=16
PHONE_IMAGE_LIMIT=8Note
Checkout is a demo flow. No card data is sent to a payment processor. Bag and orders are stored in localStorage keys defined in src/config.js.
gadget-store/
├── public/assets/ # Product images, logos, campaign art, fonts
├── scripts/
│ ├── import-phones.mjs # Phone catalog importer
│ └── import-marketplace.mjs
├── src/
│ ├── App.jsx # Routes and UI surfaces
│ ├── config.js # Catalog, copy, layout, shape tokens
│ ├── format.js # NGN formatting helpers
│ ├── generated/ # Imported catalog modules
│ ├── main.jsx
│ └── styles.css
├── design.md # Visual / interaction design reference
├── vercel.json # SPA rewrites
└── vite.config.js
- No hardcoding in components — colors, copy, prices, product data, and motion values live in tokens or
src/config.js - Products are the subject — interface chrome stays quiet until the user asks for search, filters, cart, or configuration
- Progressive disclosure — search, categories, and cart appear on demand; add-to-bag happens from product detail
npm run catalog:phones:demoWrites assets under public/assets/catalog/imported-phones/ and generated data to src/generated/phones.js.
- Set
MOBILEAPI_KEYandPHONE_CATALOG_SOURCE=mobileapi(see.env.example). - Run
npm run catalog:phones.
Marketplace fixtures are imported via scripts/import-marketplace.mjs and merged into the catalog from src/generated/marketplace.js.
The app is a static SPA. Vercel is configured with a catch-all rewrite to index.html:
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}Typical flow:
npm run build
# deploy dist/ (or connect the GitHub repo to Vercel)For tokens, typography, motion rules, and non-negotiable UI principles, see design.md. Logo assets are under public/assets/logo/.