【Next.js】Tailwind CSS でアニメーションを付ける方法を解説|入場・ホバー・ループ

公開日: 2026年9月12日 · 読了目安 13 分

Next.js と Tailwind CSS だけで、ホバー、ページを開いたときの入場、ロゴのループ、画面に入ったときのフェードを付ける方法をコード付きで解説。v4 の @theme と v3 の設定、Framer Motion が必要になる境目、よくある失敗もまとめます。

Next.js のアニメーションは Tailwind CSS だけで足りる

Next.js で LP を作っていて、静的なページに少し動きを足したい。けれど、アニメーションのためにライブラリを入れるべきか迷う。

そんなときは、まず Tailwind CSS だけで書いてください。ホバーやページを開いたときの入場、ロゴのループは、CSS のアニメーションで十分に作れます。

Motion(旧 Framer Motion)を足すのは、スクロール量に合わせて値を変えたいときや、要素が消えるときの動きが必要なときだけです。境目はこの記事の後半で整理します。

この記事で分かることは次のとおりです。

  • transition と hover: で作るホバーの動き

  • @keyframes を足して作る、ページを開いたときの入場アニメーション

  • ロゴやテキストが流れ続けるループ

  • 画面に入ったときにフェードさせる、ブラウザで動く小さな部品

  • Tailwind CSS だけでは届かない動きと、よくある失敗

Tailwind CSS でアニメーションを書く3つの利点

1つ目は、サーバーコンポーネント(サーバーで組み立てる部品)のまま使えることです。Tailwind CSS のクラスはただの CSS なので、"use client"(この部品はブラウザで動かす、という印)を付けなくても動きます。

ページ全体を、ブラウザで動く部品にしなくて済みます。

2つ目は、JavaScript が増えないことです。動きはブラウザの CSS エンジンが処理します。

transform と opacity だけを動かせば、スマホでも滑らかに動きやすくなります。

3つ目は、変更が1か所で済むことです。イージングや時間をテーマに登録しておけば、ページ全体の動きを同じ調子に揃えられます。

Tailwind CSS でホバーアニメーションを作る

ホバーは一番手軽な動きです。transition で変化にかける時間を決め、hover: で変化後の見た目を書きます。

親に group を付けると、カードにマウスを乗せたときに中の矢印だけを動かす、といった指定もできます。

動かすものは1つか2つに絞ります。候補は translate と scale、opacity、色です。

box-shadow を大きく変える動きは描画が重く、カードが多いと引っかかって見えます。

tsx
export function FeatureCard({ title, body }: { title: string; body: string }) {
    return (
      <article className="group rounded-2xl border border-zinc-200 p-6 transition duration-200 ease-out hover:-translate-y-1 hover:border-zinc-400">
        <h3 className="font-semibold">{title}</h3>
        <p className="mt-2 text-sm text-zinc-600">{body}</p>
        <span className="mt-4 inline-block text-sm transition-transform duration-200 group-hover:translate-x-1">
          詳しく見る →
        </span>
      </article>
    );
  }

Tailwind CSS の入場アニメーションを @keyframes で作る

Tailwind CSS には animate-spin や animate-bounce などが最初から入っていますが、LP の入場には向きません。下から少し浮き上がって現れる動きを、自分で1つだけ登録します。

Tailwind CSS v4:CSS ファイルに入場の動きを登録する

v4 では、globals.css の @theme(共通の値をまとめる場所)に書きます。

その中に、--animate- で始まる名前と @keyframes(動きの中身)を並べます。

すると、animate-rise のようなクラスが使えるようになります。

下の例では、後で使うループ用の marquee も一緒に登録しています。

css
/* app/globals.css */
  @import "tailwindcss";
  
  @theme {
    --animate-rise: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
    --animate-marquee: marquee 30s linear infinite;
  
    @keyframes rise {
      from { opacity: 0; transform: translateY(16px); }
      to { opacity: 1; transform: translateY(0); }
    }
  
    @keyframes marquee {
      from { transform: translateX(0); }
      to { transform: translateX(-50%); }
    }
  }

Tailwind CSS v3:設定ファイルに入場の動きを登録する

v3 では、tailwind.config.ts の theme.extend に同じ内容を書きます。

書く項目は keyframes と animation の2つです。

クラス名は v4 と同じ animate-rise になります。

ts
// tailwind.config.ts
  import type { Config } from "tailwindcss";
  
  export default {
    content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"],
    theme: {
      extend: {
        keyframes: {
          rise: {
            from: { opacity: "0", transform: "translateY(16px)" },
            to: { opacity: "1", transform: "translateY(0)" },
          },
          marquee: {
            from: { transform: "translateX(0)" },
            to: { transform: "translateX(-50%)" },
          },
        },
        animation: {
          rise: "rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both",
          marquee: "marquee 30s linear infinite",
        },
      },
    },
  } satisfies Config;

ヒーローの見出し・説明・ボタンを順番にアニメーションさせる

要素ごとに開始を少しずらすと、ページを開いた瞬間に読む順番が生まれます。

motion-safe:[animation-delay:100ms] のような任意プロパティで遅らせます。間隔は 80〜120ms が目安です。

animate-rise はショートハンドなので、遅延にも motion-safe: を付けて後から上書きされないようにします。

motion-safe: を付けておくと、OS の設定で動きを減らしている人には動きを出しません。この部品は、サーバーで組み立てる部品のままです。

tsx
export function Hero() {
    return (
      <section className="px-6 py-32 text-center">
        <h1 className="text-5xl font-semibold tracking-tight motion-safe:animate-rise">
          LP の公開を、今週中に。
        </h1>
        <p className="mx-auto mt-6 max-w-xl text-zinc-600 motion-safe:animate-rise motion-safe:[animation-delay:100ms]">
          Next.js と Tailwind CSS で作る、軽い入場アニメーションの例です。
        </p>
        <a
          href="#pricing"
          className="mt-10 inline-flex rounded-full bg-zinc-950 px-6 py-3 text-sm text-white motion-safe:animate-rise motion-safe:[animation-delay:200ms]"
        >
          料金を見る
        </a>
      </section>
    );
  }

Tailwind CSS でロゴが流れるループアニメーションを作る

導入企業のロゴを横に流すマーキーも、CSS だけで作れます。同じリストを2回並べ、全体を -50% まで動かして、最初に戻ったことが見えないようにします。

継ぎ目をずらさないため、要素の間隔は gap ではなく各要素の px で取ります。

マウスを乗せたら止まるようにしておくと、ロゴを読みたい人に親切です。複製した側は aria-hidden にして、読み上げが2回にならないようにします。

tsx
const logos = ["Acme", "Globex", "Initech", "Umbrella", "Hooli"];
  
  export function LogoMarquee() {
    return (
      <div className="overflow-hidden py-10">
        <ul className="flex w-max motion-safe:animate-marquee hover:[animation-play-state:paused]">
          {[...logos, ...logos].map((name, i) => (
            <li
              key={i}
              aria-hidden={i >= logos.length}
              className="px-8 text-lg font-semibold text-zinc-400"
            >
              {name}
            </li>
          ))}
        </ul>
      </div>
    );
  }

ループアニメーションは1ページに1つまで

ずっと動き続ける要素は、視線を奪い続けます。ロゴのマーキーとグラデーションの背景が同じ画面にあると、どちらも目立たなくなります。

ループは1ページに1つ、duration は長め(20〜40秒)にします。

詳しい作り方は https://design-layer.com/ja/articles/nextjs-logo-marquee-guide で解説しています。

画面に入ったときのフェードアニメーションを付ける

ここまでの入場は、ページを開いた瞬間に動きます。スクロールして画面に入ったときに動かしたいなら、要素が見えたかどうかを知る必要があり、CSS のクラスだけでは足りません。

Motion を入れなくても、ブラウザで動く小さな部品で対応できます。使うのは IntersectionObserver(画面に入ったかを調べる仕組み)です。

ブラウザで動かす印は、このファイルだけに付けます。ページはサーバーで組み立てる部品のままにして、中身を Reveal で包みます。

tsx
"use client";
  
  import { useEffect, useRef, useState, type ReactNode } from "react";
  
  export function Reveal({ children, className = "" }: { children: ReactNode; className?: string }) {
    const ref = useRef<HTMLDivElement>(null);
    const [visible, setVisible] = useState(false);
  
    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      const observer = new IntersectionObserver(
        ([entry]) => {
          if (entry.isIntersecting) {
            setVisible(true);
            observer.disconnect();
          }
        },
        { rootMargin: "0px 0px -10% 0px" }
      );
      observer.observe(el);
      return () => observer.disconnect();
    }, []);
  
    return (
      <div
        ref={ref}
        className={
          (visible ? "translate-y-0 opacity-100" : "translate-y-4 opacity-0") +
          " transition duration-700 ease-out motion-reduce:translate-y-0 motion-reduce:opacity-100 motion-reduce:transition-none " +
          className
        }
      >
        {children}
      </div>
    );
  }

