Skip to main content

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

Every story node in The Beach Mystery carries a scene field — a short string key like "beach" or "cave-fork" — that tells the app which illustration to display while that node’s text is on screen. The lookup happens in data/sceneImages.js, which maps each key to a PNG filename and a descriptive alt text string. Vite resolves those filenames into fingerprinted, production-ready URLs at build time, so adding or swapping an illustration is as simple as editing the map and dropping a file into the right folder.

How sceneImages.js Works

The file builds its URL map with a small helper function:
// data/sceneImages.js (source — simplified for readability)
const a = n => new URL(`../assets/story-art/${n}`, import.meta.url).href;

const s = {
  beach:            { src: a('beach-shore.png'),          alt: '...' },
  chest:            { src: a('buried-treasure-chest.png'), alt: '...' },
  'chest-open':     { src: a('buried-treasure-chest.png'), alt: '...' },
  'beach-walk':     { src: a('beach-shore.png'),          alt: '...' },
  map:              { src: a('treasure-map.png'),          alt: '...' },
  island:           { src: a('treasure-island.png'),       alt: '...' },
  'island-path':    { src: a('treasure-island.png'),       alt: '...' },
  riddle:           { src: a('treasure-island.png'),       alt: '...' },
  'cave-entrance':  { src: a('cave-entrance.png'),         alt: '...' },
  'cave-interior':  { src: a('dark-cave.png'),             alt: '...' },
  'cave-fork':      { src: a('cave-fork.png'),             alt: '...' },
  'treasure-chamber': { src: a('hidden-cave-treasure.png'), alt: '...' },
  'animal-den':     { src: a('magical-creature-den.png'),  alt: '...' },
  'beach-sunset':   { src: a('beach-sunset.png'),          alt: '...' },
};

// Exported lookup — falls back to 'beach' if a key is not found
export const g = e => s[e] || s.beach;
The new URL(path, import.meta.url).href pattern is the Vite-idiomatic way to reference static assets from JavaScript. Vite detects these calls during the build, copies the referenced files into dist/, appends a content-hash to each filename (fingerprinting), and rewrites the href value to the hashed path. You never need to manage asset paths manually.
Two or more scene keys can share the same PNG file — for example, chest and chest-open both point to buried-treasure-chest.png, and island, island-path, and riddle all use treasure-island.png. This is intentional: the scene key captures narrative context while the image file is the shared visual.

Complete Scene Key → Filename Map

Scene keyPNG fileUsed by nodes
beachbeach-shore.pngbeachOpening
chestburied-treasure-chest.pngchestDiscovery, chestChoice
chest-openburied-treasure-chest.pngchestOpened
beach-walkbeach-shore.pngchestSkipped, skipMap
maptreasure-map.pngmapChoice
islandtreasure-island.pngfollowMap
island-pathtreasure-island.pngriddleSolved
riddletreasure-island.pngriddleIntro
cave-entrancecave-entrance.pngcaveChoiceWithJewel, caveChoiceWithoutMap
cave-interiordark-cave.pngcaveEnteredWithJewel, caveEnteredWithoutJewel
cave-forkcave-fork.pngcavePathChoiceWithJewel, cavePathChoiceWithoutJewel
treasure-chamberhidden-cave-treasure.pngcaveLeftWithJewel, caveLeftWithoutJewel
animal-denmagical-creature-den.pngcaveRightWithJewel, caveRightWithoutJewel
beach-sunsetbeach-sunset.pngcaveSkippedWithJewel, caveSkippedWithoutJewel

Replacing an Existing Illustration

1

Prepare your new image

Create your replacement PNG. A 16:9 aspect ratio is recommended — the feature-card__art CSS rule crops to this ratio on the gallery and storyline screens. Any resolution is fine; images are lazy-loaded so large files do not block the initial render.Name the file something descriptive, for example beach-shore-v2.png.
2

Drop it into assets/story-art/

Copy the file to assets/story-art/ at the root of the repository. This is the only directory Vite scans for scene art.
assets/
└── story-art/
    ├── beach-shore.png          ← original
    ├── beach-shore-v2.png       ← your replacement
    └── ...
3

Update the map entry in sceneImages.js

