No.106
TRANSITION CSSJSFRAMER

Framer Page Transition

前のページが退場してから次のページが入場するルート遷移。AnimatePresence mode=waitで実装するSPA/Next.js向けの定番。

DEMO

01HOME PAGE
02ABOUT PAGE

CODE

HTML
<div class="demo-stage fpt-stage">
  <div class="fpt-tabs">
    <button class="fpt-tab fpt-tab-active" data-page="home" type="button">HOME</button>
    <button class="fpt-tab" data-page="about" type="button">ABOUT</button>
  </div>
  <div class="fpt-view">
    <div class="fpt-page fpt-in" data-page="home"><span>01</span>HOME PAGE</div>
    <div class="fpt-page fpt-hidden" data-page="about"><span>02</span>ABOUT PAGE</div>
  </div>
</div>
CSS
.fpt-stage { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1.1rem; padding:3rem 1.5rem; background:#0F0F12; min-height:300px; }
.fpt-tabs { display:flex; gap:.6rem; }
.fpt-tab {
  padding:.5rem 1.4rem; border-radius:999px; cursor:pointer;
  font-family:'Oswald',sans-serif; font-size:.72rem; letter-spacing:.2em;
  color:#c9c9d1; background:transparent; border:1px solid rgba(255,255,255,.2);
  transition: color .25s ease, border-color .25s ease;
}
.fpt-tab-active { color:#fff; border-color:#E91B89; background:rgba(233,27,137,.14); }
.fpt-view { position:relative; width:min(380px,100%); height:130px; }
.fpt-page {
  position:absolute; inset:0; border-radius:14px;
  display:flex; align-items:center; justify-content:center; gap:.8rem;
  background:#1A1A1A; border:1px solid rgba(255,255,255,.08);
  color:#fff; font-family:'Oswald',sans-serif; letter-spacing:.16em;
}
.fpt-page span { color:#E91B89; font-size:1.4rem; }
.fpt-hidden { display:none; }
.fpt-in { animation: fpt-enter .34s cubic-bezier(.16,1,.3,1) both; }
.fpt-out { animation: fpt-exit .22s ease both; }
@keyframes fpt-enter { from { opacity:0; transform:translateX(28px); } to { opacity:1; transform:translateX(0); } }
@keyframes fpt-exit { from { opacity:1; transform:translateX(0); } to { opacity:0; transform:translateX(-28px); } }
JS
(function(){
  var busy = false;
  document.querySelectorAll('.fpt-tab').forEach(function(tab){
    tab.addEventListener('click', function(){
      if (busy || tab.classList.contains('fpt-tab-active')) return;
      busy = true;
      document.querySelectorAll('.fpt-tab').forEach(function(t){ t.classList.remove('fpt-tab-active'); });
      tab.classList.add('fpt-tab-active');
      var current = document.querySelector('.fpt-page:not(.fpt-hidden)');
      var next = document.querySelector('.fpt-page[data-page="' + tab.dataset.page + '"]');
      current.classList.remove('fpt-in');
      current.classList.add('fpt-out');
      current.addEventListener('animationend', function handler(){
        current.removeEventListener('animationend', handler);
        current.classList.remove('fpt-out');
        current.classList.add('fpt-hidden');
        next.classList.remove('fpt-hidden');
        next.classList.add('fpt-in');
        busy = false;
      });
    });
  });
})();
FRAMER MOTION (React)

ライブデモはFramer Motion不要のCSS/JS等価版(exit完了をanimationendで待ってから入場)。下がReact + Framer Motionの実装です。mode="wait" が「退場が終わってから入場」を自動化します。

import { AnimatePresence, motion } from "framer-motion"
import { usePathname } from "next/navigation"

export default function Template({ children }) {
  const pathname = usePathname()
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, x: 28 }}
        animate={{ opacity: 1, x: 0 }}
        exit={{ opacity: 0, x: -28 }}
        transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

AI PROMPT

Claude Code Cursor v0
PROMPT
ページ切り替え時に前のページが左へ退場してから次のページが右から入場するルート遷移をFramer Motionで作って。ページラッパーを <AnimatePresence mode="wait"> で包み、<motion.div key={pathname}> に initial={{ opacity: 0, x: 28 }} / animate={{ opacity: 1, x: 0 }} / exit={{ opacity: 0, x: -28 }} を指定。key がルートごとに変わることで退場と入場が発火し、mode="wait" が「退場完了→入場開始」の順序を保証する。Next.js App Router なら template.js に置く。CSSで近似するなら exit 用 keyframes の animationend を待ってから入場側を再生する。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 UIデザインの教科書 新版 マルチデバイス時代のインターフェース設計 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

RELATED EFFECTS

↗ MORE
→ PAGE FADE TRANSITION → SLIDE PANEL TRANSITION → FRAMER ANIMATEPRESENCE

Framer Motion Page Transition とは

SPAやNext.jsでルートを切り替えるとき、前のページが退場するアニメーションを見せてから次のページを入場させるページ遷移演出。AnimatePresence の mode="wait" と、ルートごとに変わる key の組み合わせで「退場完了→入場開始」の順序が保証されるのが核心です。

なお、CSSのみで実装する当サイトの Page Fade Transition はページ読み込み時の入場演出が対象で、本ページはSPAのルート切り替え(退場つき)が対象という違いがあります。MPAなら View Transition API という選択肢もあります。