page-transition-animation
This skill should be used when the user asks to "animate page or route transitions", "add a page transition or crossfade between views", "animate route changes in Next.js App Router", "use the View Transitions API", "AnimatePresence exit doesn't fire on navigation", or "Framer Motion exit animation not working / exit animation before unmount in Next.js". Covers enter/exit page-transition animation with the View Transitions API and Framer Motion, for Next.js App Router and general SPA routing.
1How It Works & Mechanism
First clarify the structural root cause of the failure: App Router navigation immediately swaps out the old route content, whereas AnimatePresence can only execute exit animations when the unmounting child remains mounted and its wrapper persists continuously.
The reason is structural — in the App Router, when navigating, Next.js unmounts the old route's content and mounts the new one almost immediately.
Solution A consists of template.tsx (remounts on every navigation) + AnimatePresence keyed by pathname + FrozenRouter (snapshots LayoutRouterContext so the old tree keeps rendering old content).
const frozen = useRef(context).current;
Solution B consists of the native View Transitions API (::view-transition-old/new animations with router.push wrapped in document.startViewTransition), or out-of-the-box integration via next-view-transitions.
::view-transition-old(root) { animation: fade 0.25s both reverse; }
Structure troubleshooting as an ordered checklist (7 items) covering wrapper placement, keys, direct motion descendants, mode="wait", FrozenRouter, 'use client', and single children.
**Stable, changing key.** The animated child needs `key={pathname}` (or similar) so removal is detected.
Reduce verification to an independently reproducible single-file harness: use two stub pages with ?view=/phase= to freeze transition phases and validate the motion shape isolated from the Next project first.
One file is the right tier for validating motion before wiring the router; don't stand up Next just to eyeball a fade.
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | framer-motion(AnimatePresence 与 motion,配合 template.tsx 与 FrozenRouter) |
| package | next-view-transitions(把 View Transitions 接进 App Router 客户端导航) |
| api | Next.js 内部模块 LayoutRouterContext(next/dist/shared/lib/app-router-context.shared-runtime)与 usePathname |
| api | 浏览器原生 View Transitions API(document.startViewTransition、::view-transition-old/new、view-transition-name) |
| network | 交付页从 CDN 取 motion 与 React(产物期引用,不判级;正文未点名具体域) |
| cli | npx / Playwright(宿主侧截图工具)与仓库根 scripts/ 助手(seek-shot.sh、contact-sheet.sh 依赖 ffmpeg) |
4Recommended Governance & Evidence
- 网络端点: External network access declaration: Zero outbound requests during runtime execution; delivery page imports motion and React from CDN (artifact-phase reference; domains not specified in the body)
- 本地工作目录: File I/O and artifact output: Zero disk writes within this skill directory; agent writes template.tsx, page-transition.tsx, and frozen-router.tsx into user's Next project
5Second-Pass Verification
- [discrepancy] description 声称的覆盖范围(Next.js App Router 与 general SPA routing)与正文实际覆盖面是否相符 — frontmatter 写 ... for Next.js App Router and general SPA routing.,但正文主体(根因分析、方案 A 的三个文件、7 条排查清单)全部是 App Router 专属;general SPA 只在 View Transitions API 与交付用 stub HTML 两处被顺带覆盖,既无 React Router/Remix 等通用 SPA 路由的接线,也无对应排查清单。属描述略宽于实现(非虚假,但按 description 期望通用方案会落空)。
- [ok] FrozenRouter 依赖的 Next 内部 import 路径真实性 — SKILL.md 给出 import { LayoutRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime'; 并在正文、notes、gotchas、references 四处重复提示 internal、跨版本可能变,references 还专列 FrozenRouter import note across versions 一节;即文档自己对该脆弱性做了标注,非隐瞒。
- [ok] View Transitions 验收的可行性(快照是瞬态) — 正文承认 snapshots are transient,并给出替代(截结束态或用 emulate slow animations),验收步骤不依赖捕捉瞬态帧;startViewTransition 的能力守卫在正文与完成前清单第 1 条都要求。
- [discrepancy] Packaged helper 的路径基准与语义 — SKILL.md 的 verify 段仍贴 scripts/seek-shot.sh anim.html 0 1.5 3(时间冻结、仓库根路径),但本 skill 的冻结参数是 ?view= 与 ?phase=(状态与相位),helper 不匹配;scripts/ 也在仓库根而非本 skill 目录。
6Verdict & Scenarios
083d4f27aaf44464…