# Motion Review

> Use when reviewing a diff that contains animation code and a Block or Approve verdict with a Before/After/Why table is required.

- Skill: `agentsorg/motion-review` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add agentsorg/motion-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agentsorg/motion-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: agentsorg (https://skillmd.com/u/agentsorg)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/agentsorg/motion-review

---



# Reviewing Motion in a Diff

Default to flagging. Approval is earned, not assumed: motion that runs is not motion that passes, and a transition that merely works while landing from the wrong origin, firing on a keyboard shortcut, or holding the user for 450ms is a regression this diff introduced. Read every motion hunk before judging any line of it, measure each animation against a fixed bar rather than against your mood, and close with one word — **Block** or **Approve**. A review that ends in a paragraph of impressions has not been done.

This skill reviews the motion in a diff and nothing else. `ui-review` owns non-motion UI code; on a mixed diff `ui-review` runs first and hands its motion hunks here, so never re-report its spacing, type, or contrast findings. Two further boundaries: a whole-codebase sweep that ends in a prioritized fix plan is `motion-audit`, not this skill, and a rendered screen or screen recording rather than source text is `design-critique`.

**Read the project's own motion vocabulary before writing the first finding.** Grep for `--ease-`, `--duration-`, the Tailwind theme block, and whichever of `motion`/`framer-motion`, React Spring, GSAP, or plain CSS the package manifest actually carries. Review the diff against that system: if the repo defines `--ease-drawer`, a hand-typed `cubic-bezier(0.32, 0.72, 0, 1)` in the diff is a finding even though the number is correct, and every fix you propose must be written in the repo's existing tokens. A review that bolts on a parallel easing vocabulary has added the exact debt it was called to prevent.

## Quick Reference

| Topic | Where |
| --- | --- |
| Exact curves, durations, spring configs, velocity and stagger numbers | [standards.md](references/standards.md) — open it the moment a finding needs a number, before you type one from memory |
| Documented exemptions and known false positives | [standards.md](references/standards.md) — open it before reporting anything that looks like a violation but sits on a modal, a marketing page, or a reduced-motion block |

## Core Principles

1. **Ask the frequency question before the easing question.** A perfectly eased 200ms animation on a command-palette toggle is still a Block, and re-easing it is wasted work: the fix is deletion. The gate is `100+/day → no animation, ever`, and any keyboard-initiated action is disqualified regardless of count. Exception: marketing and explanatory surfaces, where a given visitor sees the motion roughly once and the frequency gate does not bind.

2. **Flag the five prohibitions on sight and do not deliberate.** `transition: all`, entering from `scale(0)`, `ease-in` on product UI, `transform-origin: center` on a trigger-anchored popover, and any animation on a keyboard-initiated action are lint-checkable, so deliberating over them only makes the review slower. Exception: modals legitimately keep `transform-origin: center` because they are anchored to the viewport rather than to a trigger — reporting that costs you the reader's trust for every later row.

3. **Cite `file:line` and the exact replacement value, never an adjective.** "Too slow" cannot be executed, and the author will guess wrong; a reviewable finding reads `Drawer.tsx:41 — 500ms ease-in-out → 250ms var(--ease-drawer)`. Pull the number from [standards.md](references/standards.md) rather than approximating. Exception: when the repo already owns a token for that role, cite the token name and not the literal behind it.

4. **Treat any product-UI duration over `300ms` as a finding unless the diff says why.** Perceived responsiveness is dominated by the first 100ms of movement, so a 180ms dropdown reads as faster than a 400ms one that covers the same distance. Exception: modals and drawers may run to `500ms`, and marketing or explanatory motion is out of budget by design.

5. **Prefer deletion to correction, in that order.** The remedial hierarchy is delete → reduce → re-ease → fix the origin → make it interruptible → move it to the GPU → make the timing asymmetric → polish → accessibility and cohesion, and an animation that is removed can never regress again. Exception: motion carrying spatial information — where a panel came from, which edge a toast leaves by — where deletion trades one regression for another.

6. **Judge interruptibility by how often the element is retriggered, not by which API it uses.** `@keyframes` is not itself a defect; `@keyframes` on a toast stack, a toggle, or a drag is, because keyframes restart from zero while transitions and springs retarget from the current value. Exception: a one-shot entrance or a loader that always runs to completion, where restarting is unreachable.

7. **Do not flag a curve for being unfamiliar — flag a diff for carrying two curve families.** Two published families exist and they genuinely disagree: one overshoots past `1` (`cubic-bezier(0.175, 0.885, 0.32, 1.1)`), one settles into it (`cubic-bezier(0.23, 1, 0.32, 1)`). Either is defensible; mixing them inside one product is the finding. [standards.md](references/standards.md) carries both families and their attribution. Exception: a deliberately playful surface inside an otherwise crisp app, when the diff or a design doc states the intent — note it, do not report it.

8. **Do not re-litigate a decision the code already documents.** A comment or design note explaining why a drawer runs long is evidence the tradeoff was made deliberately, and reopening it burns the review's credibility. Exception: the accessibility floor — a missing `prefers-reduced-motion` path or ungated `:hover` motion is never settled by a comment.

9. **"I cannot tell whether it feels right" is not a finding.** Guessing at feel from source text produces noise that buries the rows that are real. Name the exact interaction to watch and hand it to `design-critique`; if the diff also needs it, say so in the verdict. Exception: when the source makes feel deterministic — a 2s `ease-in` on a dropdown needs no screen to judge.

## Smell / Fix

| Smell | Fix |
| --- | --- |
| `transition: all 300ms` | Name the properties: `transition: transform 200ms var(--ease-out)` |
| `transform: scale(0)` on enter | `transform: scale(0.95); opacity: 0` |
| `ease-in` on a dropdown, sheet, or menu | `ease-out` or the repo's strong custom curve |
| `transform-origin: center` on a popover, dropdown, or tooltip | `var(--transform-origin)`; leave modals alone |
| Animation on a shortcut, palette toggle, or arrow-key move | Delete the animation |
| `@keyframes` on a toast, toggle, or drag | A transition or spring so it retargets mid-flight |
| Animated `width`, `height`, `margin`, `top`, or `left` | `transform` and `opacity` only |
| Framer Motion `x` / `y` / `scale` shorthands on a busy page | The full `transform` string |
| Enter and exit share one duration on a press-and-hold | Slow the deliberate phase, snap the release |
| A fourth hand-typed cubic-bezier in a repo that has tokens | The existing token |
| No `prefers-reduced-motion` path on anything that moves | Gentler, not zero — keep opacity, drop movement |
| A verdict of "looks good overall" | `Block` or `Approve`, one word |

## Output Format

Two parts, in this order. The table form is mandatory — Emil Kowalski's review format explicitly forbids the `Before:` / `After:` list, because a list hides how many issues there are and cannot be scanned.

**Part 1 — Findings table.** One markdown table, one row per issue, columns `| Before | After | Why |`. `Before` carries the code as written plus `file:line`; `After` carries the exact replacement including the token name; `Why` is one clause on what it costs the user. No prose between rows.

**Part 2 — Verdict.** Group any remaining commentary by impact, highest first, omitting empty tiers: feel-breaking regressions · motion that should be deleted · performance · interruptibility and timing · origin, physicality, and cohesion · accessibility. Then state the decision on its own line.

- **Block** — any feel-breaking regression, any animation on a keyboard-initiated or 100+/day action, any of the five prohibitions, or a non-GPU animation with a one-line GPU fix.
- **Approve** — no feel-breaking regressions, nothing that should be deleted, durations and curves inside budget and inside one family, interruptibility handled where the element is retriggered, and a reduced-motion path present.

An empty findings table with a fast **Approve** is a real outcome. Manufacturing rows to look thorough costs more than it buys.

## Checklist

- [ ] Read every motion hunk in the diff before writing a row
- [ ] Checked the repo's existing easing and duration tokens and written all fixes in them
- [ ] Frequency question answered for each animation before any easing judgment
- [ ] The five prohibitions scanned, with modals exempted from the origin rule
- [ ] Every row cites `file:line` and an exact value pulled from `standards.md`
- [ ] Durations checked against the per-element budget and the `300ms` product ceiling
- [ ] Interruptibility judged by retrigger rate, not by API
- [ ] Curve family checked for mixing, not for unfamiliarity
- [ ] Reduced-motion and hover gating checked on everything that moves
- [ ] Anything unjudgeable from source handed to `design-critique` by name, not guessed
- [ ] Output is one `| Before | After | Why |` table plus a one-word verdict

