mcpField
W
web-animation-design

web-animation-design

github.com/aa-on-aiデザイン・UI664 回インストール3 日前に更新
リポジトリ

web-animation-design について

web-animation-designは、Webのモーションとインタラクション設計を支援する実装スキルです。既存コンポーネントに合う手法を選び、アクセシビリティと操作上の挙動を保ちながら、根拠の範囲を明示してモーションの問題をレビューします。

主な機能

  1. 01トランジション・キーフレーム・スプリングの選択
  2. 02イージングと時間の指針
  3. 03レイアウトと共有レイアウトの動き
  4. 04視差効果を減らす設定への配慮
  5. 05フォーカスとキーボード操作の維持
  6. 06動作と根拠範囲のレビュー

活用シーン

  1. 01メニューやモーダルの反応設計
  2. 02レイアウトやタブ状態のアニメーション
  3. 03モーション上の問題がある操作のレビュー
  4. 04視差効果を減らす設定での動作確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aa-on-ai/agentic-design-system --skill web-animation-design

インストール後、Claude Code で/web-animation-designを入力して読み込まれたか確認します。

同梱ファイルPRACTICAL-TIPS.mdreferences/transitions-candidate.md

詳細

Webアニメーション設計

適用範囲と責任範囲

タスクにアニメーション、モーション、操作感が関わる場合、または ADS が特定のモーション上の不具合を検出した場合に使用します。通常の UI 作業には、基本のモーションリファレンスで十分です。タスクの管理、権限、レビュー、修正予算は ADS が担います。これは ADS の実装スペシャリストであり、2つ目のハーネスではありません。

実際の依頼と、既存のプリミティブ、状態、プロジェクトトークンを起点にしてください。状態、階層、因果関係、フィードバックの役割を特定します。モーションを加えても価値がない場合は、明確な静的フィードバックを維持してください。レビュー依頼は読み取り専用のまま扱い、実装依頼は既存の権限の範囲内で進めてください。実行可能な依頼を、一般的な準備状況の回答に置き換えないでください。

以下の例は、Emil Kowalski の「Animations on the Web」コースを参考にした実装方法の選択肢であり、必須の美的基準や依存関係のセットではありません。数値例は、プロジェクトトークンで方針が定まっていない場合に限り、出発点として使用してください。

レビュー結果

タスク内の既存のレビューに、観察された挙動、関連する状態や要素、提案する変更、エビデンスの限界を記載してください。複数の指摘を比較するには、変更前と変更後の表が役立ちます。単一の不具合であれば、決まった形式にこだわる必要はありません。決定論的な状態・タイミングの確認、実際のブラウザー操作、測定したパフォーマンス、明瞭さや操作感に関するユーザーの判断は、それぞれ分けてください。

既存の実装方法を選ぶ

  • 既存のコンポーネントとモーションライブラリを維持してください。例に従うためだけに依存関係をインストールまたは移行せず、インストール済みのパッケージとバージョン、および対応している API を確認してください。
  • インタラクティブなホバー、押下、開閉状態の変更には、CSS トランジションで十分な場合はそちらを優先してください。CSS トランジションは、現在補間中の状態から目標値を変更できます。
  • キーフレームはタイムラインを記述するもので、意図的なシーケンスや実際のローディングループに適しています。トランジションのように、変更された終点に自動的に追従するわけではありません。キャンセル、反転、目標値の変更には、明示的なオーケストレーションが必要です。
  • ジェスチャーの物理演算、連携する退出動作、共有レイアウトなど、複雑さに見合う場合は、既存の Motion/アニメーションライブラリの方法を使用してください。CSS だけでも JavaScript だけでも、コンポジターでの実行が保証されるわけではありません。

モーションを減らす設定を確認し、アクセシブルなプリミティブのフォーカス、キーボード操作、非表示コンテンツ、アンマウント時のライフサイクルを維持してください。以下の Motion の例では motion/react を使用しています。インポートや依存関係を黙って変更せず、実際にプロジェクトに存在するパッケージに合わせてください。

クイックスタート

