Skip to main content

Documentation Index

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

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

dev.void uses React Router v6 for client-side navigation. All route declarations live in assets/main.js, which defines nine routes, each wrapped in a shared PageWrapper component that drives a Framer Motion enter/exit animation. Because the portfolio is deployed as a static file (no custom server), a HashRouter is used so that navigation never triggers a real HTTP request.

Why HashRouter?

HashRouter stores the current location in the URL hash (e.g. https://example.com/#/about) rather than the URL pathname. This means the browser never actually requests /about from a server — it only ever fetches index.html from the root. Static hosts such as GitHub Pages and Netlify’s basic static mode serve index.html regardless of the hash, so the SPA always boots correctly. If BrowserRouter were used instead, navigating directly to https://example.com/about would return a 404 unless the host is configured to rewrite all paths to index.html.
Navigation links in AuroraNav therefore use hash-prefixed hrefs — /#/about, /#/projects, and so on — rather than bare pathnames.

Router structure

The router tree is assembled in the AppRoutes component and mounted inside a top-level HashRouter:
assets/main.js
// Minified aliases from the compiled bundle:
// H  = HashRouter       (from react-router-dom)
// g  = AnimatePresence  (from framer-motion, re-exported as A from AuroraNav.js)
// d  = useLocation      (from react-router-dom)
// f  = Routes           (from react-router-dom)
// l  = Route            (from react-router-dom)

const AppRoutes = () => {
  const location = useLocation(); // drives AnimatePresence key

  return (
    <AnimatePresence mode="wait">
      <Routes location={location} key={location.pathname}>
        <Route path="/"            element={<PageWrapper><Home /></PageWrapper>} />
        <Route path="/about"       element={<PageWrapper><About /></PageWrapper>} />
        <Route path="/projects"    element={<PageWrapper><Projects /></PageWrapper>} />
        <Route path="/skills"      element={<PageWrapper><Skills /></PageWrapper>} />
        <Route path="/work"        element={<PageWrapper><Work /></PageWrapper>} />
        <Route path="/case-studies" element={<PageWrapper><CaseStudies /></PageWrapper>} />
        <Route path="/blog"        element={<PageWrapper><Blog /></PageWrapper>} />
        <Route path="/contact"     element={<PageWrapper><Contact /></PageWrapper>} />
        <Route path="/testimonials" element={<PageWrapper><Testimonials /></PageWrapper>} />
      </Routes>
    </AnimatePresence>
  );
};

function App() {
  return (
    <HashRouter>
      <div className="relative min-h-screen bg-space-950 text-slate-200">
        <StarfieldBackground />
        <CometCursor />
        <AuroraNav />
        {/* inline SVG filter used by .aurora-filter utility */}
        <main className="relative z-10">
          <AppRoutes />
        </main>
      </div>
    </HashRouter>
  );
}
Passing location as a prop to <Routes> and keying <AnimatePresence> by location.pathname is the standard React Router v6 pattern for page-transition animations. When the pathname changes, React unmounts the old route tree and mounts the new one, giving Framer Motion a clean exit/enter lifecycle.

Page transition — PageWrapper

Every route element is wrapped in PageWrapper, a motion.div that applies an identical enter/exit animation to every page:
assets/main.js
const PageWrapper = ({ children }) => (
  <motion.div
    initial={{ opacity: 0, y: 20,  filter: "blur(10px)" }}
    animate={{ opacity: 1, y: 0,   filter: "blur(0px)"  }}
    exit={{    opacity: 0, y: -20, filter: "blur(10px)" }}
    transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
    className="pt-24 min-h-screen relative z-10"
  >
    {children}
  </motion.div>
);

Enter

Fades in from opacity: 0, slides up 20 px, and de-blurs from 10 px over 600 ms.

Exit

Fades out to opacity: 0, slides up a further 20 px, and re-blurs to 10 px.

Easing

Uses the cubic-bezier [0.22, 1, 0.36, 1] — an “expo out” curve for a snappy, cinematic feel.

Route table

PathPage TitlePrimary ComponentDescription
/HomeAuroraHero + quote blockLanding page with animated hero and intercepted signal quote
/aboutStellar CartographyPlanetaryProfileDeveloper bio and origins
/projectsProbes & PayloadsMissionPatchWallDeployed projects grid
/skillsTelemetryTelemetryRadarTechnical skill visualisation
/workMission LogMissionLogTimelineWork history timeline
/case-studiesAnomalies InvestigatedFlightRecorderCaseDetailed project case studies
/blogField Notes from the VoidOrbitingSatellitesBlog post listing
/contactOpen a Hailing FrequencyCommsConsoleContact form
/testimonialsVoices from the CrewAsteroidQuotesTestimonials

Because HashRouter is active, AuroraNav constructs all internal links with a /#/ prefix:
// Example nav links inside AuroraNav
<a href="/#/about">About</a>
<a href="/#/projects">Projects</a>
<a href="/#/contact">Contact</a>
Do not change these to bare pathnames like /about without simultaneously switching to BrowserRouter and configuring your static host to rewrite all paths to index.html. Mixing hash links with BrowserRouter will break direct-URL navigation.

Build docs developers (and LLMs) love