The Beach Mystery is a single-page application built with React 18 and bundled by Vite, deployed as a fully static site to GitHub Pages. Because GitHub Pages serves pre-built files from a repository without any server-side routing layer, the app uses React Router’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/the-beach-mystery/llms.txt
Use this file to discover all available pages before exploring further.
HashRouter — every URL takes the form /#/play or /#/endings, so navigation events never reach the host server. All routing is resolved entirely in the browser. This page walks through every directory, file, and dependency you need to understand before making changes.
Directory Layout
The repository has a flat, intentional structure: compiled output lives inassets/, all React source is divided across components/, data/, and hooks/, and index.html is the single entry point the browser loads.
The
assets/ directory contains compiled Vite output, not hand-written source. The files main.js, main.css, Nav.css, Footer.css, index.js, index2.js, and jsx-runtime.js are generated artifacts. The authoritative source lives in components/, data/, and hooks/.Directory Purposes
assets/
Vite’s compiled output directory.
main.js is the bundled React app entry point, main.css contains compiled global styles, jsx-runtime.js is the React JSX transform runtime, and story-art/ holds all ten PNG scene illustrations used as chapter backdrops during story playback.components/
Reusable React components shared across multiple routes.
Nav.js renders the navigation bar and all route links, Footer.js renders the brand footer, and SceneArt.js resolves a scene key to its corresponding PNG and renders the illustration with alt text.data/
Pure data modules with no React dependencies.
story.js exports every STORY_NODE, ENDING, STORYLINE, PERSONALIZATION_QUESTION, GALLERY_SCENE, and the single RIDDLE object. sceneImages.js maps each scene key string (e.g. "cave-fork") to a { src, alt } object resolved via import.meta.url.hooks/
Custom React hooks.
useGameState.js is the sole file here and owns all game logic — story progression, inventory flags, achievements, settings, and localStorage persistence. See Game State for the full API.Routing
The app usesHashRouter from React Router. Hash-based URLs (/#/play, /#/endings) are processed entirely by the browser’s JavaScript runtime; the # fragment is never sent to the server. This makes the app deployable to any static file host — including GitHub Pages — without a custom 404 redirect or server configuration.
Browser loads index.html
GitHub Pages serves the single
index.html file for all requests. The HTML file preloads main.js and all module-preload hints for chunked assets.React Router initialises HashRouter
HashRouter reads window.location.hash and matches it against the registered route table, rendering the correct page component.Nav.js:
| Path | Label | Icon | Purpose |
|---|---|---|---|
/ | Start | ⚓ | Home / landing page |
/play | Play | 🎮 | Interactive story player |
/endings | Endings | 🏆 | Achievement tracker for all four endings |
/gallery | Gallery | 🖼️ | Scene artwork viewer |
/about | About | ℹ️ | About the project |
/case-study | Case Study | 📄 | Technical case study |
/blog | Journal | 📖 | Development journal |
Tech Stack
React 18
Component rendering, hooks (
useState, useCallback, useEffect), and the virtual DOM. The compiled React runtime is bundled into assets/index.js.React Router (HashRouter)
Client-side routing via hash URLs. Bundled into
assets/index2.js. HashRouter is used specifically for GitHub Pages compatibility — no server configuration required.Vite
Build tool and development server. Bundles the source modules into the
assets/ directory and resolves import.meta.url paths for story art PNGs in sceneImages.js.localStorage
Browser persistence layer for save data, achievements, and settings. Three separate keys are used:
beach-mystery-save, beach-mystery-achievements, and beach-mystery-settings. See Game State for details.CSS Custom Properties
All theme values (colours, spacing, typography scale) are defined as CSS variables on
:root. High-contrast mode is toggled by switching a data attribute that rebinds these variables.Custom Inline SVG / PNG Artwork
Ten hand-crafted storybook-style PNG illustrations stored in
assets/story-art/. Each is mapped to one or more scene keys in sceneImages.js and rendered by SceneArt.js.Typography
The app loads three Google Fonts families directly inindex.html:
| Family | Weights | Role |
|---|---|---|
| Cormorant Garamond | 400, 400i, 500, 500i, 600, 700 | Literary serif for all story narration text, dialogue, and riddle prompts |
| Outfit | 300, 400, 500, 600, 700 | Clean sans-serif for all UI elements — navigation, buttons, labels, and settings panels |
| JetBrains Mono | 400, 500 | Monospace for code blocks in the Case Study and Journal pages |