プロジェクトトークンで方針が定まらない場合は、以下を出発点にしてください。

  1. この要素は画面に入る、または画面から退出するものですか? → ease-out を使用します
  2. 画面上の要素が移動するものですか? → ease-in-out を使用します
  3. ホバー/カラーのトランジションですか? → ease を使用します
  4. 高頻度で操作されるコントロールですか? → 即時または静的なフィードバックを優先し、理解を助ける具体的な必要がある場合にのみモーションを追加します

イージングの基本設計

ease-out(最も一般的)

ドロップダウン、モーダル、ツールチップなど、画面に入る、または画面から退出する要素といった、ユーザーが開始する操作に使用します。

/* Sorted weak to strong */
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);

効果が期待できる理由:最初の動きを速くすることで、反応がすぐに伝わります。要素は目的の位置に向かって「跳び」、その後落ち着きます。

ease-in-out(移動用)

すでに画面にある要素を移動または変形させる場合に使用します。車が加速してからブレーキをかけるような、自然な動きを模倣します。

/* Sorted weak to strong */
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
ease(ホバーエフェクト用)

ホバー状態とカラーのトランジションに使用します。非対称なカーブ(開始は速く、終わりは遅い)により、穏やかなアニメーションに上品な印象を与えます。

transition: background-color 150ms ease;
linear(一定速度または直接マッピング)

一定速度のモーション、実際の時間や進捗の可視化、スクロールやジェスチャーへの直接マッピングに適しています。入力に直接追従すべきコントロールに、遅れを伴う平滑化を加えないでください。

ease-in(ほとんど使わない)

フィードバックを遅らせるような、開始が遅いカーブは避けてください。意図的に動き出しを見せる場合は別のカーブが適していることもありますが、既存の操作の意味を維持し、結果を確認してください。

対になる要素のルール

関連する要素を連携させ、ひとまとまりに感じられるようにします。イージングと時間を共通にするのが有効な出発点です。既存のコンポーネントのライフサイクルを維持できるのであれば、オーバーレイとコンテンツの動きを意図的に非対称にするのも適切です。

/* Both use the same timing */
.modal {
  transition: transform 200ms ease-out;
}
.overlay {
  transition: opacity 200ms ease-out;
}

タイミングと時間

時間のガイドライン

要素の種類時間
マイクロインタラクション100-150ms
標準的な UI(ツールチップ、ドロップダウン)150-250ms
モーダル、ドロワー200-300ms

製品におけるデフォルトであり、普遍的な上限ではありません:

  • 製品の操作に伴う動きは短く保ち、通常は100~300ms以内にします。バウンスや弾性的な行き過ぎは避けます
  • 大きな要素は、小さな要素よりもゆっくりアニメーションさせます
  • 退場アニメーションは、登場アニメーションより約20%速くできます
  • 時間は移動距離とタスクの頻度に合わせ、利用可能なコンテンツの表示を遅らせないようにします
  • より長いジェスチャーの収束や表現豊かなブランド演出には、承認済みのブリーフと実際の文脈での検証が必要です。隣接するコントロールに対するデフォルトではありません
使用頻度

ユーザーがアニメーションをどの程度の頻度で目にするかを判断します。

  • 高頻度 → アニメーションを追加しないか、フィードバックを最小限にします
  • ときどき使用 → 標準的なアニメーションにします
  • まれ/初回のみ → より特別な演出にできます

たとえば、頻繁に開くコマンドパレットは、すぐに表示されるほうが適している場合があります。

アニメーションを使う場面

アニメーションを使う場面:

  • 空間的な一貫性を保つための登場/退場の遷移
  • 視覚的な連続性が役立つ状態変化
  • ユーザーの操作に対する応答(フィードバック)
  • 演出によって価値が加わる、使用頻度の低いインタラクション

アニメーションを使わない場面:

  • アニメーションによって応答が遅れる、繰り返しのキーボード操作やフォーカス移動
  • 頻繁に使用する要素のホバー効果
  • 静的なフィードバックですでに明確な、高頻度のコントロール
  • 滑らかさより速度が重要な場合

