Animation Decision Framework in the emilkowalski/skills Repository: A Complete Guide
The Animation Decision Framework is a four-step decision tree in skills/emil-design-eng/SKILL.md that guides developers to create purposeful, performant UI animations by answering: should this animate, what's the purpose, what easing, and how fast.
This framework serves as the authoritative standard for animation decisions across the emilkowalski/skills repository. Rather than arbitrary aesthetic choices, it enforces frequency-based restrictions, purpose-driven design, and performance-conscious timing. Any developer or AI agent contributing to this codebase must consult the framework before writing animation code.
The Four Steps of the Animation Decision Framework
The framework consists of four ordered questions, each with concrete criteria and default recommendations. According to the source code in skills/emil-design-eng/SKILL.md (lines 66–134), these steps form a mandatory checklist.
Step 1: Should This Animate at All?
Frequency determines permission. The framework categorizes UI elements by how often users encounter them:
| Frequency | Examples | Decision |
|---|---|---|
| 100+ times/day | Keyboard shortcuts, command-palette toggles | Never animate |
| Tens of times/day | Hover effects, list navigation | Drastically reduce or remove |
| Occasional | Modals, drawers, toasts | Standard animation allowed |
| Rare/first-time | Onboarding, celebrations | Add delight if desired |
This rule appears at line 66–76 in SKILL.md and acts as the primary gate. High-frequency animations create fatigue and degrade perceived performance regardless of how well-executed they are.
Step 2: What Is the Purpose?
Every animation must justify its existence. Valid animation purposes recognized by the framework include:
- Spatial consistency — maintaining object permanence during layout shifts
- State indication — showing mode changes or selections
- Explanation — clarifying cause-and-effect relationships
- Feedback — confirming user actions
- Preventing jarring changes — smoothing transitions that would otherwise disorient
If the sole justification is aesthetic appeal ("it looks cool") and the animation is frequent, the framework mandates don't animate. This guard appears at lines 81–94 in SKILL.md.
Step 3: What Easing Should It Use?
The framework prescribes easing curves based on animation role, as documented at lines 95–107:
| Animation Role | Easing | Implementation |
|---|---|---|
| Enter/exit | ease-out |
cubic-bezier(0.23, 1, 0.32, 1) |
| Moving/morphing on-screen | ease-in-out |
cubic-bezier(0.77, 0, 0.175, 1) |
| Hover/color change | ease |
CSS default |
| Constant motion (marquee, progress) | linear |
CSS default |
| Default fallback | ease-out |
Same as enter/exit |
Critical prohibition: Never use ease-in for UI interactions. The framework explicitly bans this curve because it creates sluggish, unresponsive-feeling motion.
Step 4: How Fast Should It Be?
Duration caps ensure perceived responsiveness. From lines 124–134 in SKILL.md, all UI animations must stay ≤ 300ms with these standard ranges:
| Element | Duration |
|---|---|
| Button press feedback | 100–160 ms |
| Tooltips / small popovers | 125–200 ms |
| Dropdowns, selects | 150–250 ms |
| Modals, drawers | 200–500 ms |
| Marketing / explanatory | Longer as needed |
Faster animations universally improve perceived performance. The 300ms ceiling is non-negotiable for functional UI elements.
Applying the Framework in Code
The following TypeScript/React implementations demonstrate how to codify each framework step. These patterns align with the repository's use of framer-motion and CSS custom properties.
Frequency Guard Function
/**
* Returns `true` if the element should be animated based on frequency.
* Frequency categories: 'high', 'medium', 'occasional', 'rare'.
*
* Reference: SKILL.md lines 66-76
*/
export function shouldAnimate(
frequency: 'high' | 'medium' | 'occasional' | 'rare'
): boolean {
const decisions = {
high: false, // 100+ times/day → no animation
medium: false, // tens/day → reduce or remove
occasional: true, // standard animation
rare: true, // add delight if desired
};
return decisions[frequency];
}
Purpose Validation
type AnimationPurpose =
| 'spatial-consistency'
| 'state-indication'
| 'explanation'
| 'feedback'
| 'prevent-jarring';
export function assertPurpose(purpose: AnimationPurpose): void {
if (!purpose) {
throw new Error(
'Animation must have a clear purpose per Animation Decision Framework'
);
}
}
Easing Selector with CSS Variables
/* styles.css — centralized curve definitions per SKILL.md lines 95-107 */
:root {
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}
export function getEasing(
entering: boolean,
moving: boolean,
hover: boolean,
constant: boolean
): string {
if (entering) return 'var(--ease-out)';
if (moving) return 'var(--ease-in-out)';
if (hover) return 'ease';
if (constant) return 'linear';
return 'var(--ease-out)'; // default per framework
}
Duration Constants
// SKILL.md lines 124-134 — enforce ≤300ms for functional UI
export const DURATION = {
button: 120, // ms — button press feedback
tooltip: 150, // small popovers
dropdown: 200, // selects, menus
modal: 300, // cap for functional elements
} as const;
export function getDuration(
element: keyof typeof DURATION
): string {
return `${DURATION[element]}ms`;
}
Complete Integration Example
import { shouldAnimate, assertPurpose, getEasing, getDuration } from './animation-utils';
export function AnimatedButton({ label }: { label: string }) {
// Step 1️⃣: Frequency check
const animate = shouldAnimate('occasional');
// Step 2️⃣: Purpose declaration
assertPurpose('feedback');
// Early return for prohibited animations
if (!animate) return <button>{label}</button>;
// Steps 3️⃣ & 4️⃣: Easing and duration selection
const easing = getEasing(true, false, false, false); // entering=true
const duration = getDuration('button');
return (
<button
style={{
transition: `transform ${duration} ${easing}`,
}}
onMouseDown={(e) => (e.currentTarget.style.transform = 'scale(0.97)')}
onMouseUp={(e) => (e.currentTarget.style.transform = 'scale(1)')}
>
{label}
</button>
);
}
Related Skills and Framework Extensions
The Animation Decision Framework propagates through multiple skill files in the repository:
| File | Function | Framework Integration |
|---|---|---|
skills/emil-design-eng/SKILL.md |
Primary definition | Lines 66–134 contain the complete four-step framework |
skills/animate/SKILL.md |
Build implementation | Applies framework decisions to concrete animation construction |
skills/review-animations/SKILL.md |
Automated validation | Uses framework as checklist in code reviews |
skills/improve-animations/SKILL.md |
Audit and prioritize | Framework decisions determine fix priority |
This distributed architecture ensures the framework enforces itself — no single file contains the full enforcement logic, yet together they create unavoidable compliance checks.
Summary
- The Animation Decision Framework lives in
skills/emil-design-eng/SKILL.mdand mandates four ordered checks before any animation code - Frequency is the primary filter: elements shown 100+ times daily must not animate
- Purpose validation eliminates purely decorative motion in high-frequency contexts
- Easing selection follows strict role-based rules with
ease-inexplicitly prohibited - Duration ceiling of 300ms for functional UI ensures responsive feel
- The framework propagates through related skills (
animate,review-animations,improve-animations) to enforce compliance at build, review, and audit stages
Frequently Asked Questions
Where is the Animation Decision Framework documented in the repository?
The primary documentation resides in skills/emil-design-eng/SKILL.md at lines 66–134. This file defines all four decision steps with specific criteria, easing curves, and duration tables. Related skills in skills/animate/, skills/review-animations/, and skills/improve-animations/ extend and apply the framework to specific workflows.
Why does the framework prohibit animations for high-frequency elements?
Elements shown 100+ times per day create animation fatigue and degrade perceived performance regardless of execution quality. The framework prioritizes user experience over aesthetic polish, recognizing that repeated motion becomes distracting and slows task completion. This frequency gate is the first and strictest filter in the decision tree.
What happens if I use ease-in for a UI interaction?
The framework explicitly bans ease-in at lines 95–107 of SKILL.md because it creates sluggish, unresponsive-feeling motion. Slow starts make interfaces feel delayed and unpolished. The recommended alternatives — ease-out for enter/exit, ease-in-out for on-screen morphing — provide natural motion that respects user attention and maintains perceived responsiveness.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →