レビュー待ち·難易度: 初級·更新: 2026-04-18

Tailwind CSS でアニメーションとトランジションを実装する

Tailwind CSS の animate-* / transition / duration / ease クラスを使い、フェード・スライド・スケール・スピンなどのアニメーションをコンポーネントに適用する例。

nextjsstylingtailwindcss

対応バージョン

nextjs 15react 19tailwindcss 4

前提環境

Tailwind CSS の基本的なクラスを理解していること

概要

Tailwind CSS の transition / duration / ease クラスでトランジション効果を付け、animate-* クラスで組み込みアニメーションを適用する。状態変化に連動したホバー・フェード・スライド・スピンのパターンを示す。

インストール

npm install tailwindcss

実装

トランジション(状態変化に連動)

// components/TransitionExamples.tsx
"use client";

import { useState } from "react";

export function TransitionExamples() {
  const [expanded, setExpanded] = useState(false);
  const [visible, setVisible] = useState(true);

  return (
    <div className="space-y-6 p-6">
      {/* ホバートランジション */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">ホバーエフェクト</h3>
        <button className="rounded bg-blue-600 px-4 py-2 text-sm text-white
                           transition-all duration-200 ease-in-out
                           hover:scale-105 hover:bg-blue-700 hover:shadow-md
                           active:scale-95">
          ホバーしてみて
        </button>
      </div>

      {/* 幅のトランジション */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">幅のアニメーション</h3>
        <button
          onClick={() => setExpanded(!expanded)}
          className={`overflow-hidden rounded bg-green-500 py-2 text-sm text-white
                      transition-all duration-500 ease-in-out
                      ${expanded ? "w-48" : "w-24"}`}
        >
          {expanded ? "縮む" : "広がる"}
        </button>
      </div>

      {/* 表示・非表示トランジション */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">フェードイン・アウト</h3>
        <button
          onClick={() => setVisible(!visible)}
          className="mb-3 rounded border px-3 py-1 text-sm"
        >
          {visible ? "非表示" : "表示"}
        </button>
        <div
          className={`rounded bg-purple-100 p-4 text-sm text-purple-800
                      transition-opacity duration-300
                      ${visible ? "opacity-100" : "opacity-0"}`}
        >
          フェードするボックス
        </div>
      </div>

      {/* カラートランジション */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">カラートランジション</h3>
        <div className="group cursor-pointer rounded border p-4
                        transition-colors duration-300
                        hover:bg-yellow-50 hover:border-yellow-300">
          <p className="text-sm text-gray-600 transition-colors duration-300 group-hover:text-yellow-700">
            ホバーで色が変わる
          </p>
        </div>
      </div>
    </div>
  );
}

組み込みアニメーション(animate-*)

// components/AnimateExamples.tsx
"use client";

import { useState } from "react";

export function AnimateExamples() {
  const [loading, setLoading] = useState(false);

  async function handleClick() {
    setLoading(true);
    await new Promise((r) => setTimeout(r, 2000));
    setLoading(false);
  }

  return (
    <div className="space-y-6 p-6">
      {/* ローディングスピナー */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">ローディングスピナー</h3>
        <button
          onClick={handleClick}
          disabled={loading}
          className="flex items-center gap-2 rounded bg-blue-600 px-4 py-2 text-sm text-white disabled:opacity-70"
        >
          {loading && (
            <span className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
          )}
          {loading ? "処理中..." : "実行する"}
        </button>
      </div>

      {/* パルス(スケルトンローディング) */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">スケルトンローディング</h3>
        <div className="space-y-2">
          <div className="h-4 w-3/4 animate-pulse rounded bg-gray-200" />
          <div className="h-4 w-1/2 animate-pulse rounded bg-gray-200" />
          <div className="h-4 w-5/6 animate-pulse rounded bg-gray-200" />
        </div>
      </div>

      {/* バウンス */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">バウンス</h3>
        <div className="flex items-end gap-1">
          {[0, 1, 2].map((i) => (
            <div
              key={i}
              className="h-3 w-3 animate-bounce rounded-full bg-blue-500"
              style={{ animationDelay: `${i * 150}ms` }}
            />
          ))}
        </div>
      </div>

      {/* ping(通知バッジ) */}
      <div>
        <h3 className="mb-2 text-sm font-semibold">通知バッジ(ping)</h3>
        <div className="relative inline-flex">
          <button className="rounded bg-gray-100 px-4 py-2 text-sm text-gray-700">
            通知
          </button>
          <span className="absolute -right-1 -top-1 flex h-3 w-3">
            <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75" />
            <span className="relative inline-flex h-3 w-3 rounded-full bg-red-500" />
          </span>
        </div>
      </div>
    </div>
  );
}

ページ

// app/page.tsx
import { TransitionExamples } from "@/components/TransitionExamples";
import { AnimateExamples } from "@/components/AnimateExamples";

export default function Page() {
  return (
    <main className="mx-auto max-w-xl p-8">
      <h1 className="mb-8 text-2xl font-bold">Tailwind アニメーション</h1>
      <section className="mb-8">
        <h2 className="mb-4 text-lg font-semibold">トランジション</h2>
        <TransitionExamples />
      </section>
      <section>
        <h2 className="mb-4 text-lg font-semibold">animate-*</h2>
        <AnimateExamples />
      </section>
    </main>
  );
}

ポイント

  • transition-{property} で対象プロパティを指定し、duration-{ms} / ease-{type} で速度と加速を制御する。transition-all ですべてのプロパティに適用
  • 状態変化(expanded ? "w-48" : "w-24" 等)の切り替えに transition を付けるだけでアニメーションになる
  • 組み込みアニメーション: animate-spin(スピナー)/ animate-pulse(スケルトン)/ animate-bounce(バウンス)/ animate-ping(通知バッジ)
  • group クラスを親に付けて group-hover: で子要素に連動したスタイル変化を実現できる
  • animationDelaystyle 属性で指定することで、複数要素の時差アニメーションを実装できる

注意点

tailwind-dark-mode はテーマ切り替え、tailwind-responsive-layout はブレークポイント。アニメーション(animate-fade / animate-spin / transition-all 等)は独立した用途。

関連サンプル