Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/magical/llms.txt

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

The Magical Portfolio solves a fundamental tension of static hosting: React Router expects to own the URL path (/about, /projects, …), but static file servers can only serve files that exist on disk. Rather than requiring 404-rewrite rules — which are host-specific and sometimes unavailable — the portfolio uses hash-based routing. Every page is addressable as pages/About.html#/about. The fragment (#/about) is consumed entirely client-side by React Router; the file server only ever sees the path portion, which always resolves to a real .html file.

Hash Routing Mechanism

Each HTML shim contains a small inline script that runs before the React bundle loads. Its job is to ensure the URL always carries a hash fragment, so React Router’s HashRouter has something to parse:
<script>
  window.__STATIC_PAGE_ROUTE__ = "/";

  (function () {
    if (!window.location.hash || window.location.hash === "#") {
      window.location.replace(
        window.location.pathname +
        window.location.search +
        "#/"
      );
    }
  })();
</script>
Two things happen here:
  1. window.__STATIC_PAGE_ROUTE__ is set to the canonical route path for this page (e.g. "/", "/about", "/case-studies"). The React app reads this value on boot to initialise the router with the correct starting location even before any user navigation.
  2. The IIFE checks whether window.location.hash is absent or is the bare "#" string. If so, it immediately replaces the URL with the same path + a #/ (or #/about, depending on the page) fragment. This handles the case where a user lands on pages/About.html directly without a fragment — the redirect fires instantly, before React mounts, so there is no flash.
The combination means every entry point is guaranteed to carry a valid hash route before React Router initialises.

Route Definitions

The portfolio ships with seven routes. Each has its own HTML shim, a descriptive page title, and an arcane name used throughout the UI:
URL PathHTML FilePage TitleArcane Name
/index.htmlHomePortal
/aboutpages/About.htmlAboutChronicle
/projectspages/Projects.htmlProjectsGrimoire
/skillspages/Skills.htmlSkillsAffinities
/writingpages/Writing.htmlWritingTomes
/case-studiespages/CaseStudies.htmlCase StudiesRituals
/contactpages/Contact.htmlContactSummoning
The root index.html sets window.__STATIC_PAGE_ROUTE__ = "/" and redirects to #/. The pages/ shims set their respective route strings and redirect to the appropriate fragment (#/about, #/case-studies, etc.). The Navigation component is driven by a static navItems array defined at the top of components/Navigation.js. Each item maps a React Router path to a human label and a Lucide React icon:
const navItems = [
  { path: "/",             label: "Portal",     icon: Sparkles },
  { path: "/about",        label: "Chronicle",  icon: Book },
  { path: "/projects",     label: "Grimoire",   icon: CodeXml },
  { path: "/skills",       label: "Affinities", icon: Hexagon },
  { path: "/writing",      label: "Tomes",      icon: ScrollText },
  { path: "/case-studies", label: "Rituals",    icon: FlaskConical },
  { path: "/contact",      label: "Summon",     icon: Send },
];
The navigation bar renders navItems.slice(1) (skipping the Portal/home entry) as <NavLink> components. When a link is active, Framer Motion animates a shared layout indicator (layoutId="nav-indicator") across the active item using spring physics (stiffness: 300, damping: 30), creating the sliding highlight effect. The Portal link (path: "/") appears as the logo/wordmark on the left side of the nav bar rather than in the link list, keeping the navigation compact.

Adding a New Route

1

Create the page component

Add a new React component file to your source tree, for example src/pages/Compendium.jsx. Give it a default export rendering your page content. Follow the existing pattern of wrapping the page in a min-h-screen pt-12 pb-24 container so it clears the fixed navigation bar.
2

Register the route in the router

Open the App shell (the file that defines the <Routes> tree) and import your new component. Add a <Route> entry:
import Compendium from "./pages/Compendium";

// Inside <Routes>:
<Route path="/compendium" element={<Compendium />} />
The AnimatePresence wrapper keyed on useLocation().pathname will automatically apply the standard blur/fade page transition to the new route.
3

Add the nav item

Append an entry to the navItems array in components/Navigation.js. Choose an appropriate Lucide icon and an arcane label that fits the mystical theme:
{ path: "/compendium", label: "Compendium", icon: Library },
Import Library (or your chosen icon) from lucide-react at the top of the file.
4

Create the HTML shim file

Copy an existing shim — for example pages/About.html — to pages/Compendium.html. Update the three fields that are page-specific:
  1. <title> tag: change About | magicalCompendium | magical
  2. window.__STATIC_PAGE_ROUTE__: change "/about""/compendium"
  3. The hash-redirect string: change "#/about""#/compendium"
Ensure the relative paths to ../assets/main.js and ../assets/main.css are correct for a file sitting inside pages/.
After these four steps, the new page is accessible at both pages/Compendium.html#/compendium (direct link) and via the navigation bar inside the running SPA.

Build docs developers (and LLMs) love