Skip to main content

Documentation Index

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

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

Web Weaver’s routing is powered by React Router v6 with hash-based URLs — each new page is a React component, a registered route, a static HTML file for direct linking, and a nav entry. All four pieces live in assets/main.js, except the static HTML file which goes in pages/. This guide walks through each step, then covers how to populate existing pages by extending the built-in data arrays.

Adding a New Page

1

Write a new React component in main.js

Open assets/main.js and define your new page component. Place it near the other page-level components (look for HeroSection, ProjectsPage, etc.). The component is a plain React function that returns JSX:
assets/main.js
function SpellbookPage() {
  return (
    <div className="min-h-screen bg-ink text-parchment px-6 py-16 max-w-4xl mx-auto">
      <h1 className="font-serif text-4xl text-teal-bright mb-4">
        The Spellbook
      </h1>
      <p className="font-mono text-parchment/80 leading-relaxed">
        A curated collection of code incantations and development rituals.
      </p>
      {/* Your content here */}
    </div>
  );
}
Use the same Tailwind utilities found throughout the project (bg-ink, text-teal-bright, font-serif, font-mono) so the new page stays visually consistent with the existing aesthetic.
2

Register a route in the Routes block

Find the <Routes> block in main.js — it contains all existing <Route> declarations. Add your new route inside it:
assets/main.js
<Routes>
  <Route path="/"           element={<HeroSection />} />
  <Route path="/about"      element={<AboutPage />} />
  <Route path="/projects"   element={<ProjectsPage />} />
  <Route path="/blog"       element={<BlogPage />} />
  {/* Add your new route here */}
  <Route path="/spellbook"  element={<SpellbookPage />} />
</Routes>
The path you choose here becomes the hash segment in the URL — visitors will reach this page at https://yoursite.com/#/spellbook.
3

Create a static HTML file in pages/

Each route needs a corresponding static HTML file so that the page is directly linkable and indexable. Create pages/Spellbook.html with this template:
pages/Spellbook.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>The Spellbook — Morgan Weaver</title>
    <script>
      // Tells the SPA which route to activate on load
      window.__STATIC_PAGE_ROUTE__ = '/spellbook';
    </script>
    <script type="module" src="../assets/main.js"></script>
    <link rel="stylesheet" href="../assets/main.css" />
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
The window.__STATIC_PAGE_ROUTE__ assignment is the critical piece — the React app reads this value on boot and navigates to the correct route when the page loads directly (not via the SPA’s own navigation).
4

Add a nav link in the Layout component

Find the Layout component in main.js — it contains the site-wide <header> with navigation links. Add your new page to the nav list:
assets/main.js
// Inside the Layout component's <nav> or <header> element
<NavLink to="/spellbook" className={({ isActive }) =>
  `font-mono text-sm tracking-wider transition-colors ${
    isActive ? 'text-teal-bright' : 'text-parchment/70 hover:text-parchment'
  }`
}>
  Spellbook
</NavLink>
The isActive class swap (text-teal-bright when active, muted otherwise) follows the same pattern used by every other nav link in the Layout component.

How Data Arrays Work

Web Weaver’s existing pages are driven by data arrays defined near the top of assets/main.js. Each array holds plain JavaScript objects that the corresponding page component maps over to render cards, lists, and sections. To populate a page, you edit the array — no template logic changes required. The six core arrays are:
VariablePageContent type
MProjectsPortfolio project cards
TAboutSkills wheel entries
xAboutWork history timeline
FProjectsCase study entries
EBlogBlog post previews
qAboutTestimonial cards

Extending Existing Pages

Projects — array M

Each project entry controls a card in the Projects grid. Add a new object to the M array:
assets/main.js
// The M (projects) array
const M = [
  // ...existing entries...
  {
    id:          "hex-engine",
    title:       "Hex Engine",
    phase:       "full",          // moon phase — see values below
    description: "A declarative rule engine for evaluating complex boolean logic as arcane sigils.",
    tech:        ["TypeScript", "React", "Zod"],
  },
];
Moon phase values control the phase icon rendered on each project card:
"new" — New moon (dark circle). Use for projects in early ideation or not yet started.

Skills — array T

Each skill entry places a node on the radial skills wheel. The angle value (0–360) controls where on the wheel the node appears:
assets/main.js
const T = [
  // ...existing entries...
  { name: "GraphQL",  level: 80,  angle: 210 },
  { name: "Rust",     level: 45,  angle: 270 },
];
level is an integer from 0–100 that controls the node’s distance from the wheel’s centre and its visual weight.

Work History — array x

assets/main.js
const x = [
  // ...existing entries...
  {
    id:          "arcane-labs",
    title:       "Senior Frontend Engineer",
    company:     "Arcane Labs",
    period:      "2022 – Present",
    description: "Led the redesign of the core dashboard, reducing TTI by 40% through route-based code splitting.",
    isLit:       true,   // true = candle is lit (highlighted role); false = unlit
  },
];

Blog Posts — array E

Each blog post entry renders a Tarot-card-styled preview on the Blog page. The suit and number fields control the Tarot card designation displayed in the corner:
assets/main.js
const E = [
  // ...existing entries...
  {
    id:      "the-art-of-memoization",
    title:   "The Art of Memoization",
    excerpt: "Caching the results of pure functions — a spell that trades memory for speed.",
    suit:    "swords",   // tarot suit — see values below
    number:  7,
  },
];
Tarot suit values for blog posts:

wands

Fire energy — used for posts about building, creating, ambition, and new projects.

swords

Air energy — used for posts about logic, debugging, architecture decisions, and sharp thinking.

cups

Water energy — used for posts about UX, design, emotion, and human-centred work.

pentacles

Earth energy — used for posts about performance, infrastructure, tooling, and practical craft.

Testimonials — array q

assets/main.js
const q = [
  // ...existing entries...
  {
    type:   "raven",   // familiar type — see values below
    quote:  "Morgan conjured a design system from thin air in under a week. Genuinely magical.",
    author: "Sylvia Crane",
    role:   "CTO, Nightshade Digital",
  },
];
Familiar type values set the spirit-animal icon rendered beside each testimonial:
ValueFamiliarVibe
"cat"Black catMysterious, independent collaborator
"raven"RavenWise, perceptive, high praise
"owl"OwlThoughtful, detail-oriented feedback
"toad"ToadEarthy, practical, grounded endorsement
"bat"BatEnergetic, fast-moving, startup context

Case Studies — array F

assets/main.js
const F = [
  // ...existing entries...
  {
    id:      "ritual-redesign",
    title:   "The Ritual Redesign",
    summary: "How we cut checkout abandonment by 28% through progressive disclosure.",
    details: "Full narrative of the discovery, design, and engineering process...",
  },
];

Personalising Hero Text

The hero name and tagline are plain string literals inside the HeroSection function in main.js. Search for "Morgan Weaver" and "Software Sorcery & Digital Hexes" to locate and replace them:
assets/main.js
// Inside HeroSection()
const heroName    = "Morgan Weaver";             // ← your name here
const heroTagline = "Software Sorcery & Digital Hexes"; // ← your tagline here
After editing main.js, run npm run build to regenerate the dist/ folder before deploying. During local development with npm run dev, Vite’s HMR will hot-reload changes to data arrays immediately without a full page refresh.

Build docs developers (and LLMs) love