Skip to content

coells/careen

Repository files navigation

Careen

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.

Quick start

pnpm install
pnpm dev

The dev server runs at http://localhost:5173 with HMR.

Features

  • 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.

Tech stack

  • 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).

Architecture overview

  • src/routes/+layout.svelte: global shell (header/nav, locale toggle) that calls session.initialize() inside onMount before rendering children.
  • src/routes/+page.svelte: dashboard composer (hero, resume prompt, stats, featured grid) sourced from src/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 through src/lib/ui/quiz/Hero and Overview (which compose Actions + 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: clears localStorage on mount and routes home.
  • src/lib/services/session.ts: writable sessionStore plus helper API (set/update/clear in-progress, appendAttempt, getRecentAttempts*, updatePreferences).
  • src/lib/services/storage.ts: SSR-safe loadState/saveState helpers with debounced localStorage writes keyed by careen.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.

Data model & persistence

  • 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 key careen.v1.
  • Attempt IDs rely on crypto.randomUUID() with ISO timestamps for startedAt/completedAt.

Core flows

  • 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.

Internationalization (i18n)

  • Supported locales: en (default) and cs, defined in SUPPORTED_LOCALES within src/lib/i18n/index.ts.
  • Locale toggle lives in the layout header; calling toggleActiveLocale(next?) writes to session.updatePreferences so the choice persists per browser.
  • UI strings use the reactive $t() helper from $lib/i18n. English source strings double as keys; Czech translations live in localeRegistry.cs (see src/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.

Adding a new quiz

  1. Create src/lib/database/<category>/<quiz_id>.ts exporting a named Quiz constant (no side effects).
  2. Import and register it within the sorted collection array in src/lib/services/database.ts so helper selectors pick it up.
  3. Set isPublished (catalog visibility) and isFeatured (dashboard placement) as needed.
  4. Provide content.en plus optional content.cs; the UI will fall back to English when a localized template is missing.
  5. Keep IDs unique—the build step currently relies on deterministic ordering.

Development

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

Quality gates & manual QA

  • Lint/typecheck: pnpm lint must pass before committing.
  • Format: pnpm format to 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 /reset to ensure progress clears when expected.

Careen • Explore, wonder, discover ✨

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages