Friendly, self-paced quizzes powered by SvelteKit 2 and Svelte 5 runes. Careen ships as a static single-page app with no backend, bundles all quiz content at build time, and saves learner progress locally so users can resume anytime without signing in.
Use this README as the top-level guide. Deeper implementation notes live in:
.github/instructions/copilot-instructions.md— engineering conventions, architecture notes, and expectations for contributors..github/instructions/localization.instructions.md— end-to-end i18n workflow for UI copy and quiz templates.
pnpm install
pnpm devThe dev server runs at http://localhost:5173 with HMR.
- Zero-backend SPA: everything is prerendered and hosted statically.
- Resume where you left off: in-progress answers and attempts restore automatically per browser.
- Rich summaries: per-question explanations, score goals, and retake shortcuts.
- Searchable, filterable quiz catalog with estimated time per section.
- English/Czech UI toggle backed by a reactive translation store.
- Built-in reset utility (
/reset/) for quick demos or support sessions.
- Framework: SvelteKit ^2.48, Svelte ^5.42 runes (
$state,$derived,$effect). - Build: Vite ^7, Tailwind CSS ^4 via
@tailwindcss/vite, TypeScript ^5.9. - Quality: ESLint 9 (flat), Prettier 3 (Tailwind plugin),
svelte-check. - Package manager: pnpm (workspace-ready via
pnpm-workspace.yaml).
src/routes/+layout.svelte: global shell (header/nav, locale toggle) that callssession.initialize()insideonMountbefore rendering children.src/routes/+page.svelte: dashboard composer (hero, resume prompt, stats, featured grid) sourced fromsrc/lib/ui/dashboard.src/routes/quizzes/+page.svelte: catalog search + filters + grouped listings with derived stats.src/routes/quiz/[id]/+page.svelte: briefing card, instructions list, resume summary, and start/resume/reset CTAs wired throughsrc/lib/ui/quiz/HeroandOverview(which composeActions+Instructions).src/routes/quiz/[id]/take/+page.svelte: full quiz runner with navigator, confirmation flow, and submission pipeline.src/routes/quiz/[id]/result/[attemptId]/+page.svelte: attempt summary, performance progress bars, and question review.src/routes/results/+page.svelte: historical attempts list with stats, retake/view actions, and empty state.src/routes/reset/+page.svelte: clearslocalStorageon mount and routes home.src/lib/services/session.ts: writablesessionStoreplus helper API (set/update/clear in-progress,appendAttempt,getRecentAttempts*,updatePreferences).src/lib/services/storage.ts: SSR-safeloadState/saveStatehelpers with debouncedlocalStoragewrites keyed bycareen.v1.src/lib/services/database.ts: canonical quiz collection + selectors (getQuizById,getPublishedQuizzes,getFeaturedQuizzes).src/lib/ui/**: component libraries per route (dashboard, quizzes, quiz, quiz-take, quiz-result, results, shared icons/components). Use the finished dashboard set (Hero,ResumeLastQuiz,RecommendNextQuiz,FeaturedQuizzes) and quiz overview stack (Hero,Overview,Actions,Instructions) as templates when introducing new UI sections.src/lib/types.ts: quiz models plus session-state typings shared throughout the app.
Quiz(src/lib/types.ts):{ id, categories, isPublished, isFeatured, passingScore, content }.Template:{ languageCode, title, description, questions }.Question:{ title?, text, explanation?, options[], correctAnswer }.CareenState:{ version, attempts: Record<quizId, AttemptRecord[]>, inProgress: Record<quizId, InProgressQuizState>, preferences }.- Preferences currently store
{ catalogFilter, locale }and drive derived stores for UI binding. - Persistence flows through
session.initialize()→loadState(defaultState)→sessionStore.subscribe(saveState). Writes are debounced (500 ms) and scoped to the browser via keycareen.v1. - Attempt IDs rely on
crypto.randomUUID()with ISO timestamps forstartedAt/completedAt.
- Dashboard: hero message, resume call-to-action (last in-progress quiz), compact stats, and featured quiz tiles.
- Catalog (
/quizzes): search field + category pills, derived stats (quiz count, categories, questions), and grouped card sections with estimated time (Math.ceil(questionCount * 0.8)). - Briefing (
/quiz/[id]): breadcrumb, hero metadata, instructions, pass goals, resume summary, and start/resume/start-over actions. - Take quiz (
/quiz/[id]/take): session banner, sidebar navigator, question panel, answer confirmation, submit or quit flow. - Results (
/quiz/[id]/result/[attemptId]): pass/fail card, score summary, progress copy, and question-by-question breakdown. - History (
/results): aggregate stats, attempt cards with view/retake buttons, empty state that links back to the catalog. - Reset (
/reset): clears storage for quick demo resets.
- Supported locales:
en(default) andcs, defined inSUPPORTED_LOCALESwithinsrc/lib/i18n/index.ts. - Locale toggle lives in the layout header; calling
toggleActiveLocale(next?)writes tosession.updatePreferencesso the choice persists per browser. - UI strings use the reactive
$t()helper from$lib/i18n. English source strings double as keys; Czech translations live inlocaleRegistry.cs(seesrc/lib/i18n/messages.ts). getLocaleName()returns human-readable labels for toggles/tooltips.- Quiz templates embed translations directly under
quiz.content[languageCode]. If a locale is missing, the UI falls back to English automatically.
- Create
src/lib/database/<category>/<quiz_id>.tsexporting a namedQuizconstant (no side effects). - Import and register it within the sorted
collectionarray insrc/lib/services/database.tsso helper selectors pick it up. - Set
isPublished(catalog visibility) andisFeatured(dashboard placement) as needed. - Provide
content.enplus optionalcontent.cs; the UI will fall back to English when a localized template is missing. - Keep IDs unique—the build step currently relies on deterministic ordering.
| Command | Description |
|---|---|
pnpm dev |
Run the dev server with HMR |
pnpm lint |
Prettier check + ESLint (Svelte aware) |
pnpm check |
svelte-check using tsconfig.json |
pnpm format |
Format sources with Prettier + Tailwind plugin |
pnpm build |
Production build (static adapter) into build/ |
pnpm preview |
Preview the production build locally |
- Lint/typecheck:
pnpm lintmust pass before committing. - Format:
pnpm formatto keep diffs clean. - Manual QA (no automated tests yet):
- Start, confirm, submit a quiz; verify attempts persist and summaries render.
- Refresh mid-quiz and confirm in-progress state restores.
- Toggle EN/CS across dashboard, catalog, quiz flows, and results.
- Use catalog search + category filter; confirm preference persists across reloads.
- Visit
/resetto ensure progress clears when expected.
Careen • Explore, wonder, discover ✨