Skip to main content

Documentation Index

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

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

The PageTransition component is a thin Framer Motion wrapper that gives every page in the app a polished enter and exit animation. When a route changes, the outgoing page scales up slightly and blurs out while the incoming page fades in from a slightly reduced scale through a blur, creating a soft, cinematic transition that complements the arcade aesthetic without feeling sluggish. It also applies the standard page-level layout classes — max-width, padding, and minimum height — so individual page components don’t need to repeat that boilerplate.

Props

children
ReactNode
required
The page content to render inside the transition wrapper. Typically the full JSX tree of a route-level component.

Usage

import PageTransition from "./components/PageTransition";

// Wrap each route's top-level return value
export default function ProjectsPage() {
  return (
    <PageTransition>
      <h1 className="font-pixel text-lime">LEVEL SELECT</h1>
      {/* page content */}
    </PageTransition>
  );
}
For exit animations to fire correctly, PageTransition must be used inside a Framer Motion AnimatePresence component that is keyed by the current route. Typically this lives in the router outlet or App.jsx.
import { AnimatePresence } from "framer-motion";
import { useLocation, Routes, Route } from "react-router-dom";

export default function App() {
  const location = useLocation();

  return (
    <AnimatePresence mode="wait">
      <Routes location={location} key={location.pathname}>
        <Route path="/"        element={<TitleScreen />} />
        <Route path="/about"   element={<About />} />
        <Route path="/projects" element={<Projects />} />
        {/* ... */}
      </Routes>
    </AnimatePresence>
  );
}

Animation Specification

The transition is defined by three Framer Motion states:
Stateopacityscalefilter
initial (before enter)00.95blur(10px)
animate (visible)11blur(0px)
exit (before unmount)01.05blur(10px)
<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.4, ease: "easeOut" }}
>
Enter: The page starts slightly shrunken and blurred, then eases out to full size and clarity. The easeOut curve means the motion decelerates as it settles, giving a satisfying “snap into place” feel. Exit: The page expands fractionally (scale 1.05) and blurs simultaneously, implying the outgoing content is receding or dissolving — a cinematic wipe-out without hard cuts. Duration: 0.4 seconds total. Fast enough to not impede navigation but long enough to register as a deliberate transition.

Layout Classes

Beyond the animation, PageTransition applies a fixed set of Tailwind classes to the motion.div that establish the standard page canvas:
min-h-screen w-full relative z-10 pt-24 pb-32 px-4 md:px-8 max-w-7xl mx-auto
ClassPurpose
min-h-screenEnsures the page fills the full viewport height even with sparse content
w-fullFull available width inside the max-width container
relativeEstablishes a positioning context for absolutely-placed children (e.g., GhostSprite)
z-10Sits above background layers (z-0 ghosts) but below the HUD (z-50) and Navigation modal (z-[100])
pt-24Clears the fixed HUD at the top of the viewport
pb-32Clears the fixed Navigation button at the bottom of the viewport
px-4 md:px-8Responsive horizontal gutters
max-w-7xl mx-autoCenters content with a maximum width of 80 rem

Full Implementation

import { motion } from "framer-motion";

export default function PageTransition({ children }) {
  return (
    <motion.div
      className="min-h-screen w-full relative z-10 pt-24 pb-32 px-4 md:px-8 max-w-7xl mx-auto"
      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.4, ease: "easeOut" }}
    >
      {children}
    </motion.div>
  );
}
Use AnimatePresence mode="wait" (not mode="sync") so the exiting page fully unmounts before the entering page starts its animation. mode="sync" causes both animations to run simultaneously, which produces a distracting double-blur overlap.
The CSS filter: blur() property triggers GPU compositing. On pages with many elements, this is negligible. However, if you apply additional CSS filters to elements inside PageTransition (e.g., drop-shadow on many GhostSprite instances), monitor for paint jank on lower-end devices.

Where It’s Used

Every route-level page component in the app wraps its top-level return in PageTransition. This ensures consistent enter/exit behavior across all seven routes without duplicating animation logic or layout boilerplate in each page file.

Build docs developers (and LLMs) love