Compare commits
2
Commits
572329097c
...
4df0d9ae86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4df0d9ae86 | ||
|
|
5c10bce740 |
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
||||
import { LayoutRouterContext } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useContext, useState, type ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* App Router swaps in the new page's content as soon as you navigate, so the
|
||||
* outgoing page would normally vanish before it can animate out. `FrozenRouter`
|
||||
* pins the layout-router context to the value it had when this instance
|
||||
* mounted, keeping the *old* subtree rendered while its exit animation plays.
|
||||
*/
|
||||
function FrozenRouter({ children }: { children: ReactNode }) {
|
||||
const context = useContext(LayoutRouterContext);
|
||||
// Freeze the context captured at mount so the outgoing subtree keeps
|
||||
// rendering (with its old route data) while its exit animation plays.
|
||||
const [frozen] = useState(context);
|
||||
|
||||
if (!frozen) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return <LayoutRouterContext.Provider value={frozen}>{children}</LayoutRouterContext.Provider>;
|
||||
}
|
||||
|
||||
/**
|
||||
* One unified page transition on every navigation: the old page animates out
|
||||
* (blur + rise up) before the new page animates in (blur + rise from below).
|
||||
*/
|
||||
export default function SiteTemplate({ children }: { children: ReactNode }) {
|
||||
const reducedMotion = useReducedMotion();
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<motion.div
|
||||
key={pathname}
|
||||
initial={reducedMotion ? false : { opacity: 0, y: 18, filter: "blur(10px)" }}
|
||||
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
|
||||
exit={reducedMotion ? { opacity: 0 } : { opacity: 0, y: -18, filter: "blur(10px)" }}
|
||||
transition={reducedMotion ? { duration: 0 } : { duration: 0.4, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
<FrozenRouter>{children}</FrozenRouter>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
+4
-3
@@ -422,10 +422,11 @@ html[lang="ar"] .eyebrow {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Hero entrance (pure CSS so it never flashes blank before hydration) ---- */
|
||||
/* ---- Hero entrance ----
|
||||
Per-element rise is disabled: the site `template.tsx` now runs one unified
|
||||
page-transition animation on every navigation instead. ---- */
|
||||
.hero-rise {
|
||||
opacity: 0;
|
||||
animation: hero-rise-in 0.62s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hero-content > *:nth-child(1) {
|
||||
|
||||
Reference in New Issue
Block a user