Skip to main content

Documentation Index

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

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

Framer Motion’s motion.* components are the engine behind every animated moment in Full Moon — from the moon slowly rising above the horizon to letters crystallising out of a blur, from a perfectly-tracked custom cursor to sigil lines drawing themselves across the screen. This page catalogues every animation pattern in the project, including the exact easing curves, durations, and prop shapes used, so you can extend or replicate them consistently.

Page Transitions

Every page in Full Moon is wrapped in a <motion.main> element that fades in on mount and fades out on unmount. This gives every route change a smooth 0.5-second cross-dissolve rather than an instant swap.
<motion.main
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.5 }}
  className="relative z-10 min-h-screen flex flex-col"
>
  <Outlet />
</motion.main>
The exit prop only fires when the component is removed from the React tree. For this to work, the router must be wrapped in Framer Motion’s <AnimatePresence> component. Without it, exit animations are silently ignored and the page disappears immediately on navigation.
The <Outlet /> renders the active route’s content. Because the wrapper is a motion.main, all child content inherits the fade — no per-page setup is required for basic transition coverage.

Home Page Entrance Sequence

The home page runs a four-step staggered entrance that plays out over roughly four seconds. Each element is timed to appear after the previous one has established itself, building a sense of ceremony.

Step 1 — Moon rises from below

A large MoonSVG is wrapped in a motion.div that translates up from 100 px below its resting position while fading in:
<motion.div
  initial={{ y: 100, opacity: 0 }}
  animate={{ y: 0, opacity: 1 }}
  transition={{ duration: 2, ease: "easeOut" }}
>
  <MoonSVG phase="full" className="w-64 h-64 text-witch-teal-dark" />
</motion.div>
The easeOut curve means the moon decelerates as it settles into position, mimicking a real celestial rise.

Step 2 — Title letters blur in (per-character stagger)

Each character of the heading is wrapped in its own motion.span. A blur(10px) CSS filter creates a ghostly materialisation effect as each letter sharpens into focus:
{"The Developer".split("").map((char, index) => (
  <motion.span
    key={index}
    initial={{ opacity: 0, filter: "blur(10px)" }}
    animate={{ opacity: 1, filter: "blur(0px)" }}
    transition={{ delay: index * 0.15, duration: 0.8 }}
    className="inline-block"
  >
    {char === " " ? "\u00A0" : char}
  </motion.span>
))}
Each character is delayed by index × 0.15s, so the first letter starts at 0s and the last letter (index 12) starts at 1.8s. Space characters are replaced with a non-breaking space (\u00A0) to preserve word spacing — without this, inline-block spans collapse whitespace.

Step 3 — Subtitle fades up

Once the title is nearly complete, the subtitle paragraph fades up from 20 px below:
<motion.p
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ delay: 2.5, duration: 1 }}
>
  I cast `useEffect()` under the full moon.
</motion.p>

Step 4 — CTA button appears

Finally, the call-to-action button fades in with no vertical movement — a pure opacity transition at delay 3.5s:
<motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  transition={{ delay: 3.5, duration: 1 }}
>
  <a href="/about">Cross the Threshold</a>
</motion.div>
The full entrance sequence runs from 0s to ~4.5s. If you add new elements to the home page, continue the delay progression in 0.5–1s increments to keep the ceremony feeling intentional rather than simultaneous.

SVG Animations

Two of the three SVG components include Framer Motion-driven animations. The table below summarises each:
ComponentAnimationDurationTrigger
CandleSVG flameScale / rotate / opacity keyframes0.5s, repeat: InfinityAlways active when lit={true}
SigilSVG drawpathLength 0 → 12s easeInOutOn mount when draw={true}
MoonSVGNone (static SVG with CSS glow filter)
See the SVG Components page for the full prop reference and code for each component.

Cursor Animations

Full Moon replaces the system cursor with a three-layer custom cursor, each layer using a different motion profile to create depth and personality.

Layer 1 — Glow orb (lags behind)

A large semi-transparent orb that trails the cursor slightly, giving the impression of a luminous wake:
<motion.div
  className="absolute w-4 h-4 rounded-full bg-witch-teal-glow mix-blend-screen blur-[2px] pointer-events-none"
  animate={{ x: t.x - 8, y: t.y - 8 }}
  transition={{ type: "tween", ease: "backOut", duration: 0.1 }}
/>
The backOut easing causes the orb to overshoot the target position slightly before settling, adding a springy organic feel even though the transition is a fixed-duration tween rather than a true spring.

Layer 2 — Precision dot (instant)

A small solid dot that tracks the exact pointer position with zero lag — this is the functional cursor replacement:
<motion.div
  className="absolute w-1 h-1 rounded-full bg-white pointer-events-none"
  animate={{ x: t.x, y: t.y }}
  transition={{ type: "tween", ease: "linear", duration: 0 }}
/>
duration: 0 makes the transition instantaneous, so the dot is always exactly under the pointer.

Layer 3 — Particles (burst on interaction)

Small particles that spawn at the cursor position and drift upward while fading and expanding. Each particle uses a unique random y and x offset:
<motion.div
  className="absolute w-3 h-3 rounded-full bg-witch-teal-dark mix-blend-screen blur-md pointer-events-none"
  initial={{ opacity: 0.6, scale: 1 }}
  animate={{
    opacity: 0,
    scale: 3,
    y: -30,   // randomised between -20 and -40
    x: 5      // randomised between -10 and +10
  }}
  transition={{ duration: 1, ease: "easeOut" }}
/>
Particles animate from opacity: 0.6, scale: 1 to fully transparent and three times their original size while drifting upward, creating a dissolving sparkle trail.
The glow orb uses mix-blend-screen so it composites additively against dark backgrounds — on a near-black canvas this makes the teal glow pop brightly without rendering an opaque circle that obscures page content. Make sure the cursor container has pointer-events-none set or it will intercept clicks on every element beneath it.

Build docs developers (and LLMs) love