Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dyed-in-the-wool/llms.txt

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

The Problem

GitHub Pages is a static file host — it serves files exactly as they exist on disk. When a visitor navigates directly to a deep URL such as username.github.io/dyed-in-the-wool/about, GitHub looks for a file at about/index.html. That file does not exist, so GitHub returns a 404. This is a common pain point for single-page applications where the router lives entirely in JavaScript and only index.html is ever physically present.

The Solution: HashRouter + Static HTML Stubs

Dyed in the Wool solves this with two complementary strategies:
  1. HashRouter — React Router is configured with HashRouter, so every URL uses the hash fragment: /#/about, /#/projects, /#/contact, etc. GitHub Pages always serves index.html for the root path, and the hash portion is handled entirely by the browser — no server involvement required.
  2. Static HTML stubs — A small HTML file lives alongside the root index.html for every route (e.g. pages/About.html). Each stub loads the same JS/CSS bundle and runs a tiny script that sets the correct hash before the app boots, so direct links to pages/About.html land on the right page.

Static Page Template

Every page stub follows this structure (from the actual source in pages/About.html):
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>About | dyed-in-the-wool</title>
    <script type="module" crossorigin src="../assets/main.js"></script>
    <link rel="modulepreload" crossorigin href="../assets/proxy.js">
    <link rel="modulepreload" crossorigin href="./components/DyeDropCursor.js">
    <link rel="modulepreload" crossorigin href="./components/Layout.js">
    <link rel="modulepreload" crossorigin href="./components/TieDyeBackground.js">
    <link rel="stylesheet" crossorigin href="../assets/main.css">
    <script>
  window.__STATIC_PAGE_ROUTE__ = "/about";
  if (!window.location.hash || window.location.hash === "#/" || window.location.hash === "#") {
    window.location.hash = "/about";
  }
</script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
The inline script does two things:
  • Records the intended route in window.__STATIC_PAGE_ROUTE__ for potential use by app code or analytics.
  • Sets window.location.hash to the correct route path if the hash is empty or set to the root #/. This fires before the React bundle boots, so the router always sees the right initial location.

Current Static Pages

FileRoute
index.html/#/ (root)
pages/About.html/#/about
pages/Projects.html/#/projects
pages/Skills.html/#/skills
pages/Work.html/#/work
pages/Contact.html/#/contact

Adding a New Route

1

Add the React route

Register the new route inside the app’s router. Add a <Route> entry alongside the existing routes in your app entry point:
<Route path="newpage" element={<NewPage />} />
2

Create the static HTML stub

Create pages/NewPage.html using the template above. Update the two route-specific values:
<title>New Page | dyed-in-the-wool</title>
...
<script>
  window.__STATIC_PAGE_ROUTE__ = "/newpage";
  if (!window.location.hash || window.location.hash === "#/" || window.location.hash === "#") {
    window.location.hash = "/newpage";
  }
</script>
3

Update the nav

Add the new route to the navLinks array in Layout.js so it appears in the site navigation:
{ path: '/newpage', label: 'New Page' }
4

Rebuild and redeploy

Run npm run build to produce an updated bundle, then push the new pages/NewPage.html and updated dist/ contents. If you are deploying to GitHub Pages, remember to also copy pages/NewPage.html into dist/ (or update your CI step to do so automatically).
Each page stub (including index.html) contains <link rel="modulepreload"> tags that reference component files by relative path — for example ./components/DyeDropCursor.js, ./components/Layout.js, and ./components/TieDyeBackground.js. These hints tell the browser to fetch those chunks early. If you rename or move component files during development, remember to update these paths in every static HTML file to avoid broken preloads.
The window.__STATIC_PAGE_ROUTE__ variable is set in every stub but is not currently read back by any app code — it is purely informational at the moment. You can hook into it yourself: for example, read it in your app entry point to implement route-aware server-side analytics, skip the hash redirect under certain conditions, or power an A/B testing layer that keys off the original entry URL.

Build docs developers (and LLMs) love