MelodyMind is an engaging and competitive music trivia game where players can test their knowledge across various music genres. Whether you're a rock enthusiast, pop aficionado, or jazz expert, this game offers a thrilling experience with multiple categories and rounds.
All localized content uses English (en) as the single canonical fallback. If a specific piece of
translated data (category, question set, album/playlist metadata) is missing for the requested
language, the system transparently falls back to the English source variant. This keeps behavior
predictable, reduces redundant branching, and simplifies maintenance. See docs/data-loading.md for
architectural details.
Active UI/content languages (in routing & content collections):
en, de, es, fr, it, pt
Deprecated / removed legacy locales purged on 2025‑10‑09 to reduce repository size and build complexity:
cn, da, fi, jp, nl, ru, sv, uk
Rationale:
- Large volume of stale knowledge articles (≈1,100 markdown files) provided no current product value
- Reduced editor noise + faster grep/search ergonomics
- Avoids accidental drift of untranslated legacy content
Guidelines for (Re)Adding a Locale:
- Add locale code to the central supported locales constant (
src/constants/languages.ts). - Provide an English source template then derive translation files (see i18n generation scripts in
scripts/). - Add content collections or data (categories, knowledge) only once translation coverage reaches an agreed threshold.
- Keep fallback semantics (single fallback =
en) — do not introduce multi‑stage fallback chains.
If a previously removed locale must return, reintroduce it explicitly instead of resurrecting old purged markdown from history without review.
Date: 2025‑10‑09 (after pruning legacy locales & optimizing favicon)
| Artifact Type | Approx Size |
|---|---|
Total dist/ |
~1.3 GB |
| Client HTML (aggregated) | ~239 MB |
| All JS (aggregated) | ~299 KB |
| All CSS (aggregated) | ~141 KB |
| Favicon (source) | 447 bytes |
| Favicon (copied in dist) | ~12 KB |
Recent Optimizations:
- Removed eight deprecated locale content trees (≈1,124 markdown files) and associated category JSON plus feed source arrays.
- Replaced an anomalously large
favicon.svg(≈1.98 MB) with a lean vector (447 B source), cutting an unnecessary transfer overhead and improving cache priming. - Pruned dead RSS/news feed source definitions for removed locales to prevent accidental regeneration.
Observations:
- HTML still dominates (expected due to full static rendering for thousands of knowledge pages across 6 locales).
- Core JS & CSS remain lightweight, supporting fast hydration and low main‑thread cost.
- Further reduction paths likely center on: selective route generation, knowledge page pagination / summarization, image variant deduplication.
Potential Follow‑Ups (Not Yet Executed):
- Evaluate skipping generation of seldom‑visited long‑tail knowledge routes or introducing on‑demand ISR style rebuilds if framework support evolves.
- Audit image variants for redundant resolutions or format duplication.
- Investigate HTML streaming or segmented builds if peak memory pressure becomes a blocker.
This snapshot supersedes earlier pre‑optimization notes and serves as the new baseline for subsequent performance work.
- Multiple Difficulty Levels: Choose between Easy, Medium, and Hard modes:
- Easy: 10 questions per round
- Medium: 15 questions per round
- Hard: 20 questions per round
- Point System: Earn 50 points for every correct answer. The total score varies based on difficulty:
- Easy: Maximum 500 points
- Medium: Maximum 750 points
- Hard: Maximum 1000 points
- Speed Bonus: Answer quickly to earn extra points! The faster you answer, the more bonus points you
receive:
- Answer within 10 seconds: +50 bonus points
- Answer within 15 seconds: +25 bonus points
- 50:50 Joker: Limited use based on difficulty:
- Easy: 3 Jokers
- Medium: 5 Jokers
- Hard: 10 Jokers
- Use the 50:50 Joker to eliminate two wrong answers and increase your chances of success.
- Golden LPs: Unlock special rewards based on your performance:
- Musik-Novice: For completing all questions in Easy mode
- Musik-Master: For completing all questions in Medium mode
- Musik-Legend: For completing all questions in Hard mode
- Rankings: Compete against others and climb the Top 10 Leaderboards based on your scores. Check out your position in genre-specific and overall rankings.
- Music Genres: Select from a variety of music genres and prove your expertise in each category.
- Select your favorite music genre.
- Choose a difficulty level and start answering trivia questions.
- For every correct answer, you’ll earn points. The harder the difficulty, the more questions you face!
- At the end of each round, you’ll see your score and have the chance to unlock special Golden LPs if you answer all questions correctly.
- Track your position on the leaderboards and aim to become a Music Legend.
- Astro.js: For building the static and dynamic pages of the application.
- TypeScript: To ensure type safety and provide better developer experience.
- Static JSON & Markdown Data: All questions, genres, and content are stored as static assets (no external database).
- HTML5 & CSS3: For designing the user interface and styling the game.
- JSON: For managing question data and game configurations.
- Vitest: For unit and integration testing with TypeScript support.
This project uses Vitest for testing. Vitest is a fast unit test framework powered by Vite with native ESM and TypeScript support.
# Run all tests once
yarn test:run
# Run tests in watch mode
yarn test
# Run tests with UI interface
yarn test:ui
# Run tests with coverage report
yarn test:coverage- Unit tests are located next to the source files (e.g.,
utils/memoize.test.ts) - Integration tests are in
src/tests/integration/ - Test configuration is in
vitest.config.ts - Global test setup is in
src/tests/setup.ts
For detailed testing guidelines, see docs/testing-setup.md.
- Start with Easy mode to get comfortable, then work your way up to Hard.
- Use your 50:50 Jokers wisely, especially in the harder difficulty levels.
- Pay attention to your favorite genres for a better chance at winning.
- Practice regularly to improve your ranking and collect more rewards!
For automated or AI-assisted contributions, see AGENTS.md for project-specific agent guidelines
(commands, coding standards, performance, accessibility, and safety rules). Human contributors can
largely ignore it unless curious about automation practices.
The CSS layer follows a token → semantic mapping split:
src/styles/tokens.css: Pure design tokens (colors, spacing, typography, radii, shadows, durations, easing, focus baseline, icon sizes, opacity, scrollbar dimensions, achievement colors)src/styles/base.css: Semantic/UI variables (forms, buttons, cards, interactive states, scrollbar color semantics) + environment adaptation media queriessrc/styles/utilities.css: Minimal utility classes (currently only screen-reader helper)src/styles/global.css: Keeps only imports; no variables live here anymore
Rationale, maintenance checklist, and extension guidance live in docs/styles.md.
All pages use a unified SEO pipeline via buildPageSeo (see docs/seo-architecture.md). Deprecated
helper files (seoText.ts, metaUtils.ts, seoBasics.ts) were removed in October 2025. Do not
reintroduce per-page ad‑hoc meta generation; instead provide:
- Base
title,description,url - Optional
enrichedParts(array of semantic strings) - Optional
fallbackKeywords(curated terms) structuredData(JSON-LD objects) &breadcrumbs
The builder normalizes title suffixes, derives description & keywords, assembles robots directives,
and injects BreadcrumbList if missing.
This project uses Astro's new Content Layer compatibility mode for legacy type: "content"
collections. Migration rationale, explicit collection declarations, and reviewed breaking changes
are documented in MIGRATION_CONTENT_LAYER.md. If you add new knowledge collections (e.g.
additional languages), ensure they are declared in src/content/config.ts.
Categories are now handled through a clear separation of responsibilities to keep memory footprint, complexity and coupling low:
-
Loader (I/O + fallback resolution):
src/utils/category/categoryLoader.ts- Provides
loadCategoriesForLanguage(lang)andloadCategoryBySlug(slug, { language }). - Performs lazy loading via
import.meta.globthrough an internal index (categoriesIndex.ts). - Applies a single canonical fallback to English (
en) if a localized category set is missing.
- Provides
-
Pure transforms (in‑memory operations only):
src/utils/category/categoryTransforms.tsfilterPlayableCategories,filterNonPlayableCategoriesgetCategoryStats,getCategoriesByTypeisPlayableCategory(type guard)searchCategories(text search across headline/subline/body/slug)- All functions are side‑effect free and tree‑shakable.
Removed / deprecated:
- Legacy wrapper utilities (
categoryLoadingUtils.ts) and unused sorts were removed in Oct 2025 to avoid divergent logic and reduce cognitive load.
Design principles:
- A single fallback path keeps logic deterministic.
- I/O kept isolated so transforms remain easily testable later.
- No runtime language “detection” — route param drives locale; fallback only when data absent.
When adding new category logic:
- Put data fetching / fallback logic in the loader.
- Keep any data shaping, filtering, aggregation pure in the transforms file (or a new dedicated transform module if unrelated to categories).
- Avoid re‑introducing combined loader+transform helpers; compose them at call sites instead for clarity and tree‑shaking.
This structure improves build memory behavior and future test coverage readiness while staying aligned with the overall minimal client‑JS philosophy.
➡ For a deeper dive (loading flow diagrams, anti‑patterns, future ideas) see
docs/category-architecture.md.