A modern, elegant French word game inspired by Motus and Wordle. Built with Eleventy (11ty) and vanilla JavaScript.
-
Mot du jour (Daily Mode)
- One shared daily word for all players
- Deterministic word selection based on Europe/Paris timezone
- Streak tracking for consecutive daily wins
- Share your results with emoji grid
- Progress persists across sessions
-
Mode Aléatoire (Random Mode)
- Play unlimited random words
- New puzzle on demand
- Perfect for practice
- Variable word length: 4-10 letters (grid adapts automatically)
- Smart attempts: 6 attempts (7 for words > 7 letters)
- Color feedback:
- 🟩 Green = correct letter, correct position
- 🟨 Yellow = correct letter, wrong position
- ⬜ Gray = letter not in word
- Duplicate letter handling: Follows standard Wordle rules
- Dictionary validation: Only accept valid French words
- Accent-insensitive: Compare by normalized form (works with or without accents)
- Definition hints: See word definitions after completing the puzzle
- Clean, modern design with smooth animations
- Responsive layout (mobile-first approach)
- Light/Dark mode toggle with persistent preference
- Dual input methods: On-screen keyboard + physical keyboard
- Accessibility features: ARIA labels, good contrast, focus management
- French AZERTY keyboard layout
MotMystere/
├── package.json # Dependencies and scripts
├── .eleventy.js # Eleventy configuration
├── scripts/
│ └── generate-data.js # CSV parser and JSON generator
├── src/
│ ├── _includes/
│ │ └── layout.njk # Base layout template
│ ├── index.njk # Landing page
│ ├── daily.njk # Daily mode page
│ ├── random.njk # Random mode page
│ ├── data/
│ │ └── dictionary.csv # French word dictionary
│ └── assets/
│ ├── css/
│ │ └── main.css # Styles with theme support
│ ├── js/
│ │ ├── game.js # Core game engine
│ │ ├── dict.js # Dictionary loader
│ │ ├── storage.js # LocalStorage manager
│ │ ├── utils.js # Utility functions
│ │ ├── daily.js # Daily mode logic
│ │ └── random.js # Random mode logic
│ └── data/ # Generated JSON (output)
│ ├── words.json
│ └── defs.json
├── tests/
│ └── game.test.js # Unit tests
└── _site/ # Generated static site (output)
- Node.js (v18 or higher recommended)
- npm
-
Clone or download this repository
-
Install dependencies:
npm install
-
Ensure your dictionary CSV is at
src/data/dictionary.csvwith this format:Mot,Définitions Accueil,"[""Definition 1"", ""Definition 2""]"
Run the development server with live reload:
npm run devThe site will be available at http://localhost:8080
Generate the static site:
npm run buildThe output will be in the _site/ directory, ready to deploy to any static hosting service.
Execute the unit tests:
npm testThe daily word is selected deterministically using a simple hash function:
- Get current date in Europe/Paris timezone (YYYY-MM-DD format)
- Hash the date string to generate a seed
- Use seed to select index:
index = hash(dateString) % wordsCount - All users see the same word for the same date
This approach ensures:
- No server required
- Same word for all users on a given day
- Predictable but pseudo-random selection
- Works offline after initial load
The streak system tracks consecutive daily wins:
- Increment: Streak increases when you solve the daily puzzle
- Continue: Solving today after solving yesterday maintains the streak
- Reset: Streak resets when you:
- Fail to solve the daily puzzle, OR
- Skip a day (didn't solve yesterday's puzzle)
Streak data is stored locally with the last solved date for continuity verification.
At build time, the generate-data.js script:
- Reads
src/data/dictionary.csv - Parses each word and its definitions
- Handles mixed quotes in definition strings:
- Attempts JSON.parse first
- Sanitizes quotes if needed
- Falls back to heuristic splitting
- Normalizes words (removes accents) for comparison
- Generates two JSON files:
words.json: Array of{word, normalized}objectsdefs.json: Map ofnormalized word → definitions array
The evaluation algorithm correctly handles duplicate letters:
- First pass: Mark all correct positions (green) and decrement letter counts
- Second pass: For remaining letters:
- If letter count > 0: mark as present (yellow) and decrement
- Otherwise: mark as absent (gray)
Example: If the solution has one "E" and guess has two "E"s, only the first "E" (if present) gets marked yellow/green; the second gets gray.
The game uses different localStorage keys for each mode:
motmystere_daily_state: Current daily game state + datemotmystere_daily_streak: Streak count + last solved datemotmystere_random_state: Current random game statetheme: User's theme preference (light/dark)
Edit the range in dict.js:
export function getRandomWord(minLength = 4, maxLength = 10)
export function getDailyWord(date, minLength = 4, maxLength = 10)Modify in mode files (daily.js, random.js):
const maxAttempts = todayWord.length > 7 ? 7 : 6;Change the layout in mode files:
const KEYBOARD_LAYOUT = [
['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'],
['ENTER', 'W', 'X', 'C', 'V', 'B', 'N', '⌫']
];Edit CSS variables in main.css:
:root {
--color-correct: #6aaa64;
--color-present: #c9b458;
--color-absent: #787c7e;
/* ... */
}- Modern browsers with ES6 module support
- LocalStorage API
- Clipboard API (for share feature)
- CSS custom properties
Tested on:
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Eleventy (11ty): Static site generator
- Vanilla JavaScript: ES6 modules, no frameworks
- CSS: Custom properties, flexbox, grid
- Node.js: Build scripts and data processing
- Node test runner: Built-in test framework
MIT License - Feel free to use and modify for your own projects.
Inspired by:
- Wordle by Josh Wardle
- Motus (French TV game show)
Enjoy playing Mot Mystère! 🎮