# Animation Decision Framework in the emilkowalski/skills Repository: A Complete Guide

> Explore the Animation Decision Framework in emilkowalski/skills. This guide helps you build purposeful, performant UI animations with a simple four-step process. Learn to animate effectively.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: deep-dive
- Published: 2026-08-07

---

**The Animation Decision Framework is a four-step decision tree in [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/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`](https://github.com/emilkowalski/skills/blob/main/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`](https://github.com/emilkowalski/skills/blob/main/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`](https://github.com/emilkowalski/skills/blob/main/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`](https://github.com/emilkowalski/skills/blob/main/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

```typescript
/**
 * 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

```typescript
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

```css
/* 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);
}

```

```typescript
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

```typescript
// 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

```tsx
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`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) | **Primary definition** | Lines 66–134 contain the complete four-step framework |
| [`skills/animate/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/SKILL.md) | Build implementation | Applies framework decisions to concrete animation construction |
| [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) | Automated validation | Uses framework as checklist in code reviews |
| [`skills/improve-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/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.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) and 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-in` explicitly 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`](https://github.com/emilkowalski/skills/blob/main/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`](https://github.com/emilkowalski/skills/blob/main/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.