Tips & Tricks (更新: 2026/6/2)

Claude CodeでFramer Motionアニメーションを実装する実践ガイド

Claude CodeでFramer Motion(Motion for React)を実装する実践ガイド。プロンプト、コード、落とし穴、検証手順まで解説。

Claude CodeでFramer Motionアニメーションを実装する実践ガイド

Framer Motionで検索してここに来た人が最初に確認すべき点は、2026年時点の公式ドキュメントでは製品名が Motion for React と案内され、インストールは motion、Reactからのimportは motion/react が基本になっていることです。古い記事や既存コードには framer-motion が残っていますが、新規実装ではまず公式のMotion for Reactを基準にします。

ただし、Claude Codeに「いい感じにアニメーションを付けて」とだけ頼むと、動きは派手でもプロダクトでは使いにくい差分になりがちです。Masaが実務用の管理画面で試したときも、最初の生成結果はカードが跳ねすぎ、通知の終了アニメーションが発火せず、スクロール演出が低スペック端末で重くなりました。この記事では、その失敗を前提に、Claude Codeへ渡す指示、実装コード、レビュー観点を一つの流れで整理します。

対象読者は、Reactのコンポーネントは書けるが、アニメーション設計を毎回CSSで手作業している人です。この記事のコードは npm install motion 済みのReactプロジェクトに貼り付けて動かせる粒度にしています。Next.jsやAstroで使う場合は、アニメーション対象をクライアントコンポーネントとして切り出してください。

まず決める設計

アニメーションは装飾ではなく、状態変化を理解しやすくするためのUIです。Claude Codeに任せる前に、次の4点を明文化します。

観点Claude Codeに渡す内容レビュー時に見ること
目的何を分かりやすくしたいか動きが情報理解に貢献しているか
範囲触ってよいファイルと触らないファイル余計なリファクタが混ざっていないか
制約motion/react、Reduced Motion、既存CSS古いimportや過剰な依存がないか
検証手動操作、キーボード、低速端末見た目だけで合格にしていないか

概念図にすると、Claude Codeは「実装者」ではなく「差分を作る共同作業者」です。設計と検証条件を人間側が先に置くと、生成されるコードの品質が安定します。

目的と制約
  -> Claude Codeへの具体的な依頼
  -> Motion for Reactの実装
  -> 手動確認とアクセシビリティ確認
  -> 必要なら再プロンプト

最初の依頼文は、短くても境界を入れます。

Reactの既存コンポーネントにMotion for Reactのアニメーションを追加してください。
条件:
- 新規コードは `motion/react` からimportする
- 既存のデータ取得とフォーム処理は変更しない
- Reduced Motionに対応する
- opacityとtransform中心にして、layout shiftを起こさない
- 変更後に確認する手動テストも箇条書きで出す

この形にすると、Claude Codeが「どこまでやるべきか」を判断しやすくなります。特に既存プロジェクトでは、import元の混在を避ける指示が重要です。framer-motionmotion/react を同じファイルに混ぜると、レビュー時に意図が追いにくくなります。

ユースケース1: カード一覧を自然に表示する

最初の実例は、ダッシュボードや記事一覧で使うカードの入場アニメーションです。重要なのは、1枚ずつ派手に動かすことではなく、読み始める順番を自然に作ることです。staggerChildren は便利ですが、遅延を長くしすぎるとユーザーの操作開始を邪魔します。Masaの検証では、カード6枚程度なら0.06から0.09秒が実用的でした。

import { motion, useReducedMotion } from "motion/react";

type Feature = {
  id: string;
  title: string;
  body: string;
  metric: string;
};

const demoFeatures: Feature[] = [
  {
    id: "review",
    title: "レビュー待ち",
    body: "Claude Codeが作った差分を人間が確認するキューです。",
    metric: "8件",
  },
  {
    id: "motion",
    title: "アニメーション改善",
    body: "離脱しやすい画面遷移を短い動きで補助します。",
    metric: "14%",
  },
  {
    id: "a11y",
    title: "Reduced Motion",
    body: "動きを減らしたいユーザーにはフェード中心で表示します。",
    metric: "対応済み",
  },
];

