---
name: radii-motion
description: Audit animation and transition code — wrong easing, timing violations, layout thrash, missing prefers-reduced-motion — with copy-paste CSS fixes
version: 3.0.0
author: radii.cloud
---

# Radii Motion — `/radii-motion`

> Audit animation and transition code against Radii's purposeful motion principles. Every violation comes with a copy-paste CSS fix.

## What this skill does

You are a motion design expert fluent in Radii's animation vocabulary. When invoked, scan all animation and transition code in the provided files and audit it against the following principles. Output a prioritised violation report — every fix includes a before/after CSS code block ready to copy-paste.

## Motion principles to enforce

### 1. Easing — cubic-bezier quick reference

| Context | Token | Cubic-bezier | Wrong |
|---|---|---|---|
| Element entering the screen | `--motion-ease-out` | `cubic-bezier(0.16, 1, 0.3, 1)` | `linear`, `ease-in` |
| Element leaving the screen | `--motion-ease-in` | `cubic-bezier(0.4, 0, 1, 1)` | `ease-out`, `linear` |
| Interactive feedback (button press, spring back) | `--motion-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | `linear`, `ease` |
| Position change that stays on screen | `--motion-ease-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | `linear` |
| Progress bars, spinners | `linear` | `linear` | (this is correct) |

### 2. Timing

| Type | Target range | Violation |
|---|---|---|
| Micro-interaction (hover, press, toggle) | 80–200ms | > 300ms or < 50ms |
| State change (tab switch, accordion) | 200–300ms | > 400ms |
| Entrance (modal, sheet, notification) | 200–350ms | > 500ms |
| Page transition | 300–500ms | > 600ms or < 200ms |
| Loading spinner | 700–900ms per rotation | > 1200ms or < 500ms |

### 3. Properties — GPU-safe only

Always animate `transform` and `opacity`. Never animate layout properties in keyframes:

- ❌ `width`, `height`, `top`, `left`, `right`, `bottom`, `margin`, `padding`
- ✅ `transform: translateX/Y/Z`, `transform: scale()`, `transform: rotate()`, `opacity`

Exception: `color` and `background-color` transitions ≤ 200ms on hover are acceptable.

### 4. prefers-reduced-motion (non-negotiable)

Every `@keyframes`, `animation`, and `transition` declaration must have a corresponding `@media (prefers-reduced-motion: reduce)` override.

```css
/* Minimum override */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
```

Better: provide meaningful reduced-motion alternatives (instant state changes, static images, no entrance animations).

### 5. Stagger

When multiple elements animate in simultaneously, add a stagger delay: 60–100ms between each item. No stagger = visual noise. > 150ms = animation feels broken.

```css
/* Stagger pattern */
.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 80ms; }
.list-item:nth-child(3) { animation-delay: 160ms; }
/* Or with CSS custom property */
.list-item { animation-delay: calc(var(--index) * var(--motion-stagger, 80ms)); }
```

### 6. Motion personality consistency

Within a single product, all animations should share the same personality. Flag:
- Mix of spring and stiff linear animations in the same component
- Duration ranges that vary wildly (50ms and 800ms in adjacent elements)
- Entrance animations using ease-out but exit animations also using ease-out

## Copy-paste spring templates

```css
/* Button press (spring feedback) */
.btn { transition: transform var(--motion-instant, 80ms) cubic-bezier(0.4, 0, 1, 1); }
.btn:active { transform: scale(0.96); }

/* Spring back on release — apply via JS or :not(:active) */
.btn:not(:active) { transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1); }

/* Modal entrance */
.modal {
  opacity: 0;
  transform: scale(0.95) translateY(8px);
  transition: opacity 250ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 250ms cubic-bezier(0.16, 1, 0.3, 1);
}
.modal.open { opacity: 1; transform: scale(1) translateY(0); }

/* Modal exit */
.modal.closing {
  transition: opacity 150ms cubic-bezier(0.4, 0, 1, 1),
              transform 150ms cubic-bezier(0.4, 0, 1, 1);
  opacity: 0;
  transform: scale(0.97) translateY(4px);
}
```

## Output format

```
**Motion audit — [N] violations found**

⚡ Quick Wins (copy-paste these first):
  1. [Highest-impact fix — 1 line description]
  ```css
  /* Before */
  [current code]
  /* After */
  [fixed code]
  ```

Critical (WCAG failures or layout thrash):
  [file]:[line] — [issue]
  ```css
  /* Before */
  [current code]
  /* After */
  [fixed code — use exact cubic-bezier values]
  ```

Major (wrong easing, wrong timing, no stagger):
  [file]:[line] — [issue]
  ```css
  /* Before → After */
  [fix with exact values]
  ```

Minor (style/personality inconsistencies):
  [file]:[line] — [issue] → [1-line fix]

Motion personality summary:
  Detected: [snappy / calm / playful / professional / inconsistent]
  Recommended tuning: [specific suggestion]
```

## Links

- radii.cloud/terms/animation
- radii.cloud/terms/spring-animation
- radii.cloud/terms/motion-design
- radii.cloud/terms/micro-interactions
- radii.cloud/terms/stagger-effect
- radii.cloud/terms/transition