マーケティングと製品の違い:

  • マーケティング:より凝った演出や長めの時間が許容されます
  • 製品:素早く、目的に沿ったものにし、無意味な演出は避けます

スプリングアニメーション

スプリングは、ジェスチャーの勢いと、対象が切り替わったときの動きを保つのに役立ちます。ただし、必ずしも短い遷移より優れているわけではなく、選択肢のひとつです。時間ベースと物理ベースの設定では、動作が異なります。

スプリングを使う場面
  • 慣性を伴うドラッグ操作
  • アニメーションの途中で中断できるジェスチャー
  • スプリングがブリーフの要件にかなう、明確に承認された表現豊かなインタラクション
設定

バウンスなしの時間ベースの製品例(インストール済みの API/トークンに合わせて調整してください):

// Duration + bounce (easier to understand)
{ type: "spring", duration: 0.25, bounce: 0 }

従来の物理ベースの設定:

// Mass, stiffness, damping (more complex)
{ type: "spring", mass: 1, stiffness: 100, damping: 20 }
バウンスのガイドライン
  • 製品における基本の動きでは、バウンスや弾性的な行き過ぎを使いません。
  • ジェスチャー/境界への応答やブランド表現では、承認済みのブリーフで裏付けられている場合に限り、控えめな例外を設けることができます。「遊び心がある」というだけでは、あらゆる場所にバウンスを追加する理由になりません。
  • 選択したパラメーターは、実際のインタラクションで評価してください。バウンスの数値範囲は、デザイン上の承認を意味するものではありません。即時のフィードバックと、動きを減らす設定にした場合の動作を維持してください。
中断可能性

スプリングの実装によっては、対象が切り替わったときに現在の速度を保ちます。インストール済みの API で確認してください。CSS の遷移も、操作中に遷移先を切り替えられます。これらを、記述された開始時点からキーフレームのタイムラインを再生することと混同しないでください。

アニメーション中に、閉じる/再度開く/閉じる操作を試してください。最新の意図が優先され、古いコールバックが新しい状態をクリアしないようにします。遷移先の変更時やアンマウント時には、古い処理をキャンセルするか無効化してください。また、動きを減らす設定で時間が0になる経路では、発生しない可能性のあるイベントを待たないようにします。状態のクリーンアップは既存のプリミティブ内で行ってください。

レイアウトアニメーション(Framer Motion)

Motion のレイアウト投影では、flex-direction のような離散的なレイアウト変更を含め、サイズや位置の変化による視覚的な結果をアニメーション化できます。ただし、これによって CSS プロパティ自体が直接補間可能な値に変わるわけではありません。

layout Prop

レイアウトの変更を自動でアニメーションするには、任意の motion.* 要素に layout を追加します。

<motion.div layout className="element" />

ライブラリは、サポートされているレイアウトの変更を測定し、その視覚的な変化をアニメーションします。実際の利用環境で、子要素の歪み、クリッピング、スクロール、パフォーマンスを確認してください。この Prop は、滑らかさを保証するものではありません。

共有レイアウトアニメーション(layoutId)

別々の要素をつなぎ、一方からもう一方へ変形させます。

// Tab highlight: only rendered for active tab
{activeTab === tab ? (
  <motion.div layoutId="tab-indicator" className="highlight" />
) : null}

ユースケース:タブのハイライト、カード → モーダルへの展開、ボタン → ポップオーバーへの変形、ゴミ箱への操作(画像がコンテナ間を移動)。

クリエイティブなテクニック: layoutId は錯覚を生み出します。フィードバックのポップオーバーにある「プレースホルダー」は、実際には共有 layoutId を持つ <span> であり、本物のテキストエリアのプレースホルダーではありません。ボタンのテキストからポップオーバーのテキストへと変形します。

動的な高さのアニメーション

動的なコンテンツの高さを明示的に測定する必要がある場合は、プロジェクトにすでにある測定機能を再利用します。インストール済みであれば、react-use-measure が選択肢のひとつです。

import useMeasure from "react-use-measure";
const [ref, bounds] = useMeasure();