export function AnimatedFeatureCards({
  items = demoFeatures,
}: {
  items?: Feature[];
}) {
  const shouldReduceMotion = useReducedMotion();

  const container = {
    hidden: { opacity: 0 },
    visible: {
      opacity: 1,
      transition: {
        staggerChildren: shouldReduceMotion ? 0 : 0.08,
      },
    },
  };

  const card = {
    hidden: {
      opacity: 0,
      y: shouldReduceMotion ? 0 : 16,
    },
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        duration: 0.32,
        ease: "easeOut",
      },
    },
  };

  return (
    <motion.section
      aria-label="機能カード"
      variants={container}
      initial="hidden"
      animate="visible"
      style={{
        display: "grid",
        gap: 16,
        gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
      }}
    >
      {items.map((item) => (
        <motion.article
          key={item.id}
          variants={card}
          whileHover={shouldReduceMotion ? undefined : { y: -4 }}
          style={{
            border: "1px solid #d9e2ec",
            borderRadius: 12,
            padding: 20,
            background: "#ffffff",
            boxShadow: "0 8px 24px rgba(15, 23, 42, 0.08)",
          }}
        >
          <p style={{ margin: 0, color: "#2563eb", fontWeight: 700 }}>
            {item.metric}
          </p>
          <h3 style={{ margin: "8px 0", fontSize: 18 }}>{item.title}</h3>
          <p style={{ margin: 0, lineHeight: 1.7, color: "#475569" }}>
            {item.body}
          </p>
        </motion.article>
      ))}
    </motion.section>
  );
}

このコードのレビューで見るべき点は、key が安定していること、動きが opacitytransform 中心であること、そしてReduced Motion時に大きな移動が消えることです。Claude Codeには「見た目をもっと豪華に」ではなく、「カード数が増えても操作開始を遅らせない範囲で調整して」と再依頼すると実務向きになります。

ユースケース2: 通知の追加と削除を破綻させない

AnimatePresence は、Reactツリーから消える要素に exit アニメーションを与えるためのコンポーネントです。公式のAnimatePresenceドキュメントにもある通り、直接の子要素を追跡するため、安定した key が必要です。Claude Codeが失敗しやすいのは、親コンポーネントごと条件分岐で消してしまい、exit が走る前に全体がアンマウントされるパターンです。

通知UIなら、リストを常に残し、各通知を AnimatePresence の中で増減させます。

import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";

type Toast = {
  id: number;
  message: string;
};

let nextToastId = 1;

export function AnimatedNotifications() {
  const [toasts, setToasts] = useState<Toast[]>([
    { id: 0, message: "保存しました" },
  ]);

  function addToast() {
    const id = nextToastId++;
    setToasts((current) => [
      ...current,
      { id, message: `バックグラウンド処理 ${id} が完了しました` },
    ]);
  }

  function dismissToast(id: number) {
    setToasts((current) => current.filter((toast) => toast.id !== id));
  }

  return (
    <div>
      <button type="button" onClick={addToast}>
        通知を追加
      </button>

      <div
        aria-live="polite"
        style={{
          position: "fixed",
          right: 24,
          top: 24,
          display: "grid",
          gap: 12,
          width: "min(360px, calc(100vw - 48px))",
        }}
      >
        <AnimatePresence initial={false} mode="popLayout">
          {toasts.map((toast) => (
            <motion.div
              layout
              key={toast.id}
              initial={{ opacity: 0, x: 40, scale: 0.96 }}
              animate={{ opacity: 1, x: 0, scale: 1 }}
              exit={{ opacity: 0, x: 40, scale: 0.96 }}
              transition={{ duration: 0.2, ease: "easeOut" }}
              style={{
                borderRadius: 10,
                border: "1px solid #cbd5e1",
                background: "#ffffff",
                padding: 16,
                boxShadow: "0 12px 30px rgba(15, 23, 42, 0.16)",
              }}
            >
              <p style={{ margin: "0 0 12px", color: "#0f172a" }}>
                {toast.message}
              </p>
              <button type="button" onClick={() => dismissToast(toast.id)}>
                閉じる
              </button>
            </motion.div>
          ))}
        </AnimatePresence>
      </div>
    </div>
  );
}

