Skip to main content

Documentation Index

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

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

Dev Nexus uses React Router v6 with a HashRouter for all client-side navigation. Because the site is deployed as a collection of static files, hash-based routing means the browser never makes a network request for a new path — the #/route segment is handled entirely in JavaScript without any server rewrite rules.

Why HashRouter?

A standard BrowserRouter requires the server to return index.html for every path (e.g. /about, /projects). When hosting static files on a basic file server or CDN that does not support catch-all rewrites, requests to /about would return a 404. HashRouter solves this because the hash portion of a URL (#/about) is never sent to the server — the browser always loads the root file (index.html or a per-route shell), and React Router reads the hash to decide which component to render.

Route Definitions

All seven routes are defined in assets/main.js inside the AppRoutes component. The nav labels come from the navItems array in components/NavBar.js:
PathComponentNav Label
/HomePORTAL
/aboutAboutORIGIN
/projectsProjectsSPELLS
/skillsSkillsARSENAL
/writingWritingSCROLLS
/case-studiesCaseStudiesRITUALS
/contactContactSUMMON

Router Structure

The router is bootstrapped at the bottom of assets/main.js where ReactDOM.render() is called. The outer HashRouter (imported as H from NavBar.js, which re-exports from react-router-dom) wraps an App component that renders the shell and an inner AppRoutes component. Here is the simplified structure:
// Simplified from assets/main.js

import { HashRouter, Routes, Route, useLocation } from 'react-router-dom';
import { AnimatePresence } from 'framer-motion';

function AppRoutes() {
  const location = useLocation();
  return (
    <AnimatePresence mode="wait">
      <Routes location={location} key={location.pathname}>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/projects" element={<Projects />} />
        <Route path="/skills" element={<Skills />} />
        <Route path="/writing" element={<Writing />} />
        <Route path="/case-studies" element={<CaseStudies />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </AnimatePresence>
  );
}

function App() {
  return (
    <HashRouter>
      <div className="min-h-screen relative flex flex-col">
        <ParticleField />
        <NavBar />
        <main className="flex-grow relative z-10">
          <AppRoutes />
        </main>
        <Footer />
      </div>
    </HashRouter>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

AnimatePresence + useLocation Page Transitions

The AnimatePresence component from Framer Motion enables exit animations — without it, a component would be removed from the DOM instantly before its exit animation could play. The key technique is passing both location and key={location.pathname} to <Routes>:
const location = useLocation();

<AnimatePresence mode="wait">
  <Routes location={location} key={location.pathname}>
    {/* routes */}
  </Routes>
</AnimatePresence>
  • location tells <Routes> which location to render for, so the old route stays mounted long enough for its exit animation to finish.
  • key={location.pathname} causes React to fully unmount and remount the <Routes> tree when the path changes, which triggers AnimatePresence to run the exit animation on the old page and the enter animation on the new one.
Each page component is wrapped in <PageTransition>, which applies an opacity + scale + backdrop-filter: blur() animation on mount and unmount:
// components/PageTransition.js
// enter: { opacity: 0, scale: 0.95, filter: 'blur(10px)' }
//        → { opacity: 1, scale: 1,    filter: 'blur(0px)' }
// exit:  → { opacity: 0, scale: 1.05, filter: 'blur(10px)' }
// duration: 0.5s, easing: easeInOut

Static HTML Shells

Because the site is deployed as static files, each route has a corresponding HTML file in the pages/ directory. These shells allow users to bookmark or share direct links (e.g. https://example.com/pages/About.html) without hitting a 404. Each shell does two things:
  1. Sets window.__STATIC_PAGE_ROUTE__ to the route path so the app knows its initial location.
  2. Redirects to the correct hash if no hash is already present in the URL.
<!-- pages/About.html -->
<script>
  window.__STATIC_PAGE_ROUTE__ = "/about";

  (function () {
    if (!window.location.hash || window.location.hash === "#") {
      window.location.replace(
        window.location.pathname +
        window.location.search +
        "#/about"
      );
    }
  })();
</script>
The same pattern is used in the root index.html for the / route, with window.__STATIC_PAGE_ROUTE__ = "/" and a redirect to #/. The NavBar component uses useLocation() from React Router to detect which route is currently active and applies a highlighted style to the matching link:
// components/NavBar.js (simplified)
const navItems = [
  { path: '/',             label: 'PORTAL'  },
  { path: '/about',        label: 'ORIGIN'  },
  { path: '/projects',     label: 'SPELLS'  },
  { path: '/skills',       label: 'ARSENAL' },
  { path: '/writing',      label: 'SCROLLS' },
  { path: '/case-studies', label: 'RITUALS' },
  { path: '/contact',      label: 'SUMMON'  },
];

function NavBar() {
  const location = useLocation();

  return (
    <nav>
      {navItems.map(item => {
        const isActive = location.pathname === item.path;
        return (
          <Link
            key={item.path}
            to={item.path}
            className={
              isActive
                ? 'text-neon-lime text-glow-lime'          // active style
                : 'text-moonlight/70 hover:text-electric-violet'  // default
            }
          >
            {isActive ? `[ ${item.label} ]` : item.label}
          </Link>
        );
      })}
    </nav>
  );
}
Active links are wrapped in square brackets (e.g. [ PORTAL ]) and rendered in text-neon-lime with a lime glow effect.

Adding a New Route

To add an eighth page to Dev Nexus, follow these three steps:
1

Create the page component

Add your page component inside assets/main.js (or as a separate component file). Wrap the content in <PageTransition> so it receives the enter/exit animation:
function Experiments() {
  return (
    <PageTransition>
      <div className="max-w-4xl mx-auto">
        <h1 className="font-display text-4xl text-moonlight">
          EXPERIMENTS
        </h1>
      </div>
    </PageTransition>
  );
}
2

Register the route

Add a <Route> inside the AppRoutes component in assets/main.js:
<Route path="/experiments" element={<Experiments />} />
Then add the nav item to the navItems array in components/NavBar.js:
{ path: '/experiments', label: 'FORGE' },
3

Add the static HTML shell

Create pages/Experiments.html by copying any existing shell (e.g. pages/About.html) and updating the route values:
<script>
  window.__STATIC_PAGE_ROUTE__ = "/experiments";

  (function () {
    if (!window.location.hash || window.location.hash === "#") {
      window.location.replace(
        window.location.pathname +
        window.location.search +
        "#/experiments"
      );
    }
  })();
</script>
Also add a <link rel="modulepreload"> for any new component files in the shell’s <head>.

Build docs developers (and LLMs) love