<motion.div animate={{ height: bounds.height }}>
  <div ref={ref}>{dynamicContent}</div>
</motion.div>

高さのアニメーションはレイアウトに影響します。空間的な連続性を保つ必要がある場合にのみ使用し、対象ブラウザーで測定してください。このスニペットのためだけに依存関係を追加しないでください。

AnimatePresence(詳細)

モード
  • "sync"(デフォルト):入場と退場を同時に再生します
  • "wait":退場が完了してから入場を開始します(コピー/チェックアイコンの切り替え)
  • "popLayout":退場する要素をレイアウトフローからすぐに取り除きます(UI を変形させる場合に適していることがよくあります)
方向に応じたトランジション

custom Prop を使って、退場するコンポーネント(状態が古くなっている)に動的なデータを渡します。

<AnimatePresence mode="popLayout" custom={direction}>
  <motion.div
    key={step}
    custom={direction}
    initial="enter"
    animate="center"
    exit="exit"
    variants={{
      enter: (dir) => ({ x: dir > 0 ? 100 : -100, opacity: 0 }),
      center: { x: 0, opacity: 1 },
      exit: (dir) => ({ x: dir > 0 ? -100 : 100, opacity: 0 }),
    }}
  />
</AnimatePresence>
キーに関するルール
  • AnimatePresence 内のアニメーション要素には、必ず key Prop を追加します
  • マウント時のアニメーションをスキップするには initial={false} を使います(アイコンの切り替え、ボタンの状態)
  • オーケストレーションを変更する前に、インストール済みのバージョンで素早く切り替える際の不具合を再現します。過去のバージョンへの固定を、自動的な修正として適用しないでください

モーション値とフック

モーション値は React のレンダーサイクルの外部で更新できます。これにより、フレームごとの React の状態更新を避けられますが、フレームレートが保証されるわけではありません。

useMotionValue:即時更新(ジェスチャー)
const x = useMotionValue(0);
// Update via x.set(newValue), read via x.get()
<motion.div style={{ x }} />

用途:ジェスチャーの直接追跡(ドラッグ距離 → スケール)、スプリングによる遅れが不自然に感じられるような、入力と 1:1 で対応するあらゆる場面。

useSpring:アニメーション付き更新(追従)
const x = useSpring(0, { mass: 0.1, damping: 16, stiffness: 71 });
// x.set(newValue) animates to it with spring physics

用途:カーソルの追従、慣性効果、入力に遅れて追従させたいあらゆる場面。

useTransform:値を別の値にマッピング
// Range mapping: y position [0, 300] → scale [1, 1.5]
const scale = useTransform(y, [0, 300], [1, 1.5]);

// Function form: format a value
const display = useTransform(angle, v => `${Math.round(v)}°`);

用途:スクロール連動エフェクト、カーソルとの距離に応じたエフェクト、値のフォーマット。

useMotionTemplate:モーション値を使った文字列補間
const clipPath = useMotionTemplate`inset(0px ${percent}% 0px 0px)`;
<motion.div style={{ clipPath }} />
MotionConfig:サブツリーのデフォルトトランジション
<MotionConfig transition={{ type: "spring", duration: 0.25, bounce: 0 }}>
  {/* All children use this transition unless overridden */}
</MotionConfig>

オーケストレーション(段階表示とシーケンス)

段階表示は、短いシーケンスによって読み取り順序が明確になる場合、または承認済みの演出上のタイミングがある場合にのみ使用します。シーケンスを待たなくても、実際のコンテンツや操作を利用できるようにしてください。

CSS による段階表示(ライブラリなし):
.item { animation: fadeSlideIn 200ms ease-out both; }
.item:nth-child(1) { animation-delay: 0ms; }
.item:nth-child(2) { animation-delay: 50ms; }
.item:nth-child(3) { animation-delay: 100ms; }
/* etc. */
FM による段階表示:
const container = {
  animate: { transition: { staggerChildren: 0.05 } }
};
const item = {
  initial: { opacity: 0, y: 20 },
  animate: { opacity: 1, y: 0 }
};