ここでClaude Codeに追加で頼むなら、「通知が0件のときもコンテナを消さない」「キーボードで閉じられる」「aria-live の読み上げが過剰にならない」の3点です。アニメーションだけを見ると合格に見えても、通知は実際の操作中に何度も出るため、読み上げとフォーカスの設計が雑だとすぐにストレスになります。

ユースケース3: スクロール進捗と読み物の演出

LPや長いドキュメントでは、スクロール位置に連動した進捗バーが有効です。MotionのuseScrollは、ページ全体または特定要素の進捗をMotionValueとして返します。useSpring と組み合わせると、値の変化が滑らかになります。

ただし、パララックスを入れすぎると読みにくくなります。Reduced Motionが有効な場合は、位置移動ではなく不透明度だけに寄せます。

import { useRef } from "react";
import {
  motion,
  useReducedMotion,
  useScroll,
  useSpring,
  useTransform,
} from "motion/react";

const sections = [
  {
    title: "要件を固定する",
    body: "Claude Codeに任せる前に、目的、対象ファイル、禁止事項を明文化します。",
  },
  {
    title: "小さく動かす",
    body: "最初はopacityとtransformだけで、ユーザーの理解を助ける動きに絞ります。",
  },
  {
    title: "実機で確認する",
    body: "低速端末、キーボード操作、Reduced Motionを確認してから公開します。",
  },
];

export function ScrollReadingProgress() {
  const articleRef = useRef<HTMLElement | null>(null);
  const shouldReduceMotion = useReducedMotion();
  const { scrollYProgress } = useScroll({
    target: articleRef,
    offset: ["start start", "end end"],
  });

  const scaleX = useSpring(scrollYProgress, {
    stiffness: 120,
    damping: 28,
    mass: 0.2,
  });
  const y = useTransform(scrollYProgress, [0, 1], [0, -48]);

  return (
    <article ref={articleRef} style={{ position: "relative", padding: 24 }}>
      <motion.div
        aria-hidden="true"
        style={{
          position: "sticky",
          top: 0,
          zIndex: 10,
          height: 4,
          scaleX: shouldReduceMotion ? 1 : scaleX,
          transformOrigin: "0% 50%",
          background: "#2563eb",
        }}
      />

      <motion.header
        style={{
          y: shouldReduceMotion ? 0 : y,
          padding: "56px 0 32px",
        }}
      >
        <p style={{ color: "#2563eb", fontWeight: 700 }}>
          Claude Code x Motion
        </p>
        <h2 style={{ fontSize: 36, margin: 0 }}>
          読み進めるほど文脈が分かるページ
        </h2>
      </motion.header>

      <div style={{ display: "grid", gap: 24 }}>
        {sections.map((section) => (
          <section
            key={section.title}
            style={{
              border: "1px solid #dbe4ee",
              borderRadius: 12,
              padding: 24,
              background: "#ffffff",
            }}
          >
            <h3>{section.title}</h3>
            <p style={{ lineHeight: 1.8 }}>{section.body}</p>
          </section>
        ))}
      </div>
    </article>
  );
}

手順記事やドキュメントサイトでこのパターンを使う場合は、スクロール進捗を「読者の現在地を示す補助」として扱います。セクションごとに要素が横から飛び込むような演出は、最初は楽しく見えても、読み返すと邪魔になることがあります。

ユースケース4: Claude Codeにレビューまで頼む

実装後の品質は、生成したコードそのものよりレビュー依頼の質に左右されます。Claude Codeには、次のように「批判的に見てほしい観点」を渡します。

今の差分をFramer Motion/Motion for Reactの実装としてレビューしてください。
特に次を厳しく確認してください。
- `motion/react` と古い `framer-motion` のimportが混在していないか
- `AnimatePresence` の直接の子に安定したkeyがあるか
- Reduced Motionで大きな移動、パララックス、自動再生が止まるか
- width/height/top/leftを頻繁に動かしていないか
- 手動確認手順が実際のユーザー操作になっているか

このレビュー依頼を挟むだけで、単なる「動くデモ」から「公開できるUI」に近づきます。Claude Codeは差分全体を見られるため、同じコンポーネント内のCSS、状態管理、テスト不足もまとめて指摘させると効果的です。

よくある落とし穴

1つ目は、古いimportをそのまま使うことです。既存記事では import { motion } from "framer-motion" が多く残っています。すでにそのパッケージで統一されたプロジェクトなら急に混ぜるべきではありませんが、新規実装や更新記事では npm install motionmotion/react を基準にします。

2つ目は、exit が動かない原因をイージングやdurationだと思い込むことです。多くの場合、問題は AnimatePresence の外側で親が先に消えること、またはリストの key がindexになっていることです。動きの調整より先にReactツリーの残り方を確認します。

3つ目は、layout を万能だと思うことです。layout は並び替えやサイズ変化に強い一方、画像読み込み、CSS Gridの急な列数変更、フォント遅延が重なると意図しない跳ね方になります。Claude Codeには「画像の幅高さを固定する」「カードの最小幅を維持する」など、レイアウト側の制約も同時に頼みます。

4つ目は、アクセシビリティを最後に回すことです。Motion公式のアクセシビリティガイドではReduced Motionへの対応が説明されています。大きな移動、視差効果、自動再生を使うなら、最初の実装時点で代替表現を入れます。

5つ目は、パフォーマンス検証を開発PCだけで済ませることです。opacitytransform は比較的安全ですが、巨大なぼかし、頻繁な box-shadow、高解像度画像の同時移動は重くなります。Chrome DevToolsのPerformanceだけでなく、実際に画面をスクロールして引っかかりを確認します。

公開前チェックリスト

  • Motionの公式ドキュメントとimport元が一致している
  • 主要な状態変化に目的がある
  • Reduced Motionで大きな動きが抑制される
  • AnimatePresence の子に安定した key がある
  • キーボード操作とフォーカス表示が壊れていない
  • スクロール演出が本文の読みやすさを邪魔していない
  • 手動確認結果をPRや記事末尾に残している

関連記事として、UI部品の土台はClaude CodeでRadix UIコンポーネントを活用する、デザインシステム側の整理はClaude Codeでshadcn/uiを活用する、動き全般の考え方はClaude Codeアニメーション実装ガイドも合わせて読むと、実装の切り分けがしやすくなります。Claude Code自体の使い方はAnthropicのClaude Codeドキュメントも確認してください。

まとめ

Claude CodeとFramer Motionを組み合わせる価値は、派手な動きを一瞬で作ることではありません。状態変化、削除、スクロール、ユーザー設定という見落としやすい条件を、実装とレビューの両方で早めに潰せることです。

この記事で紹介した内容をMasaが実際に試した結果、最も効果があったのは「最初のプロンプトにReduced Motionとimport元を明記すること」でした。後から直すより、初回差分で制約を入れた方が、レビュー時間も手戻りも明らかに減ります。社内のUI改善に展開するなら、無料チートシートでプロンプト型を手元に置き、複数画面をまとめて改善したい場合はトレーニング相談から設計レビュー込みで進めるのが現実的です。

#Claude Code #Framer Motion #Motion for React #React #アニメーション #UI/UX
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。