Skip to main content

Documentation Index

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

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

PageTransition is the invisible glue that makes navigating between routes feel like a cinematic cut rather than an abrupt page swap. It wraps every page component in a Framer Motion motion.div that simultaneously fades, scales, and blurs its way in on mount — and reverses the sequence smoothly on unmount before the next page enters. The result is a polished, space-travel-inspired transition that reinforces the portfolio’s atmosphere with every navigation.

Usage

Every page component in the portfolio wraps its root element in <PageTransition>. The component passes children straight through, so it has no impact on layout or styling.
import { P as PageTransition } from '../components/cosmos/PageTransition'

export function MyPage() {
  return (
    <PageTransition>
      <div className="flex-1 flex flex-col">
        {/* page content */}
      </div>
    </PageTransition>
  )
}
PageTransition itself renders a motion.div with built-in padding (pt-24 pb-20 px-6 md:px-12 lg:px-24) and min-h-screen w-full flex flex-col, so every page gets consistent spacing and fills the viewport automatically.

How It Works

The animation is driven by three Framer Motion props on the inner motion.div:
<motion.div
  initial={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
  animate={{ opacity: 1, scale: 1,    filter: 'blur(0px)'  }}
  exit={{    opacity: 0, scale: 1.05, filter: 'blur(10px)' }}
  transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
>
  {children}
</motion.div>
LifecycleOpacityScaleBlur
Enter (initial → animate)0 → 10.95 → 110px → 0px
Exit (animate → exit)1 → 01 → 1.050px → 10px
The entry scale starts slightly small (0.95) and grows to full size, giving the impression the page is zooming gently into view from a distance. The exit scale overshoots to 1.05 — the page appears to expand and dissolve away, like a warp jump. The easing curve [0.22, 1, 0.36, 1] is a custom cubic bezier that decelerates sharply at the end of the animation, making the settle feel weighted and physical rather than mechanical.

AnimatePresence Integration

PageTransition only animates on exit when it is a direct child of a Framer Motion <AnimatePresence> component. In the Cosmic Developer app, this is wired up in the AppRoutes component:
import { AnimatePresence } from 'framer-motion'
import { Routes, Route, useLocation } from 'react-router-dom'

function AppRoutes() {
  const location = useLocation()

  return (
    <AnimatePresence mode="wait">
      <Routes location={location} key={location.pathname}>
        <Route path="/"          element={<HomePage />} />
        <Route path="/about"     element={<AboutPage />} />
        <Route path="/projects"  element={<ProjectsPage />} />
        {/* ... other routes */}
      </Routes>
    </AnimatePresence>
  )
}
1

location as key

Passing location.pathname as the key on <Routes> forces React to unmount the old route tree and mount the new one as a completely fresh component — rather than reconciling them in place. This gives Framer Motion the unmount event it needs to play the exit animation.
2

mode="wait"

mode="wait" on <AnimatePresence> tells Framer Motion to fully complete the exit animation of the departing page before beginning the enter animation of the arriving page. Without this, both transitions would play simultaneously and overlap visually.
3

AnimatePresence detects unmount

When the route changes, React unmounts the old page component (which contains a <PageTransition>). AnimatePresence intercepts this unmount, plays the exit variant, and only removes the DOM node after duration: 0.6 seconds have elapsed.
mode="wait" means the total navigation time before the new page appears is slightly over 0.6 s (the exit duration). For pages with heavy content, this is barely perceptible — but if you want snappier transitions, reduce duration in PageTransition.js to 0.3 or 0.4.

Customization

All transition values are co-located in PageTransition.js. Common modifications:

Change duration

Edit transition.duration (currently 0.6). A value of 0.3 produces a snappy cut-style transition; 1.0 creates a slow, dramatic wipe.

Add vertical movement

Extend initial and exit with a y value to add a vertical slide:
initial={{ opacity: 0, scale: 0.95, y: 20, filter: 'blur(10px)' }}
exit={{    opacity: 0, scale: 1.05, y: -20, filter: 'blur(10px)' }}

Change easing

Replace the custom cubic bezier [0.22, 1, 0.36, 1] with a named Framer Motion ease such as "easeInOut" or "circOut" for a different deceleration feel.

Skip blur

Remove the filter keys from initial, animate, and exit if the blur effect is too heavy on low-powered devices. Opacity + scale alone still produces a clean transition.
To change the transition duration or add y-axis movement, modify only the initial, animate, exit, and transition props inside PageTransition.js. You do not need to touch any individual page component — the change propagates everywhere automatically since every page renders through the same wrapper.

Build docs developers (and LLMs) love