AI

AIが書いたアニメーションが動かないとき — 原因7分類と切り分けの手順

AIに書かせたアニメーションが動かないとき、同じ指示をもう一度投げても直らないことがよくあります。原因の大半は限られたパターンに収束するので、当てずっぽうに直すより上から順に潰すほうが速く着きます。このガイドでは、実際に多い7つの原因を「見た目の症状」から引ける形に分類し、最後に最小再現で切り分ける手順をまとめます。AI特有の原因(存在しないクラス名・混ざったライブラリ前提)も分けて扱います。

まず症状を3つに切り分ける

原因を探す前に、症状がどれかを決めます。ここを飛ばすと関係ない場所を触ることになります。

A. まったく変化しない — CSSが当たっていないか、アニメーションの定義自体が成立していません。原因1〜4がここです。

B. 一瞬で終わる/最初から最終状態 — 時間指定か初期状態の問題です。原因3・5がここです。

C. 行きは動くが戻らない/たまに動かない — 指定の置き場所か上書きの問題です。原因1・6がここです。

JSを伴う実装でコンソールにエラーが出ている場合は、CSSを見る前に原因7を先に潰します。

原因1:transition を :hover 側に書いている

最も多い型です。transition平常時の要素に書きます。:hover の中に書くと、マウスを乗せたときは動くのに離したときは瞬時に戻ります(症状C)。

AIが書いたコードでこれが起きるのは、指示が「ホバーしたときにこう動かして」という形だったときです。要求が :hover に寄っていると、指定もそこに集まります。

最小再現 — transition の置き場所
<!-- 切り分け用の最小再現。これが動くならCSSの書き方は正しい -->
<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .box {
    width: 100px; height: 100px; background: #E91B89;
    /* transition は「平常時の要素」に書く。:hover 側に書くと戻りが効かない */
    transition: transform .3s ease-out;
  }
  .box:hover { transform: translateY(-12px); }
</style>
<div class="box"></div>

この最小再現が動くなら、書き方の理解は正しいので、次は実プロジェクト側の上書き(原因6)を疑います。

原因2:animation-name と @keyframes 名が一致していない

@keyframes fadeUp と定義して animation: fade-up 1s と書く——ハイフンとキャメルケースの取り違えです。CSSはエラーを出さず、ただ何も起きません(症状A)。

AIに複数回書き直させた場合に特に起きます。1回目の命名と2回目の命名が混ざるためです。検索して両方の綴りを突き合わせるのが確実です。

同じ理由で、@keyframes の定義そのものがスコープの外(メディアクエリの内側など)に置かれていないかも確認します。

原因3:duration が指定されていない

animation-duration の初期値は 0s です。指定がなければ定義があっても一瞬で終わり、見た目には何も起きません(症状B)。

animation: fadeUp ease-out; のように名前とイージングだけ書かれているケースが典型です。ショートハンドでは最初に現れる時間値が durationとして解釈されるため、時間を1つも書かないと 0s のままになります。

transition も同様で、transition-duration がなければ変化は即時に反映されます。

原因4:display:none の要素をアニメーションさせている

display:none の要素にはアニメーションが適用されません。displaynone から block に変えると同時にフェードさせる指定も、その1フレームでは効きません(症状A)。

この場合は display を切り替えずに、opacityvisibility で出し入れするか、表示に切り替えた次のフレームでクラスを付ける形にします。モーダルの実装で頻出する型で、完成形は Modal FadeModal Scale Blur で確認できます。

同じ理由で、親要素が display:none の場合も子は動きません。要素単体ではなく親をたどって確認します。

原因5:初期状態が書かれていない

「画面に入ったらふわっと出す」実装で、.is-visible 側だけが書かれ、平常時の opacity:0 が無い——というパターンです。最初から見えているので、何も起きていないように見えます(症状B)。

AIが片方だけ書くのは、指示が「表示されたときにこうして」という変化後の状態だけを語っているときです。「初期状態は opacity:0 / translateY(24px)」と明示すると落ちません。

スクロール連動の実装全体の型は スクロールアニメーション完全ガイド に整理してあります。完成形は Fade UpGSAP Scroll Reveal が参考になります。

原因6:transform が別の指定で上書きされている

transform1つのプロパティです。transform: translateY(-8px)transform: scale(1.05) を別々のルールで書くと、後勝ちで片方が消えます(症状C:一部だけ効かない)。

