The Craft is a living grimoire — nothing is ever quite still. Motion is layered into the experience at two levels: a JavaScript animation layer driven by Framer Motion handles interactive and entrance effects that respond to user behavior, while a CSS keyframe layer runs continuously in the background, keeping ambient elements like candle flames, glowing rings, and rotating orbs perpetually in motion. Understanding how both systems work — and where they intersect — gives you full control over the portfolio’s kinetic personality.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/the-craft/llms.txt
Use this file to discover all available pages before exploring further.
Two Animation Systems
Framer Motion
JavaScript-driven. Responds to state, user interaction, and route changes. Handles entrance sequences, hover micro-animations, page transitions, and
AnimatePresence-managed content swaps.CSS Keyframes
Declarative and ambient. Runs continuously in the background regardless of user interaction. Applied via utility classes like
.animate-flicker and .animate-spin-slow. Defined entirely in main.css..animate-spin-slow for its continuous rotation while Framer Motion handles its entrance opacity fade-in and hover scale response.
CSS Keyframe Animations
Four keyframe animations are defined inassets/main.css and exposed as utility classes. Apply them directly in JSX class names.
| Class | Keyframe name | Duration & easing | Purpose |
|---|---|---|---|
.animate-flicker | flicker | 3s, infinite alternate | Candle flame opacity and scale oscillation |
.animate-pulse | pulse | 2s, cubic-bezier(.4,0,.6,1) infinite | Generic opacity pulse to 50% |
.animate-pulse-glow | pulseGlow | 2s, cubic-bezier(.4,0,.6,1) infinite | Teal drop-shadow breathing between 10px and 4px |
.animate-spin-slow | spin | 20s, linear infinite | Full 360° slow rotation for ambient ring elements |
Keyframe definitions
Framer Motion Animation Patterns
Framer Motion is used for every animation that needs to respond to application state, route changes, or user input. The following patterns appear consistently throughout The Craft’s component tree.Page Entrance — Staggered Reveal
Content sections fade and slide up from below when a page mounts, with each item delayed by its position in the list.y: 50 initial offset gives a sense of items rising into place rather than simply fading in. Each additional item waits 0.2s longer, so a list of five items takes one full second to complete its cascade.
Page Transition — Blur Fade via PageTransition
Route changes are handled by a dedicated PageTransition wrapper component (components/PageTransition.js) that blurs and fades the outgoing page out before the incoming page blurs and fades in. The component is itself a motion.div — AnimatePresence is applied by the router outlet that wraps it.
ease: [0.22, 1, 0.36, 1] cubic-bezier curve produces a fast initial movement that eases into a gradual stop — the “custom ease out” feel that makes the transition feel intentional rather than mechanical. The 0.8s duration gives the blur enough time to resolve cleanly before the user begins reading the new page.
Card Hover — Scale and De-rotate
Project cards enter with a slight rotation applied via an inlinestyle prop (each card’s rotation value from the data array). On hover the card snaps to rotate: 0 alongside a gentle scale-up. Because the rotation is set on the style prop rather than a Framer initial value, the whileHover needs only to reset it explicitly to zero.
Pentagram Node — Scale and Pulse Ring
Navigation nodes on the pentagram diagram scale up on hover and emit an expanding ring that fades out.scale: 0 relative to its own enlarged size) and expands to scale: 2 while fading to fully transparent, creating a sonar-like ripple effect.
Ambient Blob — Scale and Opacity Breathing
Large blurred background orbs use a looping scale and opacity animation to give the dark background a sense of slow, living energy.[1, 1.05, 1] defines a three-keyframe sequence — Framer Motion interpolates between these values across the full duration. The blob expands 5 % and brightens to 80 % opacity before contracting back over 4 seconds.
Scrying Mirror — Rotation and Scale Pulse
The decorative scrying mirror element combines a full rotation with a scale pulse over a long 20-second cycle.AnimatePresence mode='wait'
Two distinct UI areas use AnimatePresence with mode="wait" to coordinate animated content swaps. In both cases AnimatePresence is imported from components/CursorTrail.js (which re-exports Framer Motion’s AnimatePresence alongside the cursor particle system).
Testimonials
Testimonial quotes swap in and out with
mode="wait" — the exiting quote fully disappears (opacity fade + blur) before the next one enters. The transition values on the inner motion.div use scale rather than y to give a zoom-in/zoom-out feel distinct from the page transition.Work History Panel
The detail panel for each work history entry uses
AnimatePresence mode="wait" so the outgoing role card fully fades and blurs out before the incoming one fades and slides in. The inner motion.div reuses the same blur(10px) + y pattern as PageTransition, keeping motion language consistent.Reducing Motion
The Craft does not currently include a built-inprefers-reduced-motion guard. Users who have enabled reduced motion in their OS accessibility settings will still see all animations. To respect this preference, add the following check wherever you define Framer Motion variants:
main.css: