Skip to content
DevVaradPatilPublic

About

Free interactive nutrition courses in English and Hindi: 3 courses, 36 story-style lessons for kids through adults.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

NutriStep

Free, interactive nutrition courses in English and Hindi — 3 courses, 36 lessons, for kids through adults.

Live: nutristep.vercel.app

Each lesson is a short, sequential story rather than a quiz or a game:

Hook → What it is → How it works → Worked example
     → Try-it activity (gated) → Quiz (gated) → Recap + celebration

The interactions live inside the lesson, not instead of it. You can't skip the hands-on step — the Next button stays locked until the activity is actually completed.

Course Audience Lessons
Nutrition Basics Everyone 18
Energy Balance Teens and up 9
Weight Management Adults 9

What makes it fun

Around the lessons sits a Duolingo-inspired layer that rewards effort and never punishes a mistake — no hearts, no timers, no leaderboards.

  • Nori, the mascot: the logo apple come alive, in seven moods (happy, cheer, think, oops, proud, sleepy, idle). Pure SVG with hand-written CSS keyframes — no animation library, no JS, and it holds still under prefers-reduced-motion.
  • XP, levels and a daily goal. +10 for a new lesson, +5 bonus for a perfect quiz, +5 for a replay, points for review mode, +10 for opening a unit's chest.
  • Streaks with a shield. Learn something each day to keep the flame lit; one missed day a week is forgiven, so a busy day doesn't wipe out a habit.
  • Feedback that feels alive. Answer sheets slide up with Nori's reaction and a combo counter; progress segments shine as they fill; finishing a lesson plays a staged celebration — stats counting up, the streak ticking, then any level-up or new badge.
  • A winding path per course: unit banners, round lesson stops, stars for perfect lessons, Nori at "you are here", and a treasure chest closing each unit.
  • Sound and vibration, synthesised in the browser with the Web Audio API (no audio files), behind one mute switch in the header.
  • Badges with tiers. Course and unit milestones, plus bronze / silver / gold challenges for streaks, perfect lessons, reviews and XP, each with a progress bar.
  • Nori's wardrobe. Outfits (sweatband, smart glasses, party hat, chef's hat, crown) unlock by level and badges; the one you pick shows on every Nori in the app.

Why it's built this way

Lesson content is typed TypeScript, not JSON. Every English lesson module is satisfies LessonMap, and every Hindi module is satisfies typeof <english module>. A translation cannot drop a key, rename a card id or mistype a field without failing the build — which is the difference between a missing translation being a compile error and being a blank card in production. Locale JSON holds UI strings only (~8 KB instead of ~105 KB).

Progress is local-first. It lives in localStorage behind a useSyncExternalStore store and works with no account at all. Signing in adds a Firestore sink on top; it never becomes a prerequisite. Firebase itself is loaded through await import() only once a session actually needs it, which keeps ~650 KB off the landing page for the majority of visitors who never sign in.

Badges are derived, not stored. evaluateBadges(facts) is a pure function of saved progress (lessons done, perfect lessons, best-ever streak, reviews, XP), so there is no second source of truth to fall out of sync — and a streak badge stays earned after the streak ends, because "best streak" is computed from history.

XP is a ledger; streaks and levels are maths. The store saves only XP per local day (plus perfect lessons and opened chests). Totals, levels, the streak and its weekly shield are pure functions in lib/xp.ts, unit-tested day by day — and two devices merge cleanly (union of lessons, per-day max of XP).

Everything is static. 173 prerendered routes, zero dynamic routes — including the legacy redirects. The whole site is a CDN artifact.

No raster or audio assets. Every illustration — foods, icons, badges and the mascot — is hand-built inline SVG, and every sound is synthesised at runtime, so the art is themeable and scalable and costs no image or audio requests.


Stack

Next.js 16 (App Router, Turbopack) · TypeScript · Tailwind CSS 4 · Framer Motion · Firebase Auth + Firestore (optional) · Vitest

A few Next.js 16 specifics worth knowing if you're reading the source: routing middleware is proxy.ts (not middleware.ts), params and searchParams are Promises and must be awaited, and directional page animations use the native <ViewTransition> wrapper with transitionTypes on <Link>.


Project structure

app/[lang]/                    # locale-scoped routes (en, hi)
  page.tsx                     #   course library (landing)
  learn/[course]/              #   course roadmap
    [module]/[chapter]/        #   the lesson itself
  dictionaries/{en,hi}.json    #   UI strings only
components/
  story/                       # the lesson engine: slides, quiz, answer sheet,
                               #   end-of-lesson celebration, gating
  chapters/                    # per-lesson assembly + try-it activities
  mascot/Nori.tsx              # the mascot (motion lives in globals.css)
  landing/                     # library, Today panel, winding course path
  badges/  icons/  ui/         # trophy shelf, SVG icons, header chips, shell
lib/
  content/{en,hi}/*.ts         # lesson text as typed modules
  curriculum.ts                # courses → modules → chapters (source of truth)
  progress.tsx  badges.ts      # local-first store, derived badges
  xp.ts  sfx.ts                # XP/level/streak maths, synthesised sound
tests/                         # curriculum + content invariants, review picker,
                               #   streak/level maths, progress migration + merge

Adding a lesson means adding it to the right course module in lib/content/en/, mirroring it in lib/content/hi/, then wiring it in lib/curriculum.ts and the router switch. The compiler reports anything you missed.


Running locally

npm install
cp .env.example .env.local   # optional — see below
npm run dev

Open http://localhost:3000.

The .env.local step is optional. Omit the Firebase block entirely and the app still works — every lesson, badge and progress feature runs on localStorage; you just don't get accounts or cross-device sync. Fill it in to enable those. NEXT_PUBLIC_SITE_URL only affects sitemap.xml, robots.txt and canonical URLs, and is detected automatically on Vercel.

Scripts

npm run dev     # dev server (Turbopack)
npm run build   # production build + type check
npm run lint    # ESLint
npm test        # Vitest — curriculum, content, badges, XP/streaks, progress

Docs

A note on the content

NutriStep is general health education, not medical advice. The adult weight-management course says so on its first lesson, and each course cites the WHO / NHS material it's based on at the bottom of its roadmap.

About

Free interactive nutrition courses in English and Hindi: 3 courses, 36 story-style lessons for kids through adults.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages