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.

Every route change in the portfolio triggers a full-screen cinematic wipe animation before the incoming page fades in. These animations are orchestrated by the PageTransition component, which composes a TransitionOverlay (the wipe panels) with a content wrapper that fades children from invisible to visible once the wipe has cleared. Four distinct visual styles — each referencing a different Persona 3 UI aesthetic — are available through a single variant prop.

Using PageTransition

Wrap any page component in <PageTransition> inside your route definitions. The variant prop selects the wipe style; omitting it defaults to "default".
// Default wipe (navy → blue → sky panels)
<PageTransition>
  <MenuScreen />
</PageTransition>

// Diagonal angled panels for the About page
<PageTransition variant="about">
  <AboutMe />
</PageTransition>

// Vertical right-side stripes for the Socials page
<PageTransition variant="socials">
  <Socials />
</PageTransition>
TransitionOverlay switches between variants using a simple conditional:
function TransitionOverlay({ variant }) {
  if (variant === "about")   return <AboutTransition />;
  if (variant === "resume")  return <ResumeTransition />;
  if (variant === "socials") return <SocialsTransition />;
  return <DefaultTransition />;
}

The four transition variants

Default

Three full-screen horizontal panels — midnight navy #0d1a3a, electric blue #1a6aff, and sky blue #7dd4fc — expand from the left edge then retract to the right, staggered by 50 ms each.

About

Three diagonal parallelogram panels rotated −18deg slide in rapidly from the left side of the screen, then fade out while still moving, giving a slashing-sword feel.

Resume

Four horizontal card-shaped panels (#0f1760, #7ff6ff, #ffffff, #0f1760) sweep across from left to right and exit off the far edge, evoking a status-screen card flip.

Socials

Three vertical stripes on the right side of the viewport fall from above and exit downward, skewed by −16deg, resembling notification cards dropping into view.

Default transition

Three motion.div panels are rendered with position: fixed; inset: 0 so they cover the entire viewport. Each panel uses a keyframe array in the animate prop to expand from nothing, hold at full width, then collapse — all within 0.45 seconds. The times array pins each keyframe to a specific fraction of the total duration, and the cubic-bezier easing gives the motion a punchy feel.
const defaultBlocks = ["#0d1a3a", "#1a6aff", "#7dd4fc"]

function DefaultTransition() {
  return defaultBlocks.map((color, i) => (
    <motion.div
      key={i}
      style={{
        position: "fixed",
        inset: 0,
        background: color,
        zIndex: 999 - i,
        originX: 0,          // scale from the left edge
      }}
      initial={{ scaleX: 0 }}
      animate={{ scaleX: [0, 1, 1, 0] }}
      transition={{
        duration: 0.45,
        delay: i * 0.05,     // 0ms, 50ms, 100ms stagger
        times: [0, 0.4, 0.6, 1],
        ease: [0.76, 0, 0.24, 1],
      }}
    />
  ))
}
The originX: 0 style is critical — without it the panel would scale from its center instead of the left edge, breaking the wipe illusion.

About transition

Three rotated panels fly in from x: -500 to near-zero, then fade to transparent while decelerating. The rotate(-18deg) and parallelogram clip-path create the diagonal blade aesthetic.
const panels = [
  { color: "#00184c", top: "-12vh", left: "-18vw", width: "86vw", delay: 0 },
  { color: "#53edff", top: "24vh",  left: "-10vw", width: "72vw", delay: 0.05 },
  { color: "#ffffff", top: "58vh",  left: "-14vw", width: "82vw", delay: 0.1 },
]

// Each panel:
initial={{ x: -500, opacity: 0 }}
animate={{ x: [-500, 20, 0], opacity: [1, 1, 0] }}
transition={{ duration: 0.52, delay: panel.delay, times: [0, 0.68, 1], ease: [0.22, 1, 0.36, 1] }}
// clip-path: "polygon(0 0, 100% 0, calc(100% - 120px) 100%, 0 100%)"
// transform: "rotate(-18deg)"

Resume transition

Four card panels sweep left-to-right with a parallelogram clip-path (polygon(0 0, 97% 0, 100% 100%, 3% 100%)). Each card travels from x: -900 across the screen and exits at x: 900, staggered by 50 ms. The white panel gets an extra box-shadow of 10px 0 0 #d63232 for the signature red accent.
const cards = [
  { top: "14vh", color: "#0f1760", delay: 0 },
  { top: "31vh", color: "#7ff6ff", delay: 0.05 },
  { top: "48vh", color: "#ffffff", delay: 0.1 },
  { top: "65vh", color: "#0f1760", delay: 0.15 },
]

// Each card:
initial={{ x: -900, opacity: 1 }}
animate={{ x: [-900, 30, 0, 900] }}
transition={{ duration: 0.6, times: [0, 0.48, 0.7, 1], ease: [0.76, 0, 0.24, 1] }}

Socials transition

Three vertical stripes are anchored to the right side of the screen (left: 72vw / 80vw / 88vw) and skewed with skewX(-16deg). They fall from y: -1200 into position and then exit to y: 1200, each staggered by 60 ms.
const stripes = [
  { color: "#00184c", left: "72vw", width: "24vw", delay: 0 },
  { color: "#00dff7", left: "80vw", width: "14vw", delay: 0.06 },
  { color: "#ffffff", left: "88vw", width:  "8vw", delay: 0.12 },
]

// Each stripe:
initial={{ y: -1200, opacity: 1 }}
animate={{ y: [-1200, 0, 0, 1200] }}
transition={{ duration: 0.56, times: [0, 0.42, 0.58, 1], ease: [0.76, 0, 0.24, 1] }}

Children fade-in

After the wipe panels animate, the page content needs to appear. PageTransition wraps children in its own motion.div with a short opacity fade that starts slightly after the wipe begins:
export default function PageTransition({ children, variant = "default" }) {
  const location = useLocation()
  return (
    <AnimatePresence mode="wait">
      <motion.div key={location.pathname} style={{ position: "relative" }}>
        <TransitionOverlay variant={variant} />
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.2, delay: 0.18 }}
        >
          {children}
        </motion.div>
      </motion.div>
    </AnimatePresence>
  )
}
The 0.18s delay gives the wipe panels enough time to sweep into view before the new content becomes visible underneath them, preventing any layout flash.
To add a new transition variant, create a new component (e.g. ProjectsTransition) that returns an array of motion.div elements with your keyframe arrays, then add an if (variant === "projects") branch inside TransitionOverlay and pass variant="projects" to the <PageTransition> wrapping your new route. No other files need to change.

Build docs developers (and LLMs) love