ui-animation について
UI アニメーション設計のワークフロー型スキルです。タイミング、Motion と CSS/Tailwind の手法、アクセシビリティ、性能を案内し、画面遷移やジェスチャー、レイアウト変更を実装する開発者向けです。
主な機能
- 01アニメーション種別に応じた手法選択
- 02イージングと時間の指針
- 03Motion のジェスチャーとレイアウト遷移
- 04視差効果を減らす設定への配慮
- 05性能とフォーカスの確認
- 06再利用可能なアニメーション例
活用シーン
- 01モーダルやドロップダウンの表示・終了効果
- 02ジェスチャー操作やレイアウト変更のアニメーション
- 03視差効果を減らすユーザー向けの調整
- 04アニメーション性能とフォーカス動作の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ lukasstrickler/ ai-dev-atelier --skill ui-animationインストール後、Claude Code で/ui-animationを入力して読み込まれたか確認します。
同梱ファイルreferences/recipes.md
詳細
UI アニメーション
適切なタイミング、アクセシビリティ、パフォーマンスを備えた、洗練された UI アニメーションです。
クイックスタート
手法の選択:
- シンプルなトランジション? → CSS/Tailwind(
transition-*、animate-*) - アンマウントを伴う表示/非表示? → Motion と
AnimatePresence - ジェスチャー駆動? → Motion のスプリング
- レイアウト変更? → Motion の
layoutプロパティ
基本の流れ: イージングの決定ツリー → 期間のガイドライン → 実装 → アクセシビリティの追加 → 検証
基本原則
- 自然な動き:物理法則を模倣します。線形イージングは避けてください。一定の速度で動くものはありません。
- 目的を持たせる:すべてのアニメーションに意味を持たせてください。利点を説明できない場合は削除します。
- 速くする:UI アニメーションは 300ms 未満にします。ホバー効果は 150ms 未満にします。500ms を超えると遅く感じられます。
- 中断可能にする:ジェスチャー駆動のアニメーションにはスプリングを使います。中断を自然に処理できます。
- アクセシブルにする:常に
prefers-reduced-motionを尊重します。これは必須です。
ワークフロー
ステップ 1:アニメーションの種類を分類する
| 種類 | 例 | 手法 |
|---|---|---|
| マイクロインタラクション | ボタンの押下、切り替え、チェックボックス | CSS/Tailwind |
| 表示/非表示 | モーダル、トースト、ドロップダウン | Motion と AnimatePresence |
| レイアウト変更 | アコーディオン、並べ替え、展開 | Motion の layout プロパティ |
| 共有要素 | タブインジケーター、カードの展開 | Motion の layoutId |
| ジェスチャー | ドラッグ、スワイプ、引っ張って更新 | Motion のスプリング |
ステップ 2:タイミングを選択する
- 以下のイージングの決定ツリーを使って曲線を選択します
- 期間のガイドラインを使ってタイミングを選択します
- ジェスチャーには、スプリングアニメーションの設定を使います
ステップ 3:実装する
- コピーして使えるパターンを
references/recipes.mdで確認します - ステップ 2 で選んだタイミングを適用します
- アンマウントされる要素を
AnimatePresenceで囲みます
デザインのスクリーンショットやモックアップから開始する場合は、ui_to_artifact を使います。想定した UI と実装済みの UI を比較するには、ui_diff_check を使います。
ステップ 4:アクセシビリティ(必須)
useReducedMotion()またはmotion-safe:でprefers-reduced-motionを確認します- 動きを抑える設定を使うユーザー向けに、不透明度のみの表現へ簡略化します
- フォーカスのタイミングを確認します(アニメーションの開始後にフォーカスを移動します)
ステップ 5:検証する
- 終了アニメーションが実行される(即座にアンマウントされない)
-
opacity: 0の要素にpointerEvents: noneが設定されている - アニメーションの開始前ではなく、開始後にフォーカスが移動する
- アニメーション対象が
transformとopacityのみである
イージング
決定ツリー
What triggers the animation?
│
├─ User action (click, tap, open)?
│ └─ Use: ease-out (fast start, slow end = responsive)
│
├─ Element moving on-screen (tab switch, reorder)?
│ └─ Use: ease-in-out (accelerate then decelerate)
│
├─ Continuous/looping (spinner, marquee)?
│ └─ Use: linear (constant speed appropriate here)
│
├─ Gesture-based (drag, swipe, pull)?
│ └─ Use: Spring animation (physics-based, interruptible)
│
└─ Hover/focus effect?
└─ Use: CSS ease, 150ms (subtle, immediate)
クイックリファレンス
| 用途 | CSS | Tailwind | 期間 |
|---|---|---|---|
| モーダル/ドロワーの表示 | cubic-bezier(0.32, 0.72, 0, 1) | ease-out duration-200 | 200ms |
| モーダル/ドロワーの非表示 | cubic-bezier(0.32, 0.72, 0, 1) | ease-out duration-150 | 150ms |
| 画面上の移動 | cubic-bezier(0.4, 0, 0.2, 1) | ease-in-out duration-200 | 200-300ms |
| ホバー効果 | ease | ease duration-150 | 150ms |
| ボタンの押下 | — | active:scale-[0.97] | 即時 |
プロ向けの曲線
| 名前 | 値 | 使用例 |
|---|---|---|
| Vaul(バターのように滑らか) | cubic-bezier(0.32, 0.72, 0, 1) | シート、ドロワー、モーダル |
| 強調 | cubic-bezier(0.2, 0, 0, 1) | Material Design 3 |
| キビキビした動き | cubic-bezier(0.25, 1, 0.5, 1) | すばやい UI トランジション |
避けるもの:組み込みの ease-in。始まりが遅く、もたついて感じられます。
期間のガイドライン
| 種類 | 期間 | 備考 |
|---|---|---|
| マイクロフィードバック | 100-150ms | ボタンの押下、切り替え、チェックボックス |
| 小さなトランジション | 150-250ms | ツールチップ、アイコンの変形 |
| 中程度のトランジション | 200-300ms | モーダル、ポップオーバー、ドロップダウン |
| 大きなトランジション | 300-400ms | ページ遷移、複雑なレイアウト |
| 最大 | <500ms | 例外:オンボーディング、データビジュアライゼーション |
重要なルール:
- 表示より非表示を速くする:表示 200ms → 非表示 150ms
- ホバーは速くする:150ms 未満
- 高頻度の操作は即時にする:キーボード操作、スクロールは 100ms 未満、またはアニメーションなし
スプリングアニメーション
Duration-based(推奨)
ほかの時間指定アニメーションと組み合わせやすくなります。visualDuration(目標に視覚的に到達するまでの時間)と bounce(0 = バウンスなし、1 = 強いバウンス)を使用します。
| 印象 | 設定 | 使用例 |
|---|---|---|
| キビキビ | { duration: 0.3, bounce: 0.15 } | タブ、ボタン、素早いフィードバック |
| 標準 | { duration: 0.4, bounce: 0.2 } | モーダル、メニュー、一般的な UI |
| 穏やか | { duration: 0.5, bounce: 0.25 } | 滑らかで、人間らしい動き |
物理ベース(レガシー/上級者向け)
物理ライブラリとの統合時や、スプリングの動きを細かく制御する必要がある場合に使用します。
| 印象 | 設定 | 使用例 |
|---|---|---|
| キビキビ | { stiffness: 400, damping: 30 } | 高頻度のインタラクション |
| 標準 | { stiffness: 300, damping: 20 } | Framer Handshake の規約 |
| 穏やか | { stiffness: 120, damping: 14 } | react-motion のプリセット |
注意点:
stiffness/damping/massはduration/bounceを上書きします。どちらか一方の方法を選び、混在させないでください。
レイアウトアニメーション
layout プロパティ
layout を追加すると、位置やサイズの変化が自動的にアニメーションされます。テキストには layout="position" を使用してください(歪みを防ぎます)。
| プロパティ値 | 効果 | 使用例 |
|---|---|---|
layout={true} | 位置とサイズをアニメーション | 柔軟にサイズが変わる要素のデフォルト |
layout="position" | 移動のみをアニメーション | 引き伸ばしたくないテキスト/アイコン |
layout="size" | 寸法のみをアニメーション | 固定位置にある展開式パネル |
共有要素のトランジション(layoutId)
一致する layoutId を持つ要素は、表示時/非表示時に互いの間でアニメーションします。
重大な落とし穴:layoutId の値が重複すると、要素がページ上を瞬間移動します。コンテキストごとに一意の ID を使用するか、<LayoutGroup id="..."> で囲んでください。
レイアウトに関する注意点
- テキストの歪み:テキスト要素には
layout="position"を適用してください - 境界線の角丸:拡大縮小中に歪むことがあります。Motion が自動補正しますが、テストしてください
- SVG 要素:
layoutは<path>では機能しません。手動でモーフィングしてください
ジェスチャーに関する注意点
| 問題 | 解決策 |
|---|---|
| タッチスクロールとの競合 | dragPropagation={false} |
| 要素が元の位置に戻る | dragConstraints と dragElastic を確認 |
| 慣性が不自然 | 精密な UI では dragMomentum={false} |
| 1 方向のみ | dragElastic={{ top: 0, bottom: 0.5 }} |
スワイプで閉じる場合:距離と速度の両方を確認してください。ユーザーはフリックでも動作することを期待しています。
アクセシビリティ
prefers-reduced-motion(必須)
import { useReducedMotion } from "motion/react"
const shouldReduce = useReducedMotion()
const variants = shouldReduce
? { opacity: 1 } // Fade only
: { opacity: 1, scale: 1, y: 0 } // Full animation
Tailwind:motion-safe:animate-pulse / motion-reduce:transition-none
ベストプラクティス:無効にするのではなく、簡略化してください。空間的な移動をなくし、不透明度は維持します。
フォーカス管理
- アニメーション開始後にフォーカスを移動します:
requestAnimationFrame(() => ref.focus()) - 閉じるときは、トリガーにフォーカスを戻します
aria-live領域内ではアニメーションを使用しないでください
タッチターゲット
| 規格 | サイズ | Tailwind | 物理サイズ |
|---|---|---|---|
| Material Design | 48×48 dp | min-h-12 min-w-12 | 約9mm(推奨) |
| Apple HIG | 44×44 pt | min-h-11 min-w-11 | 約7mm |
| WCAG 2.2(AA) | 24×24 px | min-h-6 min-w-6 | 約5mm(最小) |
理由:成人の指先の平均幅は約9mmです。7mm未満のターゲットでは「ファットフィンガー」エラーが発生します。クロスプラットフォームでは Material の 48dp を、iOS 固有の最小サイズには Apple の 44pt を使用してください。
パフォーマンス
基本ルール
transformとopacityのみをアニメーションさせる—GPU で高速化されます- アニメーションさせないもの:
width、height、top、left、margin、padding will-changeは控えめに使う—アニメーション中のみ使用し、終了後に削除します- ぼかしの目安:
- ≤10px:アニメーションに安全です
- 11~20px:モバイル/4K ではカクつくことがあります。十分にテストしてください
-
20px:リアルタイム効果では避け、あらかじめぼかした画像を使用してください
- シンプルなトランジションには、JS より CSS を優先します
主な落とし穴
- 高さのアニメーション:
animate={{ height }}ではなく、layoutプロパティを使用します - 見えないのにクリックできる:
opacity: 0でもクリックを受け付けます。pointerEvents: "none"を追加してください - あらゆる場所での will-change:レイヤーが過剰に生成され、モバイルでクラッシュします
詳しい例については、references/recipes.md を参照してください。
例
references/recipes.md に、カテゴリ別のコピー&ペースト可能なパターンをまとめています。
- 一般的な UI パターン:ボタンの押下、モーダルの表示/終了、エラー時の揺れ、段階的に表示するリスト、アコーディオン
- タッチとインタラクション:アクセシブルなタッチターゲット、タッチデバイスでのホバー、即時表示のツールチップ
- レイアウトアニメーション:
layoutプロパティ、layoutIdによる共有要素、衝突の修正 - Radix UI との統合:
forceMountパターン、asChild、起点を考慮したポップオーバー - アクセシビリティ:フォーカスのタイミング、フォーカスの復元、視差効果の低減に対応したバリアント
- パフォーマンス:高さのアニメーション(
layoutを使用)、見えないのにクリックできる問題の修正、will-change - 終了パターン:
forwardRefを使ったpopLayout、SSR のハイドレーション(initial={false}) - ジェスチャー:速度チェックを使ったスワイプによる閉じる操作、弾性のあるドラッグ境界
AnimatePresence
| モード | 動作 | 用途 |
|---|---|---|
sync(デフォルト) | 表示と終了を同時に実行 | クロスフェード、オーバーレイ |
wait | 終了の完了後に表示 | ページ遷移、タブ |
popLayout | 終了する要素をフローから除外 | リスト項目の削除(layout と併用) |
終了アニメーションの落とし穴
終了アニメーションには AnimatePresence が必要です。使用しない場合、アンマウントは即座に行われます。
// ❌ Exit never runs
{isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}
// ✅ Wrap in AnimatePresence
<AnimatePresence>
{isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}
</AnimatePresence>
SSR:ページ読み込み時のアニメーションを防ぐには、<AnimatePresence initial={false}> を使用します。
アンチパターン
| 避けること | 代わりにすること | 理由 |
|---|---|---|
scale(0) から開始 | scale(0.9) 以上 | 「ポンッ」と飛び出すような効果を防ぎます |
UI に linear を使用 | ease-out またはスプリング | 線形の動きは機械的に感じられます |
| 500ms を超えるアニメーション | 300ms 未満にする | もたついて感じられます |
| ツールチップの遅延を常に同じにする | 最初は 400ms、以降は 0ms | ユーザーのメンタルモデルに合います |
| 視差効果の低減への対応を省略 | 常に motion-safe: を使用 | アクセシビリティ |
| レイアウトプロパティをアニメーション | transform: scale() を使用 | パフォーマンス |
| 過剰なバウンス | bounce: 0-0.2 | プロらしくない印象になります |
tailwindcss-animate
Tailwind v4:キーフレームは設定ファイルではなく、CSS の
@themeで定義します。
| カテゴリ | クラス |
|---|---|
| 表示 | animate-in fade-in zoom-in-95 slide-in-from-top |
| 終了 | animate-out fade-out zoom-out-95 slide-out-to-top |
| タイミング | delay-150 duration-500 |
| フィルモード | fill-mode-forwards fill-mode-backwards |
他のスキルとの連携
| タイミング | スキル | 理由 |
|---|---|---|
| 実装後 | code-quality | コードがチェックに合格することを確認します |
| 再利用可能なパターン | docs-write | コンポーネント API を文書化します |
| コミット前 | git-commit | feat(ui): または style: を使用します |
| 統合に関する問題 | search | 最新のパターンを調べます |
出力
- 成果物:コード変更のみ(
.ada/の出力なし) - 変更内容:コンポーネントのアニメーション、CSS/Tailwind のスタイル、Motion の設定
- 種類:ワークフロースキル(ガイダンスのみ。スクリプトなし)
参考資料
内部
references/recipes.md- コピー&ペースト可能なパターン、統合例、詳しい落とし穴
外部
- Motion ドキュメント
- Material Design 3 のモーション
- Apple HIG - モーション
- tailwindcss-animate
- easings.net - イージング関数のチートシート