Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/blairxu13/persona3-website/llms.txt

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

PageTransition is a Framer Motion wrapper component that intercepts React Router v7 route changes and plays a themed overlay animation before revealing the incoming page. It wraps AnimatePresence with mode="wait" so the outgoing page fully unmounts before the new one mounts, preventing two pages from rendering simultaneously during a transition. Four built-in overlay variants — default, about, resume, and socials — each express a distinct visual language tied to the page they introduce, from sweeping diagonal panels to cascading vertical stripes.

Props

children
ReactNode
required
The page content to render inside the transition wrapper. This content fades in after the overlay animation completes, using a short opacity tween delayed by 0.18 s.
variant
'default' | 'about' | 'resume' | 'socials'
default:"default"
Selects which overlay animation plays when the route changes. Each variant uses a different set of coloured panels, directions, and easing curves. Defaults to "default" if omitted.

Usage

Wrap each page component in PageTransition at the route level, passing the variant that matches the page being rendered:
<PageTransition variant="about">
  <AboutMe />
</PageTransition>
Nest PageTransition inside a route element so that useLocation — which PageTransition calls internally — is within the router context. The location.pathname is used as the key on the outermost motion.div, which is what signals AnimatePresence to run exit and enter animations on every navigation.

Variants

Each variant is implemented as a separate inner component that renders a small set of motion.div elements — coloured panels positioned with position: fixed — which animate in and out to form the transition effect.
The default transition sweeps three full-screen panels (#0d1a3a, #1a6aff, #7dd4fc) across the viewport from left to right using scaleX. Each panel originates from the left edge (originX: 0) and plays a four-keyframe sequence: collapsed → full → full → collapsed.
const defaultBlocks = ["#0d1a3a", "#1a6aff", "#7dd4fc"];

// Per panel:
initial={{ scaleX: 0 }}
animate={{ scaleX: [0, 1, 1, 0] }}
transition={{
  duration: 0.45,
  delay: i * 0.05,          // 0ms, 50ms, 100ms
  times: [0, 0.4, 0.6, 1],
  ease: [0.76, 0, 0.24, 1], // sharp deceleration
}}
Visual effect: Three stacked full-screen layers wipe in from the left edge and retract in the same direction, revealing the new page beneath. The 50 ms stagger between layers creates a layered depth effect.

Children Fade

Regardless of which overlay variant is active, the page content (children) is always wrapped in a motion.div that performs a simple opacity tween:
<motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.2, delay: 0.18 }}
>
  {children}
</motion.div>
The 0.18 s delay ensures the overlay panels have had time to begin clearing the viewport before the page content starts to appear, preventing a visual clash between the incoming content and the still-animating overlay. The exit opacity tween fires when the route changes and AnimatePresence signals the current page to leave.

Adding a New Variant

Follow these steps to create a custom transition for a new page, such as a "contact" page.
1

Write a transition component

Create a new inner component in PageTransition.jsx that returns one or more motion.div elements. Model it on the existing variants, using position: fixed and high zIndex values so the panels cover the page:
function ContactTransition() {
  const blocks = [
    { color: "#1a0033", delay: 0 },
    { color: "#9b00ff", delay: 0.06 },
    { color: "#ffffff", delay: 0.12 },
  ];
  return blocks.map((block, i) => (
    <motion.div
      key={i}
      style={{
        position: "fixed",
        inset: 0,
        background: block.color,
        zIndex: 999 - i,
        originY: 0,
      }}
      initial={{ scaleY: 0 }}
      animate={{ scaleY: [0, 1, 1, 0] }}
      transition={{
        duration: 0.5,
        delay: block.delay,
        times: [0, 0.4, 0.6, 1],
        ease: [0.76, 0, 0.24, 1],
      }}
    />
  ));
}
2

Register the variant in TransitionOverlay

Add a new conditional branch inside the TransitionOverlay function so the router knows when to use your component:
function TransitionOverlay({ variant }) {
  if (variant === "about")   return <AboutTransition />;
  if (variant === "resume")  return <ResumeTransition />;
  if (variant === "socials") return <SocialsTransition />;
  if (variant === "contact") return <ContactTransition />; // add this
  return <DefaultTransition />;
}
3

Use the variant on your route

Pass the new variant string to PageTransition wherever the new page is rendered:
<PageTransition variant="contact">
  <Contact />
</PageTransition>
AnimatePresence must be configured with mode="wait" in the component that wraps your routes (typically App.jsx) for transitions to work correctly. Without mode="wait", the incoming and outgoing pages mount simultaneously, causing overlay panels from both transitions to render on top of each other. The PageTransition component itself already uses mode="wait" internally, but the outer application shell must not introduce a second AnimatePresence without the same setting.

Page Transitions Concept

Conceptual overview of how AnimatePresence and location keys drive the transition system.

Animation System

Full reference for the Framer Motion patterns used across the portfolio.

Build docs developers (and LLMs) love