web-animation-design について
web-animation-design は、Emil Kowalski の animations.dev コースを基に、Web UI アニメーションの設計とレビューを支援するスキルです。イージング、時間、パフォーマンス、スプリング、視差効果を抑えるアクセシビリティを扱い、レビュー形式も定めています。
主な機能
- 01操作の種類に応じたイージング選択
- 02UI アニメーションの時間と頻度の指針
- 03スプリングアニメーションの設定指針
- 04CSS と JavaScript のパフォーマンス指針
- 05視差効果を抑えるアクセシビリティ対応
- 06レビューで必須の比較テーブル
活用シーン
- 01モーダルやドロップダウンのイージング選択
- 02頻繁な UI 操作のアニメーション時間設定
- 03アニメーションのパフォーマンス案の確認
- 04視差効果を抑える設定の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vercel-labs/ open-agents --skill web-animation-designインストール後、Claude Code で/web-animation-designを入力して読み込まれたか確認します。
同梱ファイルPRACTICAL-TIPS.md
詳細
Web アニメーションデザイン
Emil Kowalski の「Animations on the Web」コースに基づく、自然なアニメーションを作成するための包括的なガイドです。
初回の応答
このスキルが具体的な質問なしで初めて呼び出された場合は、次の内容だけを返してください。
Emil Kowalski の animations.dev コースに基づいたアニメーションについてお手伝いします。
ユーザーから質問されるまでは、ほかの情報を提供しないでください。
レビュー形式(必須)
アニメーションをレビューするときは、必ず Markdown の表を使用してください。「Before:」と「After:」を別々の行に並べる形式のリストは使用しないでください。常に次のような実際の Markdown の表を出力してください。
| Before | After |
|---|---|
transform: scale(0) | transform: scale(0.95) |
animation: fadeIn 400ms ease-in | animation: fadeIn 200ms ease-out |
| モーション低減のサポートなし | @media (prefers-reduced-motion: reduce) {...} |
誤った形式(絶対に使用しないでください):
Before: transform: scale(0)
After: transform: scale(0.95)
────────────────────────────
Before: 400ms duration
After: 200ms
正しい形式:| Before | After | の列を持つ単一の Markdown の表を使用し、問題ごとに 1 行を設けます。
クイックスタート
アニメーションを決めるときは、まず次の質問をします。
- この要素は画面に入るところですか、それとも画面から出るところですか? →
ease-outを使用します - 画面上の要素が移動しますか? →
ease-in-outを使用します - ホバーや色のトランジションですか? →
easeを使用します - ユーザーが 1 日に 100 回以上目にするものですか? → アニメーションを付けないでください
イージングの指針
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(UI では避ける)
次の場合にのみ使用します。
- 速度が一定のアニメーション(マーキー、ティッカー)
- 時間の可視化(長押し削除の進行状況インジケーター)
linear は、インタラクティブな要素では機械的で不自然に感じられます。
ease-in(ほぼ使わない)
UI アニメーションでは避けてください。 開始が遅く、視覚的なフィードバックが遅れるため、インターフェースがもたついて感じられます。
ペア要素のルール
一緒にアニメーションする要素には、同じイージングと継続時間を使用する必要があります。モーダルとオーバーレイ、ツールチップと矢印、ドロワーと背景は、ひとまとまりで動くなら、ひとまとまりとして感じられるようにします。
/* Both use the same timing */
.modal {
transition: transform 200ms ease-out;
}
.overlay {
transition: opacity 200ms ease-out;
}
タイミングと継続時間
継続時間の目安
| 要素の種類 | 継続時間 |
|---|---|
| マイクロインタラクション | 100-150ms |
| 標準的な UI(ツールチップ、ドロップダウン) | 150-250ms |
| モーダル、ドロワー | 200-300ms |
ルール:
- UI アニメーションは 300ms 未満に抑えます
- 大きな要素は、小さな要素よりゆっくりアニメーションします
- 退出アニメーションは、登場時より約 20% 速くできます
- 継続時間を移動距離に合わせます。移動距離が長いほど、継続時間も長くします
頻度
ユーザーがそのアニメーションを目にする頻度を判断します。
- 1 日に 100 回以上 → アニメーションなし(または大幅に低減)
- 時々使用 → 標準的なアニメーション
- まれに使用/初回 → もう少し特別な演出も可能
例: Raycast は、ユーザーが 1 日に何百回も開くため、アニメーションを一切使用しません。
アニメーションを使うタイミング
アニメーションを使うべき場面:
- 空間的な一貫性を保つための表示/終了トランジション
- 視覚的な連続性によってメリットが得られる状態変化
- ユーザー操作への応答(フィードバック)
- アニメーションによって楽しさが加わる、使用頻度の低いインタラクション
アニメーションを使わない場面:
- キーボードで開始する操作
- 頻繁に使う要素のホバー効果
- 1日に100回以上操作する要素
- 滑らかさより速度が重要な場合
マーケティングと製品の違い:
- マーケティング:より凝った表現にし、長めの再生時間も許容します
- 製品:高速で、目的が明確で、決して無駄がありません
スプリングアニメーション
スプリングは固定の再生時間を持たず、実際の物理法則を再現するため、より自然に感じられます。
スプリングを使う場面
- 慣性を伴うドラッグ操作
- 「生きている」ように感じさせたい要素(Dynamic Island)
- アニメーションの途中で中断できるジェスチャー
- 有機的で遊び心のあるインターフェース
設定
Apple の方法(推奨):
// Duration + bounce (easier to understand)
{ type: "spring", duration: 0.5, bounce: 0.2 }
従来の物理ベースの方法:
// Mass, stiffness, damping (more complex)
{ type: "spring", mass: 1, stiffness: 100, damping: 10 }
バウンスのガイドライン
- ほとんどの UI ではバウンスを避けます
- ドラッグして閉じる操作や、遊び心のあるインタラクションにはバウンスを使います
- バウンスを使う場合は、控えめにします(0.1~0.3)
中断への対応
スプリングは中断されても速度を維持しますが、CSS アニメーションは最初から再開します。そのため、動作中にユーザーが変更する可能性のあるジェスチャーには、スプリングが適しています。
パフォーマンス
最も重要なルール
アニメーション化するのは transform と opacity だけにします。これらはレイアウトとペイントの段階をスキップし、GPU 上で完結して実行されます。
アニメーション化を避けるプロパティ:
padding、margin、height、width(レイアウトが発生します)- 20px を超える
blurフィルター(特に Safari では処理負荷が高くなります) - 深いコンポーネントツリー内の CSS 変数
最適化テクニック
/* Force GPU acceleration */
.animated-element {
will-change: transform;
}
React 固有の注意点:
- 可能な場合は、React のレンダーサイクルの外でアニメーションさせます
- state の代わりに refs を使って、スタイルを直接更新します
- 毎フレームの再レンダーは、フレーム落ちの原因になります
Framer Motion:
// Hardware accelerated (transform as string)
<motion.div animate={{ transform: "translateX(100px)" }} />
// NOT hardware accelerated (more readable)
<motion.div animate={{ x: 100 }} />
CSS と JavaScript
- CSS アニメーションはメインスレッド外で実行されるため、負荷がかかっているときも滑らかです
- JavaScript アニメーション(Framer Motion、React Spring)は
requestAnimationFrameを使います - 単純で、あらかじめ決まっているアニメーションには CSS が適しています
- 動的で、中断可能なアニメーションには JavaScript が適しています
アクセシビリティ
アニメーションは、一部のユーザーに乗り物酔いや注意散漫を引き起こすことがあります。
prefers-reduced-motion
アニメーションを追加する際は、無効にするためのメディアクエリも追加します。
.modal {
animation: fadeIn 200ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
.modal {
animation: none;
}
}
モーションを抑えるためのガイドライン
- アニメーション化する要素ごとに、専用の
prefers-reduced-motionメディアクエリが必要です animation: noneまたはtransition: noneを設定します(!importantは使いません)- opacity や色の例外は設けず、すべてのアニメーションを無効にします
- 動画を自動再生する代わりに、再生ボタンを表示します
Framer Motion での実装
import { useReducedMotion } from "framer-motion";
function Component() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
initial={shouldReduceMotion ? false : { opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
/>
);
}
タッチデバイスに関する考慮事項
/* Disable hover animations on touch devices */
@media (hover: hover) and (pointer: fine) {
.element:hover {
transform: scale(1.05);
}
}
タッチデバイスではタップ時にホバーが発生し、誤検知につながります。
実用的なヒント
よくある場面ですぐに参照できるガイドです。詳しい実装については、PRACTICAL-TIPS.md を参照してください。
| 場面 | 解決策 |
|---|---|
| ボタンの反応をよくする | :active に transform: scale(0.97) を追加する |
| 要素が何もないところから現れる | scale(0) ではなく scale(0.95) から始める |
| アニメーションが揺れる/ちらつく | will-change: transform を追加する |
| ホバー時にちらつく | 親ではなく子要素をアニメーション化する |
| ポップオーバーの拡大位置がずれる | transform-origin をトリガーの位置に設定する |
| ツールチップが順番に表示されると遅く感じる | 最初のツールチップの後は遅延/アニメーションを省略する |
| 小さいボタンがタップしにくい | 最小タップ領域を 44px にする(疑似要素を使用) |
| まだ何か違和感がある | 目立たないぼかし(20px 未満)を加えて違和感を和らげる |
| モバイルでホバーが発生する | @media (hover: hover) and (pointer: fine) を使う |
イージング選択フローチャート
要素はビューポートに入る、またはビューポートから出ますか? ├── はい → ease-out └── いいえ ├── 画面上で移動または変形していますか? │ └── はい → ease-in-out └── ホバー時の変化ですか? ├── はい → ease └── 一定の動きですか? ├── はい → linear └── デフォルト → ease-out
参考ファイル
- PRACTICAL-TIPS.md - 一般的なアニメーションのシナリオに関する詳細な実装