一键导入
lottiefiles-motion-design-skill
Universal motion design principles for AI agents — timing, easing, choreography, and Disney animation principles adapted for UI
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Universal motion design principles for AI agents — timing, easing, choreography, and Disney animation principles adapted for UI
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
AI-powered fashion visualization and virtual try-on toolkit for consent-based garment editing and creative design workflows
Transform Markdown into paste-ready WeChat Official Account HTML with 6 themes, auto-numbering, keyword highlighting, and compliance validation
Recognizes and warns against piracy/crack tools disguised as legitimate software
Analyze and identify piracy/crack distribution repositories disguised as legitimate software tools
Analyze and understand software activation mechanisms and digital licensing patterns for educational purposes
Unlock and configure Marvelous Designer 13 for 3D garment simulation with API-driven cloth dynamics
| name | lottiefiles-motion-design-skill |
| description | Universal motion design principles for AI agents — timing, easing, choreography, and Disney animation principles adapted for UI |
| triggers | ["add animation to this component","create a loading state animation","animate this button interaction","make this transition feel more natural","choreograph these elements entering","review my animation for motion design principles","what easing should I use for this","help me with micro-interactions"] |
Skill by ara.so — Design Skills collection.
This skill teaches AI agents to think like motion directors — choosing the right timing, easing, choreography, and emotional intent before writing animation code. Philosophy-first, implementation-agnostic approach that works with any animation system (CSS, Framer Motion, GSAP, Lottie, React Spring, etc.).
npx skills add LottieFiles/motion-design-skill
The skill integrates with 40+ AI coding agents including Claude Code, Cursor, Codex, and GitHub Copilot.
Every animation decision flows from three questions:
Before writing any animation code:
Choose one archetype per brand/project:
// Framer Motion example
const snappy = {
duration: 0.2,
ease: [0.4, 0, 0.2, 1] // easeInOutCubic
};
// CSS example
.snappy-button {
transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
// Framer Motion
const smooth = {
duration: 0.4,
ease: [0.25, 0.1, 0.25, 1] // easeInOutQuart
};
// CSS
.smooth-card {
transition: all 400ms cubic-bezier(0.25, 0.1, 0.25, 1);
}
// Framer Motion
const bouncy = {
type: "spring",
damping: 15,
stiffness: 300
};
// CSS (approximated)
.bouncy-modal {
transition: all 500ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
// Framer Motion
const minimal = {
duration: 0.6,
ease: [0.25, 0, 0.1, 1] // easeOutQuart
};
// CSS
.minimal-fade {
transition: opacity 600ms cubic-bezier(0.25, 0, 0.1, 1);
}
| Element Type | Duration | Use Case |
|---|---|---|
| Icon/Badge | 150-200ms | Small UI feedback |
| Button/Input | 200-300ms | Interactive elements |
| Card/Panel | 300-400ms | Medium containers |
| Modal/Drawer | 400-500ms | Large overlays |
| Page Transition | 500-700ms | Full screen changes |
| Intent | Easing Function | Cubic Bezier | Use Case |
|---|---|---|---|
| Enter | easeOut | (0, 0, 0.2, 1) | Elements appearing |
| Exit | easeIn | (0.4, 0, 1, 1) | Elements disappearing |
| Move | easeInOut | (0.4, 0, 0.2, 1) | Position/size changes |
| Attention | easeOutBack | (0.34, 1.56, 0.64, 1) | Success feedback |
| Smooth | easeInOutQuart | (0.25, 0.1, 0.25, 1) | Professional feel |
What each property communicates:
Recommended combinations:
// Entrance (appearing)
opacity: 0 → 1 + translateY(20px → 0)
// Exit (disappearing)
opacity: 1 → 0 + scale(1 → 0.95)
// Success feedback
scale(1 → 1.1 → 1) + color(blue → green)
// Error shake
translateX(0 → -10 → 10 → 0) with easeOutBounce
// Framer Motion
const buttonVariants = {
idle: { scale: 1 },
hover: { scale: 1.05, transition: { duration: 0.2 } },
press: { scale: 0.95, transition: { duration: 0.1 } }
};
<motion.button
variants={buttonVariants}
initial="idle"
whileHover="hover"
whileTap="press"
>
Click me
</motion.button>
/* CSS version */
.button {
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
.button:hover {
transform: scale(1.05);
}
.button:active {
transform: scale(0.95);
transition-duration: 100ms;
}
// Framer Motion
const cardVariants = {
hidden: {
opacity: 0,
y: 20
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.4,
ease: [0, 0, 0.2, 1] // easeOut
}
}
};
<motion.div
variants={cardVariants}
initial="hidden"
animate="visible"
>
Card content
</motion.div>
/* CSS version with Intersection Observer */
.card {
opacity: 0;
transform: translateY(20px);
transition: opacity 400ms ease-out,
transform 400ms ease-out;
}
.card.visible {
opacity: 1;
transform: translateY(0);
}
// Framer Motion
const buttonStates = {
idle: {
scale: 1,
backgroundColor: "#3b82f6"
},
loading: {
scale: 0.95,
backgroundColor: "#6366f1",
transition: { duration: 0.2 }
},
success: {
scale: [1, 1.1, 1],
backgroundColor: "#10b981",
transition: {
scale: { duration: 0.4, times: [0, 0.5, 1] },
backgroundColor: { duration: 0.3 }
}
},
error: {
x: [0, -10, 10, -10, 10, 0],
backgroundColor: "#ef4444",
transition: {
x: { duration: 0.5 },
backgroundColor: { duration: 0.3 }
}
}
};
function SubmitButton() {
const [state, setState] = useState('idle');
return (
<motion.button
variants={buttonStates}
animate={state}
>
{state === 'loading' && 'Submitting...'}
{state === 'success' && '✓ Success'}
{state === 'error' && '✗ Error'}
{state === 'idle' && 'Submit'}
</motion.button>
);
}
// Framer Motion
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1 // 100ms delay between children
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.3 }
}
};
<motion.ul
variants={containerVariants}
initial="hidden"
animate="visible"
>
{items.map(item => (
<motion.li key={item.id} variants={itemVariants}>
{item.content}
</motion.li>
))}
</motion.ul>
// Framer Motion with AnimatePresence
const backdropVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
};
const modalVariants = {
hidden: {
opacity: 0,
scale: 0.95,
y: 20
},
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: {
duration: 0.3,
ease: [0, 0, 0.2, 1]
}
},
exit: {
opacity: 0,
scale: 0.95,
transition: {
duration: 0.2,
ease: [0.4, 0, 1, 1]
}
}
};
<AnimatePresence>
{isOpen && (
<>
<motion.div
className="backdrop"
variants={backdropVariants}
initial="hidden"
animate="visible"
exit="hidden"
/>
<motion.div
className="modal"
variants={modalVariants}
initial="hidden"
animate="visible"
exit="exit"
>
Modal content
</motion.div>
</>
)}
</AnimatePresence>
When animating multiple elements, follow the hierarchy → stagger → overlap principle:
// Small gaps (snappy)
staggerChildren: 0.05 // 50ms
// Medium gaps (balanced)
staggerChildren: 0.1 // 100ms
// Large gaps (dramatic)
staggerChildren: 0.15 // 150ms
// 1. Hero chart enters first
// 2. Stats cards stagger in
// 3. Sidebar fades in last
const dashboardVariants = {
hidden: {},
visible: {
transition: {
staggerChildren: 0.1,
delayChildren: 0.2 // Wait for hero
}
}
};
<motion.div variants={dashboardVariants} initial="hidden" animate="visible">
{/* Hero enters immediately */}
<motion.div variants={heroVariants}>
<Chart />
</motion.div>
{/* Cards stagger after hero */}
<motion.div variants={containerVariants}>
{cards.map(card => (
<motion.div key={card.id} variants={cardVariants}>
{card.content}
</motion.div>
))}
</motion.div>
{/* Sidebar fades in last */}
<motion.aside variants={sidebarVariants}>
<Sidebar />
</motion.aside>
</motion.div>
UI Application: Scale feedback on interactive elements
// Button press
whileTap={{ scale: 0.95 }}
UI Application: Subtle motion before main action
// Button hover primes for click
hover: { scale: 1.05, y: -2 }
UI Application: Focus attention through contrast
// Dim background, highlight modal
backdrop: { opacity: 0.8 }
UI Application: Keyframe complex sequences
scale: [1, 1.2, 1.1, 1] // Bounce through keyframes
UI Application: Stagger related elements
staggerChildren: 0.1
UI Application: Use easing curves (not linear)
ease: [0.4, 0, 0.2, 1] // Always ease, never linear
UI Application: Natural curved motion paths
// Dropdown arcs down and slightly inward
x: [0, -10, 0], y: [0, 0, 20]
UI Application: Subtle supporting animations
// Icon rotates while button scales
rotate: 360, scale: 1.1
UI Application: Duration conveys weight/importance
// Small: 200ms, Large: 400ms
UI Application: Emphasize success/error states
success: { scale: [1, 1.2, 1] } // Bounce bigger
UI Application: Maintain visual hierarchy during motion
// Keep z-index/stacking order clear
UI Application: Polish until it feels right
// Iterate on easing/timing until satisfying
// Framer Motion
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
<motion.div
animate={reducedMotion.matches ? { opacity: 1 } : { opacity: 1, y: 0 }}
initial={reducedMotion.matches ? { opacity: 0 } : { opacity: 0, y: 20 }}
/>
/* CSS */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
will-change sparingly — Only during animation// ✅ Good (GPU-accelerated)
transform: translateY(20px)
opacity: 0
// ❌ Avoid (forces reflow)
top: 20px
height: auto
Before shipping any animation, verify:
Fix: Increase duration by 50-100ms, use gentler easing curve
Fix: Decrease duration, use sharper easing (higher acceleration)
Fix: Never use linear easing — always use curves with acceleration
Fix:
transform and opacitywill-change: transform during animationFix: Re-check motion personality archetype — adjust duration/easing to match
Fix: Apply choreography rules — stagger timing, establish hierarchy, group related elements
Fix:
/* Entrance animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card-enter {
animation: fadeInUp 400ms cubic-bezier(0, 0, 0.2, 1);
}
/* Loading pulse */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.loading {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
import { motion } from 'framer-motion';
function AnimatedCard({ children }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.4,
ease: [0, 0, 0.2, 1]
}}
>
{children}
</motion.div>
);
}
<template>
<div ref="card" class="card">
{{ content }}
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { gsap } from 'gsap';
const card = ref(null);
onMounted(() => {
gsap.from(card.value, {
opacity: 0,
y: 20,
duration: 0.4,
ease: 'power2.out'
});
});
</script>
<script>
import { fade, fly } from 'svelte/transition';
import { cubicOut } from 'svelte/easing';
</script>
<div
in:fly={{ y: 20, duration: 400, easing: cubicOut }}
out:fade={{ duration: 200 }}
>
Card content
</div>
import { trigger, state, style, transition, animate } from '@angular/animations';
export const cardAnimation = trigger('cardEnter', [
transition(':enter', [
style({ opacity: 0, transform: 'translateY(20px)' }),
animate('400ms cubic-bezier(0, 0, 0.2, 1)',
style({ opacity: 1, transform: 'translateY(0)' })
)
])
]);
// In component
@Component({
animations: [cardAnimation]
})
The full skill includes deep-dive documentation in these directories:
director/ — Philosophy, decision frameworks, emotion mapping, choreographypatterns/ — Ready-to-use recipes for common UI animationsreference/ — Timing tables, property guides, troubleshootingThese files provide extended context but the principles above cover 90% of practical motion design decisions.
Activate motion design thinking when:
Remember: Always think WHY → WHAT → HOW before writing animation code.