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.

Overview

Layout is the root shell component rendered by React Router’s <Outlet>. Every page in the portfolio lives inside it. It composes five key features:
  1. Fixed navigation header — sits above all content with a mix-blend-difference blend mode
  2. Framer Motion animated nav underline — a shared layout indicator that slides between active links
  3. DyeDropCursor — the custom cursor trail, always mounted at z-[100]
  4. AnimatePresence page transitions — blur-fade animations keyed on the current route path
  5. Grain texture overlay — a subtle SVG noise layer fixed above all content

The nav header is fixed top-0 left-0 right-0 z-50 with mix-blend-difference text-white. This blend mode makes the header text and logo visually invert against any background color beneath it — so they stay legible on both light and dark surfaces without any color switching logic. Navigation items are defined in the Ip array in Layout.js:
const navLinks = [
  { path: "/",        label: "Home" },
  { path: "/about",   label: "About" },
  { path: "/projects",label: "Projects" },
  { path: "/skills",  label: "Skills" },
  { path: "/work",    label: "Work" },
  { path: "/contact", label: "Contact" },
];
Each item is rendered as a React Router NavLink. When a link is active, a motion.div underline is injected using Framer Motion’s shared layout system:
// Active nav underline — slides smoothly between links
<motion.div
  layoutId="nav-indicator"
  className="absolute -bottom-1 left-0 right-0 h-0.5 bg-dye-light rounded-full"
  initial={false}
  transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
The single layoutId="nav-indicator" means Framer Motion tracks the element across nav items and animates its position automatically whenever the active route changes.
The mix-blend-difference technique on the header means nav text appears to invert against whatever background color sits below it. On a dark background the text appears white; on a light background it flips to dark. This means the navigation is always readable without any explicit color-switching logic — but it also means the header’s text color is entirely determined by the page background, not by a CSS variable or theme setting.

DyeDropCursor

DyeDropCursor is rendered unconditionally at the top of the Layout JSX, outside the main content area:
<DyeDropCursor />
It is pointer-events-none fixed inset-0 z-[100] overflow-hidden, so it floats above everything — including the grain overlay — without blocking any clicks or touch events. See the DyeDropCursor docs for full details.

Page Transitions

Pages are wrapped in AnimatePresence mode="wait" so the exiting page fully finishes its animation before the entering page begins. Each page transition is a motion.div keyed on location.pathname:
<AnimatePresence mode="wait">
  <motion.div
    key={location.pathname}
    initial={{ opacity: 0, filter: 'blur(10px)' }}
    animate={{ opacity: 1, filter: 'blur(0px)' }}
    exit={{ opacity: 0, filter: 'blur(10px)' }}
    transition={{ duration: 0.5 }}
    className="h-full"
  >
    <Outlet />
  </motion.div>
</AnimatePresence>
The blur effect gives the transition a soft, ink-diffusing quality that matches the overall tie-dye aesthetic.

Grain Texture Overlay

A div is fixed over the entire viewport to add a tactile noise texture:
<div
  className="fixed inset-0 pointer-events-none z-40 opacity-[0.03] mix-blend-overlay"
  style={{
    backgroundImage: `url("data:image/svg+xml,<svg ...feTurbulence .../svg>")`,
  }}
/>
Key properties:
  • opacity-[0.03] — nearly invisible; adds texture without obscuring content
  • mix-blend-overlay — the noise interacts with the colours beneath it
  • pointer-events-none — completely non-interactive
  • z-40 — sits above page content but below the navigation header (z-50) and cursor (z-[100])

Customization

Changing navigation items

Edit the navLinks array (named Ip in the compiled source) at the top of Layout.js. Add, remove, or reorder objects — the nav renders the array directly:
// Add a blog link
{ path: "/blog", label: "Blog" },

Adjusting transition timing

Change the duration value on the motion.div transition inside AnimatePresence:
// Faster transitions
transition={{ duration: 0.25 }}

// Slower, more dramatic transitions
transition={{ duration: 0.8 }}

Disabling the grain overlay

Remove or comment out the <div> with opacity-[0.03] mix-blend-overlay. The rest of Layout is unaffected:
{/* Grain overlay — remove this block to disable */}
{/*
<div
  className="fixed inset-0 pointer-events-none z-40 opacity-[0.03] mix-blend-overlay"
  style={{ backgroundImage: `url(...)` }}
/>
*/}
To keep the overlay but reduce its visibility further, lower opacity-[0.03] to something like opacity-[0.015].

Build docs developers (and LLMs) love