Change the filename string inside the a() call for the relevant key:
// Before
beach: { src: a('beach-shore.png'), alt: 'Storybook illustration of a sunny beach shore ...' },

// After
beach: { src: a('beach-shore-v2.png'), alt: 'Storybook illustration of a sunny beach shore ...' },
You can update the alt text at the same time if the new image depicts the scene differently.
4

Rebuild

npm run build
Vite will hash the new filename, bundle it into dist/, and discard the old hashed copy. The old PNG is no longer referenced and will not appear in the output.

Adding a New Scene

1

Add your PNG to assets/story-art/

Place the new file in assets/story-art/. Choose a filename that matches the visual content, for example lighthouse-cliff.png.
2

Register it in sceneImages.js

Add a new key to the s object. The key should be a short, hyphenated string that describes the narrative moment — this is what story authors will write in the scene field of their nodes.
// data/sceneImages.js — add inside the s object
'lighthouse': {
  src: a('lighthouse-cliff.png'),
  alt: 'Storybook illustration of a tall white lighthouse on a rocky cliff above the sea',
},
Write alt text that describes what is actually depicted in the image, not just the narrative context. Screen readers will read this text to players who cannot see the illustration.
3

Reference the key in your story node

Set the scene field of your new node to the key you just registered:
// data/story.js — inside STORY_NODES
lighthouseDiscovery: {
  id: 'lighthouseDiscovery',
  type: 'narration',
  scene: 'lighthouse',            // ← matches the key in sceneImages.js
  lines: [
    'Beyond the cave, a narrow path climbs to a lighthouse perched on the cliff.',
    'Its beam sweeps the darkening water, steady and slow.',
  ],
  next: 'lighthouseChoice',
},

The SceneArt Component

Story nodes do not render images directly — they delegate to the SceneArt component, which handles loading, fallback, and overlay behaviour.
import { S as SceneArt } from '../components/SceneArt';

// Full-size illustration in a narration or riddle view
<SceneArt scene={node.scene} size="large" />

// Smaller thumbnail in a feature card or gallery grid
<SceneArt scene="chest" size="small" />

Props

PropTypeDefaultDescription
scenestringScene key looked up in sceneImages.js. Falls back to 'beach' if the key is not found.
size'small' | 'large''large'Applies scene-art--small or scene-art--large CSS modifier class.

Rendered structure

<div className="scene-art scene-art--{scene} scene-art--{size}" aria-hidden="true">
  <img
    src={resolvedUrl}
    alt={altTextFromMap}
    className="scene-art__img"
    loading="lazy"
    onError={/* hides img, shows fallback */}
  />
  <div className="scene-art__fallback">
    <span className="scene-art__fallback-icon">🏖️</span>
  </div>
  <div className="scene-art__overlay scene-art__overlay--{scene}" />
</div>
The aria-hidden="true" attribute marks the entire artwork container — including the img inside it — as decorative. The whole subtree is hidden from the accessibility tree, so screen readers will not announce the alt text. The alt strings in sceneImages.js are still present in the markup for maintainability and tooling, but they are not read aloud during play because the surrounding narrative text already describes the scene.

Error handling

If a PNG fails to load (broken path, network error, missing file), the onError handler hides the img element and displays the fallback div with a 🏖️ emoji instead. The story remains fully playable — artwork is decorative, not functional.
If you add a new scene key to sceneImages.js but forget to drop the PNG into assets/story-art/, the img will 404 silently and the fallback emoji will render. Always verify that the filename in the a() call matches the actual file on disk.

Image Recommendations

Format

PNG is the standard format used throughout the project. SVG files are also supported — add the .svg extension in the a() call and the rest of the pipeline is identical.

Aspect ratio

16:9 is recommended. The feature-card__art CSS rule applies this ratio on gallery and feature-card views. Tall or square images will be cropped by the browser.

Resolution

Any resolution is accepted. Images are lazy-loaded, so high-resolution files do not delay the initial page load. Vite copies them as-is; no resizing occurs at build time.

Alt text

Write alt text in sceneImages.js, not in JSX. This centralises accessibility strings and ensures every use of the same image inherits the same description. Be specific — describe what the illustration shows.

Build docs developers (and LLMs) love