Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev-mode/llms.txt

Use this file to discover all available pages before exploring further.

dev-mode uses React Router’s HashRouter to manage all client-side navigation. Every URL change is tracked in the hash fragment (#/about, #/projects, etc.) rather than the pathname, which means the browser never makes a round-trip to the server when the user moves between pages. A single AppShell layout component wraps all seven routes, providing the persistent bottom navigation bar and the ScanlineOverlay CRT effect across every view.

Why HashRouter?

Hash-based routing stores the active route after the # character in the URL. From the server’s perspective, every request is for the same file — the URL https://example.com/pages/About.html#/about resolves to About.html whether the user is on the home screen or has navigated to About. This makes dev-mode fully compatible with static file hosts (GitHub Pages, Netlify, S3, etc.) that cannot be configured with path rewrite rules.
The trade-off is that hash-based URLs are not as clean as history-mode URLs. For a portfolio project hosted statically, this is the right default choice.

Route Table

The router defines seven routes, all nested under the AppShell layout component. The top-level Route renders AppShell, and each child Route renders the matching page component.
RouteComponentArcade Theme
/#/HomeHigh-score leaderboard
/#/aboutAboutCharacter select
/#/projectsProjectsCartridge wall
/#/skillsSkillsCommand list
/#/writingWritingManual rack
/#/casesCaseStudiesBoss battles
/#/contactContactInitials entry

Page Transition Animations

Route changes are animated with Framer Motion’s AnimatePresence component, which is wrapped around the Routes tree with mode="wait". The wait mode ensures the exiting page fully completes its exit animation before the entering page begins mounting — preventing both views from being visible at the same time. Each page is wrapped in a motion.div with the following transition definition:
<AnimatePresence mode="wait">
  <motion.div
    key={location.pathname}
    initial={{ opacity: 0, scale: 0.98, filter: "blur(10px)" }}
    animate={{ opacity: 1, scale: 1,    filter: "blur(0px)"  }}
    exit={{    opacity: 0, scale: 1.02, filter: "blur(10px)" }}
    transition={{ duration: 0.3, ease: "easeInOut" }}
  >
    <Routes>{/* ... */}</Routes>
  </motion.div>
</AnimatePresence>
The combination of opacity fade, subtle scale change, and blur creates the “screen switching on/off” feel that matches the retro arcade aesthetic. The enter scale starts slightly small (0.98) while the exit scale ends slightly large (1.02), giving a punchy zoom-through sensation.
The key prop is set to location.pathname so AnimatePresence detects a new child each time the route changes and triggers the enter/exit cycle.

The window.__STATIC_PAGE_ROUTE__ Mechanism

Because the SPA is deployed as static HTML files, a user who navigates directly to pages/About.html or shares a link must still land on the correct React route. Each pages/*.html file sets a global variable before main.js loads:
<script>
  window.__STATIC_PAGE_ROUTE__ = "/about";
</script>
This tells the application which route was intended for this HTML shell. The same inline script also performs an immediate hash redirect if the URL hash is missing, so HashRouter always receives a valid route string on first load:
<script>
  window.__STATIC_PAGE_ROUTE__ = "/";

  (function () {
    if (!window.location.hash || window.location.hash === "#") {
      window.location.replace(
        window.location.pathname +
        window.location.search +
        "#/"
      );
    }
  })();
</script>
1

Browser requests a page

The user navigates directly to pages/About.html or refreshes the browser while on the About route.
2

Inline script runs

window.__STATIC_PAGE_ROUTE__ is set to "/about" and the redirect function checks whether a hash is already present.
3

Hash is set if missing

If there is no hash (e.g. pages/About.html with no fragment), the script replaces the URL with pages/About.html#/about before React mounts.
4

React Router picks up the hash

HashRouter reads #/about from the URL and renders the About component inside AppShell as if the user had navigated there normally.

AppShell Layout

Every route renders inside AppShell, which provides two persistent elements:
  • ScanlineOverlay — fixed at z-50, renders the CRT scanline and vignette over all page content.
  • Bottom navigation bar — fixed at the bottom of the viewport, containing seven ArcadeButton links to each route.
The outermost wrapper div carries the animate-flicker Tailwind utility (a subtle screen-flicker keyframe animation) and the selection color overrides selection:bg-arcade-magenta selection:text-arcade-white.

Build docs developers (and LLMs) love