Skip to main content

Documentation Index

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

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

Spooky Developer is a single-file React SPA — almost all page components, data arrays, and routing live together in assets/main.js, which makes the project very approachable to extend. Whether you want to add a Services page, wire up a new scare effect, or swap the Halloween palette for something more cyberpunk, the changes are localised and follow consistent patterns throughout the codebase.
All personal content — timeline entries, projects, skills, work history, testimonials, article titles, and case studies — lives as constant arrays at the top of assets/main.js. Updating what visitors see means editing those arrays directly; there is no CMS or external data layer.

Adding a New Page

1

Create the page component

Define a new React component in assets/main.js (or in its own file if you prefer to keep things tidy). Follow the same structural conventions used by existing pages — a wrapping <div> with py-12 and a centred <h1> in text-haunt-moon.
// 1. Page component (add near the other page components in main.js)
const ServicesPage = () => (
  <div className="py-12 max-w-4xl mx-auto">
    <h1 className="text-5xl text-haunt-moon mb-8 text-center">Services</h1>
    <p className="text-haunt-bone/80">Your services content here.</p>
  </div>
);
2

Add a Route inside the App component

The App function in main.js renders a <Routes> block nested inside <Layout>. Add your new <Route> alongside the existing ones:
// 2. Add to Routes (in the App function)
<Route path="services" element={<ServicesPage />} />
All routes are children of the root path="/" route that mounts <Layout>, so your page automatically inherits the sticky header, footer, background, and all three scare components.
3

Add the path and label to the Nav LINKS array

Open Nav.js and find the LINKS array. Append an object with path and label:
// 3. Add to Nav LINKS array (in Nav.js)
{ path: '/services', label: 'Services' }
The Nav component iterates over LINKS to render both the desktop navigation bar and the mobile menu, so one change covers both breakpoints.

Adding a New Scare Effect

All three existing scare effects (CursorTrail, SpiderScare, IdleGhost) share the same four-step pattern. Follow it to keep your new effect consistent and respecting isHaunted:
  1. Import useHaunt and gate everything behind isHaunted
  2. Set up your trigger (a timer, a DOM event, a scroll listener, etc.)
  3. Animate entry and exit with AnimatePresence + m.div
  4. Mount the component once inside Layout.js
import { useState, useEffect } from 'react';
import { AnimatePresence } from './assets/index';
import { m } from './assets/proxy';
import { useHaunt } from './assets/HauntContext';

const MyScare = () => {
  const { isHaunted } = useHaunt();
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    if (!isHaunted) {
      setVisible(false);
      return;
    }
    // Your trigger logic here — e.g. a scroll listener or a timer
  }, [isHaunted]);

  return (
    <AnimatePresence>
      {visible && (
        <m.div
          className="fixed top-0 left-0 pointer-events-none z-[9999]"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
        >
          {/* Your scare content */}
        </m.div>
      )}
    </AnimatePresence>
  );
};
Once the component is ready, import and render it in Layout.js alongside the existing scares:
// In Layout.js
import { CursorTrail } from '../scares/CursorTrail';
import { SpiderScare } from '../scares/SpiderScare';
import { IdleGhost }   from '../scares/IdleGhost';
import { MyScare }     from '../scares/MyScare'; // your new effect

// Inside the return:
<CursorTrail />
<SpiderScare />
<IdleGhost />
<MyScare />
Keep pointer-events-none on all scare overlays so they never block clicks on the underlying page content. Use high z-index values (e.g. z-[9999]) consistently with the existing scares to avoid stacking-order surprises.

Customizing the Color Scheme

Spooky Developer’s Halloween palette is expressed through Tailwind utility classes (text-haunt-moon, bg-haunt-pumpkin, border-haunt-tombstoneDark, etc.) and a small set of base styles on body. Changing the overall mood is a two-step process:
1

Update the base body styles

Open assets/main.css and override the background and default text colours:
/* In assets/main.css */
body {
  background-color: #0a0a1a; /* deep navy instead of teal */
  color: #e0e0ff;            /* lavender instead of bone */
}
2

Remap the Tailwind theme tokens (source builds only)

In tailwind.config.js, update the haunt-* colour values under theme.extend.colors to match your new palette. Every class that references a haunt-* token will update across the entire app automatically after a rebuild.
Note: The deployed site is a pre-built Vite output — there is no tailwind.config.js in the distributed files. This step applies when building from the original source project. In the compiled output, token values are baked into assets/main.css and require a full source rebuild to change.
// tailwind.config.js (source project only)
theme: {
  extend: {
    colors: {
      'haunt-moon':          '#a78bfa', // violet instead of teal
      'haunt-pumpkin':       '#f472b6', // pink instead of orange
      'haunt-bg':            '#0a0a1a', // deep navy
      'haunt-bone':          '#e0e0ff', // lavender
      'haunt-dark':          '#1e1b4b',
      'haunt-tombstone':     '#312e81',
      'haunt-tombstoneDark': '#1e1b4b',
    },
  },
},

Changing the IdleGhost Timer

The ghost that slides in from the right edge of the screen waits for 45 seconds of user inactivity before appearing. To shorten or lengthen this delay, edit the setTimeout call in IdleGhost.js:
// In components/scares/IdleGhost.js
// Change 45000 to your desired milliseconds:
timer = setTimeout(() => setVisible(true), 10000); // 10 seconds
The activity listeners (mousemove, keydown, click, scroll) always reset this timer back to zero, so the ghost will only appear during genuine idle periods regardless of the threshold you choose.
Spooky Developer is a client-side SPA with no backend. The contact form (ContactPage) uses a simulated 1.5-second setTimeout to mimic submission — no data is ever sent anywhere. To handle real enquiries, replace the setTimeout block in the handleSubmit function with a call to a service such as Formspree, EmailJS, or your own API endpoint.

Build docs developers (and LLMs) love