The Beach Mystery is a fully static React application. It makes no API calls, reads no environment variables, and requires no server-side runtime. All story data, artwork, and game logic ship in a singleDocumentation 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.
vite build output. This makes deployment to GitHub Pages straightforward: build once, push the output, enable Pages in your repository settings, and the app is live. This guide walks through every step from forking the repository to serving a production URL.
Fork the repository
Visit apursley2012/the-beach-mystery on GitHub and click Fork. This creates a copy of the repository under your own account that you have full write access to, including the ability to enable GitHub Pages.
Clone locally and install dependencies
Run the development server
http://localhost:5173. Hot module replacement is enabled — edits to story nodes, scene images, or components appear in the browser immediately without a full reload.Configure the base path
GitHub Pages serves repositories from a subdirectory path by default:
https://your-username.github.io/the-beach-mystery/. Vite needs to know this prefix so it can write the correct paths into the built HTML for scripts, stylesheets, and asset URLs.Create vite.config.js in the project root and set the base option to match your repository name:Build for production
dist/ directory. The output is a set of static files — one index.html, fingerprinted JavaScript and CSS chunks, and all PNG scene illustrations with hashed filenames. No server is needed to serve them.You can preview the production build locally before deploying:dist/ at http://localhost:4173, including the correct base path.Deploy to GitHub Pages
GitHub Pages can serve static files from two sources: a dedicated Then go to Settings → Pages in your repository, set Source to GitHub Actions, and push to Run
gh-pages branch, or a GitHub Actions workflow. Either approach works.Option A — GitHub Actions (recommended)Create .github/workflows/deploy.yml:main. The workflow builds and deploys automatically on every push.Option B — gh-pages branchInstall the gh-pages helper package and add a deploy script to package.json:npm run deploy. Then in Settings → Pages, set Source to Deploy from a branch and choose gh-pages / / (root).HashRouter and Client-Side Routing
GitHub Pages is a static file host. When a visitor navigates directly to a URL likehttps://your-username.github.io/the-beach-mystery/play, the server looks for a file at that path, finds nothing, and returns a 404. This breaks any application that uses the HTML5 History API for routing.
The Beach Mystery avoids this problem by using React Router’s HashRouter. All routes are encoded in the URL fragment — the part after # — which the browser never sends to the server:
| HashRouter URL | What the server sees |
|---|---|
https://your-username.github.io/the-beach-mystery/#/play | /the-beach-mystery/ — the index.html |
https://your-username.github.io/the-beach-mystery/#/endings | /the-beach-mystery/ — the index.html |
https://your-username.github.io/the-beach-mystery/#/gallery | /the-beach-mystery/ — the index.html |
index.html, React Router reads the fragment, and the correct page component mounts client-side. No server configuration, 404.html redirect tricks, or _redirects files are needed.
If you ever migrate to a host that supports server-side rewrites (Netlify, Vercel, Cloudflare Pages), you can switch to
BrowserRouter and add a catch-all rewrite rule instead. For GitHub Pages, keep HashRouter.Asset URL Resolution
Scene illustrations are referenced indata/sceneImages.js using the import.meta.url pattern:
new URL(..., import.meta.url) calls during the build and treats them as asset imports. Each referenced PNG is:
- Copied into
dist/assets/story-art/ - Given a content-hash suffix (e.g.
beach-shore-a3f92b.png) - Have its URL rewritten to the final hashed path, respecting the
baseoption
base setting in vite.config.js is the only configuration change needed for a subdirectory deployment. You do not need to manually prefix asset URLs, update HTML <link> tags, or maintain separate environment config files.
All story content — node text, choice labels, riddle text, ending descriptions, and personalization questions — is hardcoded in
data/story.js. There are no external API calls, no CMS integrations, and no secret keys to manage. A fork of this repository deploys as-is without any .env configuration.Troubleshooting
Blank page after deploying to GitHub Pages
Blank page after deploying to GitHub Pages
The most common cause is a missing or incorrect
base in vite.config.js. Open DevTools, check the Network tab for 404s on .js or .css files, and compare the requested paths to your repository name. Update base to match the exact repository name including leading and trailing slashes, then rebuild and redeploy.Images show the 🏖️ fallback emoji in production
Images show the 🏖️ fallback emoji in production
The PNG filenames in
data/sceneImages.js do not match the files in assets/story-art/. Filenames are case-sensitive on Linux (the GitHub Actions runner). Ensure beach-shore.png in the map matches beach-shore.png on disk exactly — not Beach-Shore.png or beach_shore.png.Refreshing a deep link returns 404
Refreshing a deep link returns 404
This happens if you switched from
HashRouter to BrowserRouter without adding a server-side rewrite rule. Revert to HashRouter for GitHub Pages, or configure a 404.html redirect to index.html if you need pretty URLs on a host that supports it.The build succeeds locally but fails in GitHub Actions
The build succeeds locally but fails in GitHub Actions
Check the Node.js version. The workflow above pins Node 20. If your local environment is different, the
package-lock.json may have been generated with a different resolver. Commit a package-lock.json generated with the same Node version the workflow uses, or add a .nvmrc file and switch to node-version-file: '.nvmrc' in the workflow.