The club's digital handbook: an installable, animated, production-grade site for the Rotaract Club of Metro City (sponsored by the Rotary Club of Kathmandu Metro), currently serving Rotary Year 2026–27 under President Rtr. Rohan Raj Gautam.
Service Above Self · Connect, Grow, Give
Architected as a tenure-aware, config-driven boilerplate: the yearly board rotation is a data change, and re-skinning the repo for another club is a config change. See the docs suite:
| Doc | What it covers |
|---|---|
docs/PRD.md |
Product vision, personas, requirements (R1–R22), success metrics |
docs/ARCHITECTURE.md |
Layer map, config/data contracts, rendering model, fork runbook |
docs/DESIGN-SYSTEM.md |
Brand tokens, type, motion contract, component inventory |
docs/PWA.md |
Manifest, service worker strategy, icons, testing checklist |
docs/TENURE-HANDOVER.md |
The ≤30-minute yearly rotation checklist |
docs/ROADMAP.md |
White-label boilerplate → minutes digitization → subscription product |
| Concern | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) · React 19 · TypeScript |
| Styling | Tailwind CSS v4 + CSS variables (design tokens) |
| Motion | CSS-first entrances/reveals; Motion + GSAP as enhancement |
| Sliders/lightbox | Swiper · yet-another-react-lightbox |
| Forms | React Hook Form + Zod |
| PWA | app/manifest.ts + hand-written public/sw.js |
| Analytics | GA4, gated behind NEXT_PUBLIC_GA_ID |
| Deploy | Vercel (all pages static; APIs serverless; weekly IG cron) |
⚠️ This repo bundles a custom Next.js 16.2.7 whose APIs differ from public Next.js — readnode_modules/next/dist/docs/before writing code (seeAGENTS.md). Notable:next/imageusespreload(notpriority);themeColorlives in theviewportexport.
npm install
cp .env.example .env.local # optional env (analytics, Instagram, importer)
npm run dev # http://localhost:3000npm run build # production build (all pages prerendered)
npm run start # serve production build (needed to test the PWA)
npm run lint # ESLint
npm run import:projects # regenerate src/data/projects.ts from the district portal
python3 scripts/generate-icons.py # regenerate public/icons/ from the logosrc/config/club.config.ts ← identity, TENURES (president/year), feature flags
src/data/ ← rosters (per tenure), sponsors (+tier benefits),
projects (generated), testimonials, site copy
src/app/globals.css ← @theme brand tokens (single re-brand point)
src/app/ ← routes, metadata, manifest.ts, sitemap, APIs
src/components/ ← ui / sections / sliders / forms / gallery / pwa
public/sw.js ← service worker (docs/PWA.md)
scripts/import-projects/ ← district-portal → projects.ts pipeline
The golden rule: components never hardcode club facts. If a fact needs
changing, it lives in club.config.ts, src/data/*, or the @theme block.
- ✅ Tenure, president, board structure, past-presidents timeline (vacant
seats render as "To be announced" — fill via
docs/TENURE-HANDOVER.md§2) - ✅ Projects 2023-24 → 2025-26 imported from the district portal
- ✅ Live Instagram gallery (token in KV, weekly refresh cron)
⚠️ Sponsors beyond the sponsoring Rotary club are[PLACEHOLDER]stubs⚠️ Testimonials are placeholders; home VideoSection parked until a real club video exists- ✅ Contact/membership submissions are emailed via Gmail SMTP (see
src/lib/email.ts+.env.example); falls back to server-log-only whenGMAIL_USER/GMAIL_APP_PASSWORDaren't configured
Semantic landmarks, skip link, focus rings, keyboard-complete controls.
Content is never gated on JavaScript (CSS-first entrances, observer-based
reveals), and prefers-reduced-motion collapses all animation to final
states. Keep both invariants when adding sections.
Vercel auto-detects Next.js. Set env vars from .env.example as needed
(NEXT_PUBLIC_GA_ID, INSTAGRAM_ACCESS_TOKEN, CRON_SECRET, KV creds).
vercel.json schedules the weekly Instagram token refresh.
The site canonicalises to https://www.rotaractmetrocity.org, defined once as
siteUrl in src/config/club.config.ts and consumed by metadataBase,
canonicals, sitemap.ts, robots.ts, llms.txt, and the JSON-LD graph. If the
domain changes, edit siteUrl — nothing else. NEXT_PUBLIC_SITE_URL overrides
it at build time (useful for preview deploys). The previous domain
rcmetrocity.org.np is 301'd path-for-path by redirects() in
next.config.ts; keep it attached to the deployment for that to fire, and set
www.rotaractmetrocity.org as the host's primary domain so the apex
redirects to www.