CSS だけでスクロールアニメーションを付ける方法

CSS には、スクロール位置を時間の代わりに使う animation-timeline: view() という仕組みもあります。JavaScript が要らないのが利点です。Chrome と Safari は対応していますが、Firefox はまだ標準では動きません。

LP で使うなら、未対応のブラウザでは最初から表示されるように書いておきます。

Tailwind CSS だけでは難しいアニメーション

次のような動きは、CSS のクラスだけで書くと複雑になります。ここから先は Motion(motion パッケージ)の出番です。

  • スクロール量に合わせて、位置や大きさが少しずつ変わる動き

  • セクションを画面に固定し、スクロールで中身を切り替える動き

  • ドラッグやスワイプに合わせた動き

  • 要素が消えるときのアニメーションや、レイアウトの変化をなめらかにつなぐ動き

導入と使い分けは https://design-layer.com/ja/articles/nextjs-framer-motion-guide にまとめています。Tailwind CSS で作った入場やホバーは、Motion を入れた後もそのまま残してかまいません。

Tailwind CSS のアニメーションでよくある失敗

1つ目は、クラス名を文字列でつなげて作ることです。

Tailwind CSS はソースに書かれたクラス名をそのまま探します。そのため "animate-" + name のように組み立てたクラスは、CSS が生成されません。

使うクラスは "animate-rise" のように、省略せずに書きます。

2つ目は、width や height、box-shadow を動かすことです。レイアウトの再計算や重い再描画が起き、カクつきやすくなります。

動かすのは transform と opacity を基本にします。

3つ目は、全セクションに入場を付けることです。どれも同じ強さで動くと、ページの山場が消えます。

強い入場はヒーローと CTA だけにし、他は Reveal の薄いフェードか、動きなしにします。

4つ目は、動きを減らす OS の設定への配慮を忘れることです。ループや入場には motion-safe: を付け、動きを減らしたい人には止まった状態で見せます。

完成したアニメーション付きセクションをコピーして使う方法

DesignLayer のコンポーネント一覧。左のカテゴリで用途ごとに絞り込める
DesignLayer のコンポーネント一覧。左のカテゴリで用途ごとに絞り込める

ホバーや入場は、この記事のコードで十分に作れます。

一方で、イージングと遅延の組み合わせを詰める作業は時間がかかります。Motion でスクロール連動の動きを一から作ると、1セクションで半日かかることもあります。

その場合は、ブラウザで実際の動きを見てから、完成したセクションを自分の Next.js に載せる方法があります。

DesignLayer は、人が組んだ React + Tailwind CSS のモーション付きセクションを、ライブプレビューで確かめてからコピーできるカタログです。

閲覧にカードは要りません。https://design-layer.com/ja/components

無料アカウントでは、無料サンプル10個を無料アカウントで回数制限なくコピーでき、動画プロンプトは月1回です。Free では MCP に接続できません。カタログ全体は30日ビルドパス($19の1回払い、自動更新なし)、Lifetime Access(先着100名の$79の1回払い)、Plus(月額 $9、いつでも解約可)です。無料サンプルなら回数の上限なくコピーできます。それ以外は30日ビルドパス($19)、Lifetime($79)、Plus(月額 $9)です。

Tailwind CSS アニメーションについてよくある質問

  • クラスで付けるホバー、入場、ループには不要です。画面に入ったことを調べるときや、state(部品が覚えておく値)で表示を切り替えるときだけ、その部品のファイルに "use client" を付けます。

まとめ:Next.js で Tailwind CSS アニメーションを付ける手順

  • ホバーと入場、ループは Tailwind CSS だけで作れる

  • 入場とループの動きは、テーマの設定に1つずつ登録する

  • 画面に入ったときの動きは、ブラウザで動く小さな部品で足りる

  • スクロール連動やドラッグ、消えるときの動きが要るときだけ Motion を足す

  • 強い動きはヒーローと CTA に絞り、motion-safe: で動きを減らしたい人に配慮する

まずはヒーローの見出しとボタンに animate-rise を付け、遅延を 100ms ずつずらしてみてください。スクロールで要素を出す演出をもっと詰めたい場合は https://design-layer.com/ja/articles/nextjs-scroll-reveal-guide も参考になります。

関連ガイド