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.

Dyed in the Wool is a single-page React portfolio bundled with Vite and deployed as a static site on GitHub Pages. Every visual layer — animated backgrounds, SVG displacement filters, cursor effects, and page transitions — is composed directly inside the React component tree, with no external state management and no backend.

Technology Stack

LayerTechnology
UI frameworkReact 18 (createRoot, StrictMode)
BundlerVite (ESM output: assets/main.js, assets/proxy.js, assets/main.css)
RoutingReact Router v6 — HashRouter for GitHub Pages compatibility
AnimationFramer Motion (motion.*, AnimatePresence, layoutId)
StylingTailwind CSS with custom dye-* color tokens
IconsLucide React v0.522.0 (ArrowRight, ExternalLink, Github, Send, X)
FontsGoogle Fonts — Caveat Brush (display) + DM Sans (body)

Project Structure

dyed-in-the-wool/
├── index.html                  # SPA shell — mounts <div id="root">
├── assets/
│   ├── main.js                 # Vite bundle: all routes, components, React Router
│   ├── proxy.js                # Shared chunk: React, ReactDOM, Framer Motion
│   └── main.css                # Compiled Tailwind output
├── components/
│   ├── DyeDropCursor.js        # Custom SVG cursor + AnimatePresence wrapper
│   ├── Layout.js               # App shell: header nav, grain overlay, page outlet
│   └── TieDyeBackground.js     # Procedural SVG background (spiral / bullseye / crumple)
└── pages/
    ├── About.html              # Static HTML stub for GitHub Pages direct-URL support
    ├── Contact.html
    ├── Projects.html
    ├── Skills.html
    └── Work.html

Component Hierarchy

The entire app renders inside a single HashRouter. The Layout component acts as the persistent shell — it renders on every route — while child route components mount and unmount inside <Outlet /> wrapped in AnimatePresence.
<HashRouter>
  <Layout>  (nav header + grain overlay + AnimatePresence)
    <DyeDropCursor />    (fixed, z-[100])
    <AnimatePresence mode="wait">
      <motion.div>       (page transition wrapper — keyed on location.pathname)
        <Outlet />       (active route component)
      </motion.div>
    </AnimatePresence>
  </Layout>
</HashRouter>
DyeDropCursor is also re-used as a standalone AnimatePresence host inside the Projects page to animate the project detail modal in and out.

Data Flow

Dyed in the Wool has no API calls and no state management library. All page data is defined as static JavaScript arrays co-located in assets/main.js:
  • Projects — array of { id, title, type, color, description, tags } objects rendered as cards and expanded into a full-screen modal via Framer Motion layoutId shared-layout animation.
  • Skills — array of { name, color, level } objects that power the dye-bottle skill meter on the Skills page.
  • Work history — array of { year, company, role, color, description } objects rendered in an alternating timeline on the Work page.
  • About copy — a plain string array of paragraphs, rendered sequentially with whileInView scroll-triggered animations.
  • Nav links — a static array [{ path, label }] defined in Layout.js and iterated to render NavLink components.
Local component state (useState) handles UI interactions: modal open/close state on Projects, selected skill on Skills, and contact form field values and submission status on Contact.

Explore Further

Client-Side Routing

How HashRouter, route definitions, NavLink active indicators, static HTML page stubs, and Framer Motion page transitions all fit together.

Tailwind & the Dye Color System

Custom dye-* color tokens, SVG displacement filters, mix-blend-mode layering, the grain overlay, and the animate-blob keyframe.

Build docs developers (and LLMs) love