概要
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:で子要素に連動したスタイル変化を実現できるanimationDelayをstyle属性で指定することで、複数要素の時差アニメーションを実装できる