中央寄せで transform: translate(-50%, -50%) を使っている要素に、ホバーで transform: scale(1.05) を当てると位置が飛ぶ——これも同じ原因です。まとめて transform: translate(-50%, -50%) scale(1.05) と書くか、内側にラッパを1枚足して役割を分けます。

詳細度の競合も同型です。効かないルールがあるときは、ブラウザの開発者ツールで打ち消し線が付いていないかを見るのが最短です。

原因7:JSが要素を掴めていない(コンソールを先に見る)

JSを伴う実装で最も多いのは、要素が存在しないタイミングでの参照です。document.querySelector(...)null を返し、以降の処理が止まります。

確認は簡単で、コンソールにエラーが出ていればここです。<script><head> にあって defer が付いていない、あるいは動的に生成される要素を初期化時に探している、のどちらかがほとんどです。

AI特有の原因として、存在しないクラス名を前提にしたコードが返ってくることがあります。プロンプトでDOM構造を渡していない場合、AIは一般的な命名(.card .item)を仮定します。実際のクラス名と突き合わせてください。

同じくAI特有として、ライブラリ前提のコードが混ざることがあります。GSAP や Framer Motion の API が、ライブラリを読み込んでいないプロジェクトに書かれている型です。読み込みの有無を先に確認します(GSAP ScrollTrigger 完全ガイドFramer Motion入門)。

それでも直らないときは最小再現に落とす

7つを潰しても直らない場合、原因はアニメーションの外にあります。切り分けは最小再現が確実です。空のHTMLに当該要素とCSSだけを移し、動くかどうかを見ます。

最小再現で動くなら、原因は実プロジェクト側にあります。親要素の overflow transform filter、あるいは詳細度の競合が候補です。特に親に transform があると position:fixed の基準が変わるため、位置がずれる系の不具合はここを疑います。

最小再現で動かないなら、書き方そのものに原因があります。原因1〜6をもう一度、症状から引き直します。

AIに聞き直すときは「動きません」ではなく、最小再現のコードと症状(A/B/C)を渡すと一度で通ります。情報が足りないまま投げると、AIは推測で別の実装に書き換えてしまい、原因が分からないまま別の不具合が増えます。

最後に:省略されがちな定型を先に入れておく

動く/動かないとは別に、AIが省略しやすく後から指摘されるのが prefers-reduced-motion 対応です。実装のたびに書かせるより、先にサイト共通で入れてしまうほうが確実です。

prefers-reduced-motion 定型
/* AIが書き忘れやすい定型。これが無いと受け入れ条件を満たさない */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

この定型を共通CSSに置き、規約ファイルにも書いておけば、以降の実装で毎回確認する必要がなくなります(CLAUDE.md / .cursorrules 規約テンプレート)。

あわせて読みたい

CSSアニメーション入門 — transition と @keyframes の使い分け

GitHub Copilot でCSSアニメーションを実装する — 補完・Chat・Edits の使い分け

CSSアニメーション AIプロンプト例文集 — 条件を落とさない指示文の型

Core Web Vitals に影響しないアニメーション設計 — 動くけれど重い、を防ぐ

FAQ

transition が効きません。最初に何を見ればいいですか?
transition の指定が :hover の中に書かれていないかを確認してください。transition は平常時の要素に書きます。:hover 側に書くと、乗せたときは動いて離したときに瞬時に戻ります。
アニメーションが一瞬で終わってしまいます。
animation-duration が指定されていない可能性が高いです。初期値は 0s なので、ショートハンドに時間値が1つも含まれていないと定義があっても何も起きません。
AIが存在しないクラス名を使ったコードを返してきます。
プロンプトでDOM構造を渡していない場合、AIは一般的な命名を仮定します。実際のHTMLの該当部分を貼るか、対象ファイルのパスを指定して読ませてから実装させてください。
最小再現では動くのに、実際のページでは動きません。
実プロジェクト側の上書きか、親要素の影響です。詳細度の競合、親の overflow / transform / filter を順に確認してください。開発者ツールで打ち消し線が付いているルールを探すのが最短です。
同じ指示を繰り返しても直らないときはどうすればいいですか?
情報が足りていないので、繰り返しても結果は変わりません。最小再現のコードと症状(変化しない/一瞬で終わる/戻らない)を添えて投げ直すと、一度で原因に到達します。