A gamified, adaptive learning web app for school kids (grades 5–11) covering Mathematics and Science. Built with React 19 + TypeScript + Vite. All progress is stored in the browser — no backend required — with an optional, free, dependency‑free cloud sync.
- Subject → Category → Chapter series flow with lessons + practice
- Adaptive difficulty (parent‑controlled speed) and non‑repeating questions
- Single & multi‑answer questions with clear, kid‑friendly selection UI
- Parent mode (unlock code
062026): progress charts, learner settings, AI coach, and Backup & Sync - Light bundle (~150 KB gzip) — deploys on the Vercel free tier
npm install
npm run dev # http://localhost:5173
npm run build # type-check + production build to dist/
npm run preview # serve the production build locallyProgress lives in each browser's localStorage, so it is per‑device.
Deploying a new version does not erase it. To move or protect progress there
are two tools, both in Parent mode → 💾 Backup.
- Download backup writes a single
learnquest-backup-YYYY-MM-DD.jsoncontaining every learner's progress. Keep it in Drive, email, or a USB stick. - Restore loads that file on another laptop. Choose:
- Merge — keep learners already on this laptop, add/update from the file (newest copy of each learner wins, so nothing newer is lost).
- Replace — discard local learners and use only the file.
Each laptop is linked with one short code (e.g. MANGO-7421). Progress then
syncs automatically: it pulls on load and pushes ~1.5 s after each
change and when the tab is hidden. Merges are newest‑wins per learner, so
moving between laptops never loses newer progress.
The sync code is the only thing guarding a learner's data blob — fine for school progress, but don't post codes publicly.
-
Create a free project at https://supabase.com → New project.
-
In SQL Editor → New query, run:
create table if not exists public.sync_blobs ( code text primary key, data text not null, updated_at timestamptz not null default now() ); alter table public.sync_blobs enable row level security; create policy "anon read" on public.sync_blobs for select using (true); create policy "anon insert" on public.sync_blobs for insert with check (true); create policy "anon update" on public.sync_blobs for update using (true) with check (true);
-
In Project Settings → API, copy the Project URL and the anon public key (the
eyJ...one — notservice_role).
-
Per browser: Parent mode → 💾 Backup → section 3, paste the Project URL + anon key, then Save cloud settings.
-
For everyone (recommended for a shared deploy): set environment variables and redeploy (see below). The UI then skips the paste step automatically.
Variable Value VITE_SUPABASE_URLhttps://xxxx.supabase.coVITE_SUPABASE_ANON_KEYthe eyJ...anon public key
- Laptop A: Backup → ✨ Create a new sync code → write down the code.
- Laptop B: Backup → enter the code under Connect with a code.
- From then on both laptops auto‑sync. Use Stop auto‑sync to unlink a laptop (local progress is kept).
The Supabase anon public key is designed to be shipped in client apps, so exposing it in the bundle or env vars is expected and safe.
This is a static Vite app, so deployment is simple.
- Push the repo to GitHub and Import it in Vercel (framework preset: Vite).
- Build command:
npm run build - Output directory:
dist
- Build command:
- (Optional, for shared cloud sync) add the two
VITE_SUPABASE_*env vars under Project → Settings → Environment Variables, then Redeploy so they are baked into the build. - Every push redeploys. This does not clear users' saved progress — that lives in each visitor's browser.
vercel.json is included for SPA routing.
src/
components/ UI screens (Dashboard, GameScreen, ParentArea, BackupPanel, …)
curriculum/ Question/lesson content by grade & subject + registry (index.ts)
data/ store.ts (localStorage + backup), cloudSync.ts (sync code)
engine/ adaptive.ts (difficulty, XP, progress)
ai/ optional AI coach hooks
types.ts shared data model
- Grades: 5, 6, 7, 8, 9, 10, 11.
Gradeunion +GRADESarray live insrc/types.ts; the dashboard grade picker readsGRADES, so adding a grade is a one-line change plus curriculum content. - Subjects: Mathematics (
math, 🔢) and Science (science, 🔬), defined inSUBJECTS(src/curriculum/index.ts). - Content flow: Subject → Category (e.g. "Algebra · Grade 9") → Chapter
(a lesson + a practice round). Lessons render in
LessonScreen, practice inGameScreen. - Question types: single-answer and multi-answer (
multi: true,answers: string[]). Multi questions require the selected set to exactly matchanswers. Generators are parameterized so questions don't repeat within a session (anti-repeat tracking inGameScreen). - Answer UX: selection is amber before submit; only after submitting do correct answers turn green and wrong picks red. On-screen instructions, per-choice tooltips, ☐/☑/○/◉ marks, and a "Select all that apply" badge for multi questions.
- Adaptive difficulty:
engine/adaptive.tsraises/lowers difficulty on streaks; parent-set speed (gentle/normal/fast) changes the streak threshold (4/3/2). - Gamification: XP, levels, day-streak, badges, totals (
GameState). - Parent mode: unlocked with code
062026(ParentGate.tsx). Tabs: 📊 Progress (per-topic charts), ⚙️ Learners (add/edit learners, grade & speed), 🤖 AI Coach, 💾 Backup (file backup + sync code). - Optional AI Coach: plug in any OpenAI-compatible API key (stored only in the browser) for richer hints/feedback; falls back to built-in hints otherwise.
| Key | Holds |
|---|---|
learnquest.children.v1 |
all learner states (Record<id, ChildState>) |
learnquest.activeChild.v1 |
id of the currently selected learner |
learnquest.sb.url / learnquest.sb.anon |
Supabase config (if pasted in UI) |
learnquest.syncCode |
this laptop's sync code (if connected) |
ChildState={ profile, settings, game, subjects, lastSavedAt }.lastSavedAtpowers newest-wins merging for backup/cloud restore.store.tsmigrate()upgrades older saved profiles (e.g. backfillsgradeandsettings.adaptiveSpeed) so existing users never lose progress on update.save()emits alearnquest:changedevent; cloud sync listens to it to push.
Content is split into registry files in src/curriculum/, all spread into
CATEGORIES in index.ts:
| File | Exports | Chapter id prefix |
|---|---|---|
math.ts |
MATH_CATEGORIES |
m5- m8- m10- |
math_more.ts |
MATH_MORE |
mx- |
math_grades.ts |
MATH_GRADES (grades 6/7/9/11) |
mg- |
science.ts |
SCIENCE_CATEGORIES |
s5- s8- s10- |
science_more.ts |
SCIENCE_MORE |
sx- |
science_grades.ts |
SCIENCE_GRADES (grades 6/7/9/11) |
sg- |
To add content:
- Add
Categoryobjects (withchapters) to the relevant file, using generator helpers fromcurriculum/util.ts:build(...)— single-answer multiple choicenumericMC(...)— numeric single-answerbuildMulti(...)— multi-answer (correct answers must never appear indistractors)
- Keep chapter ids globally unique with the prefix above —
getChapter()resolves chapters by scanning all categories. - If a new file is created, import it and spread it into
CATEGORIESinindex.ts. - Verify:
npx tsc --noEmitthennpm run build. (Type-check validates every file; a quick smoke loop overCATEGORIEScan assert each answer ∈ choices and that multi answers ⊆ choices and are disjoint from distractors.)
- R1–R2: grade-based restructure; curriculum split; adaptive engine; parent Learners tab; Vercel config.
- R3–R4: expanded curriculum (
*_more.ts); fixed select-then-submit & question repetition; dynamic round size; mistake correction; grade dropdown; parent gate changed to unlock code062026. - R5: grades 6/7/9/11; multi-answer questions + amber/green-red answer UX + on-screen selection instructions.
- R6: progress saving — Backup file (export/import, newest-wins merge) and sync code cloud sync (dependency-free Supabase REST); README + GitHub.
- More subjects (the original vision: English, languages, Computer Science,
Robotics, Health Sciences) — each is a new
Subject+ curriculum files. - Account-based sync (Option 3) on top of the same Supabase backend.
- Spaced-repetition revision scheduling; printable progress reports.
- Per-chapter target dates surfaced as reminders.