ルール:

  • 遅延は短く保ちます(項目間で 30~80ms)
  • 全体のシーケンス時間を見積もる際は、最後の項目の再生時間も含めます。開始間隔が 80ms、再生時間が 200ms の場合、10 項目では合計 920ms となり、通常の操作には遅すぎます
  • マーケティングページでは凝った演出も可能ですが、プロダクト UI は素早く動作する必要があります

アニメーション速度によるブランド表現

タイミングによって、承認済みのブランド方針を強調できます。

  • スピーディー: 即時、または非常に素早いフィードバック。
  • 落ち着いた印象: タスクを遅らせない範囲で、控えめに表現するトランジション。
  • 遊び心のある表現: 範囲を限定したスプリングや変形。バウンスを例外的に使う場合は、ブリーフに基づく理由が必要です。
  • プロダクト UI は、ブランドにかかわらず、基本的に素早く感じられるべきです。
  • マーケティングページ では、モーションを通じてブランドの個性を表現します。

フルイドインターフェース(目標)

流れるような連続性は、何かがどこから来たのか、どこへ移動したのかを伝える場合に役立ちます。すべてを変形させる必要はなく、シンプルな表示のほうがわかりやすいこともあります。

  • 共有レイアウトアニメーションは、ウェブでネイティブのような流動性を実現するのに最も近い手段です
  • 実際のタスク速度と、ユーザーが感じる連続性を照らし合わせてください。アニメーションによって遅延が許容されると思い込まないでください
  • 許可された構成上の判断を行う際に連続性を検討してください。変形を見せるためだけに既存の UI を並べ替えないでください
  • テキストの変形(例:ボタンラベルの変更)は、状態の変化をさりげなく強調します

パフォーマンス

意図した変化を表現できる場合は、動きに transform と opacity を優先してください。これらはレイアウト処理を避けられることが多く、コンポジターに適しています。ただし、実際の実行経路はブラウザー、アニメーション対象のコンテンツ、ライブラリーの実装、デバイスによって異なります。CSS 宣言やライブラリープロパティが、GPU 実行やフレームレートを保証するわけではありません。Motion のパフォーマンスガイドを参照してください。

  • 高さ、幅、パディング、マージンのアニメーションにはレイアウト処理が必要になる場合があります。正当な空間的変化がある場合にのみ使用し、実際の利用環境で測定してください。
  • ぼかし/フィルターの負荷は、描画領域、半径、デバイス、ブラウザーによって異なります。あらゆる環境で安全な 20px のしきい値はありません。未解決の遷移や状態の不具合を隠すために、ぼかしを使わないでください。
  • 各フレームの変更は局所的に保ってください。継承されるカスタムプロパティの更新はスタイル処理の範囲を広げる可能性があります。プロジェクトのトークンを維持し、手法を変更する前に影響を受ける子孫要素を確認してください。
  • 直接的なモーション値やサポートされているアニメーション API で十分な場合は、フレームごとの React の状態更新を避けてください。React のレンダリングや JavaScript のアニメーションがすべてフレーム落ちを引き起こすわけではありません。
  • will-change は、測定によって裏付けられた問題が確認された場合にのみ、関連する要素と期間に限定して追加してください。これはメモリーやスタッキングコンテキストのコストを伴うヒントであり、GPU 実行を強制するものではありません。MDN will-changeを参照してください。

パフォーマンスについて言及する場合は、実際のブラウザー/デバイス、インタラクション、パフォーマンストレースを記録してください。正当なレイアウトやぼかしのアニメーションについては、デスクトップと対象のモバイル/WebKit で確認してください。ビルドの成功、変換後の静止画、依存関係のインポート成功は、その根拠にはなりません。

アクセシビリティ

アニメーションによって、乗り物酔いのような症状や注意散漫を感じるユーザーもいます。

prefers-reduced-motion

既存の CSS またはライブラリーの仕組みを通じて、この設定に従ってください。装飾的な CSS アニメーションは、対象を限定したフォールバックで無効にできます。

