mastering-animate-presence
raphaelsalaja/skill · Agent Skill
檢查Audit Motion和Framer Motion的進出/存在模板,並使用AnimatePresence進行實踐修正。
前往安裝 ↓總覽
| 發布者 | |
|---|---|
| 儲存庫 | raphaelsalaja/skill ↗ |
| 技能資料夾 | mastering-animate-presence |
| 星 | 42 |
| 分支 | 5 |
| 附帶檔案 | 1 |
| 授權 | MIT |
| 最近更新 | 2026年3月17日 |
| 連結 | 技能原始碼 ↗ · 儲存庫 ↗ |
Mozgi 檢查
我們在技能檔案中發現的內容。詳細資訊需訂閱。
安裝
技能就是一個含 SKILL.md 的資料夾。選擇你的代理,在專案根目錄的終端機(macOS、Linux 或 Windows 的 Git Bash)執行指令。
把提示貼到 Claude、Codex 或其他助理:它會自行檢查並安裝技能。
Install the “mastering-animate-presence” skill from mozgi.io (https://mozgi.io/skills/raphaelsalaja-mastering-animate-presence/) for me. 1. Download the archive: https://mozgi.io/skills/raphaelsalaja-mastering-animate-presence/download If it fails, take the skill folder from the source: https://github.com/raphaelsalaja/skill/tree/main/skills/mastering-animate-presence 2. Read SKILL.md and the other files. Check the skill is safe: no hidden commands, no sending data elsewhere, no scripts doing something other than described. If anything looks suspicious, stop and tell me. 3. Put the “mastering-animate-presence” folder into your agent's skills folder: .claude/skills/ for Claude Code, .agents/skills/ for Codex, .cursor/skills/ for Cursor, .gemini/skills/ for Gemini CLI. Inside the project if we are in one, otherwise in the home folder (~/.claude/skills/ etc.). 4. Make sure <skills folder>/mastering-animate-presence/SKILL.md exists. 5. Briefly tell me what this skill does and offer to try it on my task. If you cannot do a step yourself, tell me what to click or type.
git clone --depth 1 --branch main https://github.com/raphaelsalaja/skill.git /tmp/skill mkdir -p .claude/skills cp -r /tmp/skill/skills/mastering-animate-presence .claude/skills/mastering-animate-presence
重新啟動 Claude Code — 技能就會出現在清單中,手動呼叫:/mastering-animate-presence。要套用到所有專案,請放在 ~/.claude/skills。
git clone --depth 1 --branch main https://github.com/raphaelsalaja/skill.git /tmp/skill mkdir -p .agents/skills cp -r /tmp/skill/skills/mastering-animate-presence .agents/skills/mastering-animate-presence
Codex 會從目前資料夾往上到儲存庫根目錄,在 .agents/skills 中尋找技能;用 /skills 或 $mastering-animate-presence 呼叫。所有專案:~/.agents/skills。
git clone --depth 1 --branch main https://github.com/raphaelsalaja/skill.git /tmp/skill mkdir -p .cursor/skills cp -r /tmp/skill/skills/mastering-animate-presence .cursor/skills/mastering-animate-presence
Cursor 啟動時會載入技能;在 Agent 聊天輸入「/」呼叫。Cursor 也會讀取 .claude/skills 與 .agents/skills。所有專案:~/.cursor/skills。
git clone --depth 1 --branch main https://github.com/raphaelsalaja/skill.git /tmp/skill mkdir -p .gemini/skills cp -r /tmp/skill/skills/mastering-animate-presence .gemini/skills/mastering-animate-presence
任務符合時,Gemini CLI 會提議啟用技能,由你確認。所有專案:~/.gemini/skills。
安裝前請先檢視 SKILL.md 與相關檔案。點擊檔案即可預覽。
SKILL.md 內容
作者原文 · 唯讀Mastering AnimatePresence
Review Motion code for AnimatePresence and exit animation best practices.
How It Works
1. Read the specified files (or prompt user for files/pattern)
2. Check against all rules below
3. Output findings in file:line format
Rule Categories
| Priority | Category | Prefix |
|----------|----------|--------|
| 1 | Exit Animations | exit- |
| 2 | Presence Hooks | presence- |
| 3 | Mode Selection | mode- |
| 4 | Nested Exits | nested- |
Rules
Exit Animation Rules
exit-requires-wrapper
Conditional motion elements must be wrapped in AnimatePresence.
Fail:
{isVisible && (
<motion.div exit={{ opacity: 0 }} />
)}
Pass:
<AnimatePresence>
{isVisible && (
<motion.div exit={{ opacity: 0 }} />
)}
</AnimatePresence>
exit-prop-required
Elements inside AnimatePresence should have exit prop defined.
Fail:
<AnimatePresence>
{isOpen && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
)}
</AnimatePresence>
Pass:
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
exit-key-required
Dynamic lists inside AnimatePresence must have unique keys.
Fail:
<AnimatePresence>
{items.map((item, index) => (
<motion.div key={index} exit={{ opacity: 0 }} />
))}
</AnimatePresence>
Pass:
<AnimatePresence>
{items.map((item) => (
<motion.div key={item.id} exit={{ opacity: 0 }} />
))}
</AnimatePresence>
exit-matches-initial
Exit animation should mirror initial for symmetry.
Fail:
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ scale: 0 }}
/>
Pass:
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
/>
Presence Hook Rules
presence-hook-in-child
useIsPresent must be called from child of AnimatePresence, not parent.
Fail:
function Parent() {
const isPresent = useIsPresent(); // Wrong location
return (
<AnimatePresence>
{show && <Child />}
</AnimatePresence>
);
}
Pass:
function Child() {
const isPresent = useIsPresent(); // Correct location
return <motion.div data-exiting={!isPresent} />;
}
presence-safe-to-remove
When using usePresence, always call safeToRemove after async work.
Fail:
function AsyncComponent() {
const [isPresent, safeToRemove] = usePresence();
useEffect(() => {
if (!isPresent) {
cleanup(); // Never calls safeToRemove
}
}, [isPresent]);
}
Pass:
function AsyncComponent() {
const [isPresent, safeToRemove] = usePresence();
useEffect(() => {
if (!isPresent) {
cleanup().then(safeToRemove);
}
}, [isPresent, safeToRemove]);
}
presence-disable-interactions
Disable interactions on exiting elements using isPresent.
Fail:
function Card() {
const isPresent = useIsPresent();
return <button onClick={handleClick}>Click</button>;
// Button clickable during exit
}
Pass:
function Card() {
const isPresent = useIsPresent();
return (
<button onClick={handleClick} disabled={!isPresent}>
Click
</button>
);
}
Mode Selection Rules
mode-wait-doubles-duration
Mode "wait" nearly doubles animation duration; adjust timing accordingly.
Fail:
<AnimatePresence mode="wait">
<motion.div transition={{ duration: 0.3 }} />
</AnimatePresence>
// Total time: ~600ms (too slow)
Pass:
<AnimatePresence mode="wait">
<motion.div transition={{ duration: 0.15 }} />
</AnimatePresence>
// Total time: ~300ms (acceptable)
mode-sync-layout-conflict
Mode "sync" causes layout conflicts; position exiting elements absolutely.
Fail:
<AnimatePresence mode="sync">
{items.map(item => (
<motion.div exit={{ opacity: 0 }}>{item}</motion.div>
))}
</AnimatePresence>
// Exiting and entering elements compete for space
Pass:
<AnimatePresence mode="popLayout">
{items.map(item => (
<motion.div exit={{ opacity: 0 }}>{item}</motion.div>
))}
</AnimatePresence>
mode-pop-layout-for-lists
Use popLayout mode for list reordering animations.
Fail:
<AnimatePresence>
{items.map(item => <ListItem key={item.id} />)}
</AnimatePresence>
// Layout shifts during exit
Pass:
<AnimatePresence mode="popLayout">
{items.map(item => <ListItem key={item.id} />)}
</AnimatePresence>
Nested Exit Rules
nested-propagate-required
Nested AnimatePresence must use propagate prop for coordinated exits.
Fail:
<AnimatePresence>
{isOpen && (
<motion.div exit={{ opacity: 0 }}>
<AnimatePresence>
{items.map(item => (
<motion.div key={item.id} exit={{ scale: 0 }} />
))}
</AnimatePresence>
</motion.div>
)}
</AnimatePresence>
// Children vanish instantly when parent exits
Pass:
<AnimatePresence propagate>
{isOpen && (
<motion.div exit={{ opacity: 0 }}>
<AnimatePresence propagate>
{items.map(item => (
<motion.div key={item.id} exit={{ scale: 0 }} />
))}
</AnimatePresence>
</motion.div>
)}
</AnimatePresence>
nested-consistent-timing
Parent and child exit durations should be coordinated.
Fail:
// Parent exits in 100ms, children in 500ms
<motion.div exit={{ opacity: 0 }} transition={{ duration: 0.1 }}>
<motion.div exit={{ scale: 0 }} transition={{ duration: 0.5 }} />
</motion.div>
Pass:
// Parent waits for children or exits simultaneously
<motion.div exit={{ opacity: 0 }} transition={{ duration: 0.2 }}>
<motion.div exit={{ scale: 0 }} transition={{ duration: 0.15 }} />
</motion.div>
Output Format
When reviewing files, output findings as:
file:line - [rule-id] description of issue
Example:
components/modal/index.tsx:23 - [exit-requires-wrapper] Conditional motion.div not wrapped in AnimatePresence
components/modal/index.tsx:45 - [exit-prop-required] Missing exit prop on motion element
Summary Table
After findings, output a summary:
| Rule | Count | Severity |
|------|-------|----------|
| exit-requires-wrapper | 2 | HIGH |
| exit-prop-required | 3 | HIGH |
| mode-wait-doubles-duration | 1 | MEDIUM |
References
常見問題
mastering-animate-presence 技能有什麼用途?
檢查Audit Motion和Framer Motion的進出/存在模板,並使用AnimatePresence進行實踐修正。
如何安裝 mastering-animate-presence?
打開本頁的「安裝」區塊並選擇你的代理 — Claude Code、Codex、Cursor 或 Gemini CLI:那裡有把技能資料夾複製到正確位置的現成指令。也可以複製「透過助理」分頁中的提示,讓助理自行檢查並安裝技能。
mastering-animate-presence 適用哪些 AI 模型?
技能是純 Markdown 指示(Agent Skills 格式),因此 mastering-animate-presence 適用任何讀取 SKILL.md 的代理:Claude Code、Codex、Cursor、Gemini CLI、GitHub Copilot、OpenCode 等。只有在任務符合技能描述時,模型才會載入指示。
mastering-animate-presence 是免費的嗎?
此技能由 raphaelsalaja 公開發布,採用 MIT 授權。技能本身免費 — 你只需向 AI 供應商支付實際使用的 token 費用。
安裝技能安全嗎?
技能是代理會遵循的指示,安裝前請閱讀 SKILL.md 及相關檔案。技能資料夾中的腳本只有在代理執行時才會運行 — 留意它的動作,不要授予多餘權限。
技能和 MCP 伺服器或外掛有何不同?
MCP 伺服器或外掛為模型提供新工具 — 會執行並回傳結果的程式碼。技能提供的是知識與流程:如何處理任務、要遵循哪些步驟、好的成果長什麼樣子。它不需要伺服器或 API 金鑰。