.modal {
  animation: fadeIn 200ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .modal {
    animation: none;
  }
}
視差効果を抑える設定のガイドライン

視差効果を抑える設定では、必ずしもアニメーションを残す必要はありませんが、状態とフィードバックは維持する必要があります。静的な変化でも問題ありません。適切な場合は短いフェードを使えますが、すべてに適用できるフォールバックではありません。

  • 削除: transform による移動、拡大縮小、スライド、視差効果
  • 維持: 見える状態、フォーカス、正確なステータス。静的なフィードバック、または適切な短いフェードを使用してください
  • 任意の置き換え: スライドイン → フェードイン、拡大縮小 → 不透明度、複雑な動き → 静的またはシンプルな動き
  • 動画/アニメーション画像の自動再生を無効にし、代わりに再生ボタンを表示してください
  • 装飾的なループを停止し、選択した静止フレームを表示してください。負のアニメーション遅延だけでは、アニメーションは一時停止しません
Framer Motion の実装

オプション 1:コンポーネントごとのフック

import { useReducedMotion } from "motion/react";

function Component({ isOpen }) {
  const shouldReduceMotion = useReducedMotion();
  const closedX = shouldReduceMotion ? 0 : "-100%";

  return (
    <motion.div animate={{
      opacity: isOpen ? 1 : 0,
      x: isOpen ? 0 : closedX
    }} />
  );
}

オプション 2:既存のサブツリー設定

import { MotionConfig } from "motion/react";

// Honors the user preference for Motion transform/layout animation in this subtree
<MotionConfig reducedMotion="user">{children}</MotionConfig>

重複するアプリのラッパーを作成せず、適用範囲内の既存設定を使用または拡張してください。ドキュメントに記載されているデフォルト値は "never" です。"user" は transform/レイアウトのアニメーションを無効にしますが、その他の値では引き続きアニメーションする場合があります。不透明度、色、タイミング、CSS/サードパーティー製のループはそれぞれ個別に確認してください。MotionConfig のドキュメントを参照してください。これらのスニペットが説明しているのはモーションのみであり、アクセシブルなモーダルのライフサイクル全体ではありません。

タッチデバイスに関する考慮事項
/* Disable hover animations on touch devices */
@media (hover: hover) and (pointer: fine) {
  .element:hover {
    transform: scale(1.05);
  }
}

タッチデバイスではタップ時にホバーが発生するため、誤検出の原因になります。

実践的なヒント

よくあるシナリオのクイックリファレンスです。詳細な実装については、PRACTICAL-TIPS.md を参照してください。

シナリオ解決策
押下時のフィードバックがないまず静的なフィードバックを維持し、必要な場合にのみスコープを限定したスケールを使用します
要素が突然現れるscale(0) ではなく、scale(0.95) から開始します
アニメーションが不安定/揺れるヒントを試す前に、レイアウト、スナップ、レンダリングを確認します
ホバー時にちらつく親要素ではなく、子要素をアニメーションさせます
ポップオーバーの拡大位置がずれているtransform-origin をトリガーの位置に設定します
ツールチップが順番に表示されると遅く感じる最初のツールチップの後は、遅延/アニメーションを省略します
小さなボタンをタップしにくい共通のターゲットサイズのプリミティブを維持し、ヒットボックスの重なりを避けます
まだ違和感があるエフェクトを追加する前に、記録/再トリガーして状態とジオメトリを確認します
モバイルでホバーが発生する@media (hover: hover) and (pointer: fine) を使用します

イージングの判断フローチャート

要素はビューポートに入る、またはビューポートから出る動きですか? ├── はい → ease-out └── いいえ ├── 画面上を移動/変形していますか? │ └── はい → ease-in-out └── ホバーによる変化ですか? ├── はい → ease └── 一定の動きですか? ├── はい → linear └── デフォルト → ease-out

参考ファイル

  • PRACTICAL-TIPS.md:関連する実装レシピのみを読んでください。
  • Transitions candidate:特定のモーション上の課題に対処する固定レシピがある場合に限り参照してください。評価対象であり、採用またはインストール済みではありません。

コメント