skills/ui-animation/SKILL.md
Designs, implements, reviews, debugs, and reverse-engineers UI motion: CSS transitions, keyframes, springs, gestures, drag, easing, timing, framer-motion, and animation curves from screen recordings. Use when asked to "add animations", "make this feel smooth", "review my animations", "add a swipe gesture", "match this easing", "reverse engineer this animation", "extract the animation curve", or "what's it called when..." to name a motion effect from a vague description. For visual direction use ui-design; for page-level UI audit use ui-audit.
npx skillsauth add mblode/agent-skills ui-animationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
ui-design), auditing a whole page's UI quality (use ui-audit), or named text-effect specs (use the external animate-text skill where installed).Canonical home for reverse-engineering motion from a recording: route "reverse engineer this animation" and "match this easing" here, not to a separate skill. If the input is a screen recording or video, you are MEASURING motion: follow the Reverse-engineer workflow. Otherwise (designing, implementing, reviewing) use the rules and Workflow below.
| File | Read when |
| --- | --- |
| references/decision-framework.md | Default: deciding whether/why to animate, picking easing character |
| references/spring-animations.md | Spring physics, framer-motion useSpring, configuring spring params, Apple damping/response values, interruption mechanics |
| references/component-patterns.md | Buttons, popovers, tooltips, drawers, modals, toasts with animation |
| references/clip-path-techniques.md | clip-path for reveals, tabs, hold-to-delete, comparison sliders |
| references/gesture-drag.md | Drag, swipe-to-dismiss, momentum, pointer capture, velocity handoff, momentum projection |
| references/performance-deep-dive.md | Jank, CSS vs JS, WAAPI, CSS variables trap, Framer Motion caveats |
| references/review-format.md | Reviewing animation code: ten standards (each with flag-on-sight triggers), Before/After/Why table, Block/Approve verdict |
| references/contextual-animations.md | Contextual icon swaps, word-level stagger entrances, fixed-offset exits |
| references/transition-recipes.md | Installing a CSS transition: card resize, badge, dropdown, modal, panel, page slide, icon swap, number pop-in, text swap, success, avatar hover, error shake |
| references/measurement-guide.md | Reverse-engineer: what to measure, eye vs script, reading metrics.json, choosing an ROI |
| references/curve-fitting.md | Reverse-engineer: reading fit_curves.py output, spring vs bezier, judging fit error, asymmetric open/close |
| references/code-output.md | Reverse-engineer: emitting code for CSS, Motion/Framer Motion, SwiftUI, React Native, UIKit |
| references/choreography.md | Reverse-engineer: multi-element/multi-phase motion: staggers, blur-before-move, per-edge settling |
| references/vocabulary.md | Naming a motion effect the user describes vaguely ("what's it called when...") |
requestAnimationFrame); under load CSS stays smooth while JS drops frames.@starting-style for DOM entry; fall back to a data-mounted attribute where unsupported.filter: blur(2px) hides rough crossfades between swapped content.transform and opacity only; they skip layout and paint.color, background-color, and opacity are acceptable.width, height, top, left); they trigger layout recalc every frame. (Exception: a deliberate container resize tween, see the card-resize recipe.)transition: all; it animates unintended properties and silently adopts future ones. List them explicitly.filter animation for core interactions; if unavoidable keep blur ≤ 20px (heavy blur is expensive, especially in Safari).<g> wrapper with transform-box: fill-box; transform-origin: center; without it they rotate/scale around the canvas origin.transform: scale() also scales children (icons, text, borders scale proportionally), unlike width/height: a feature for press feedback, but account for it when an inner element must stay fixed-size.[data-theme-switching] * { transition: none !important }), or every themed property animates at once.| Element | Duration | Easing |
| ----------------------------- | ------------ | -------------------------------- |
| Button press feedback | 100-160ms | cubic-bezier(0.22, 1, 0.36, 1) |
| Tooltips, small popovers | 125-200ms | ease-out or enter curve |
| Dropdowns, selects | 150-250ms | cubic-bezier(0.22, 1, 0.36, 1) |
| Modals, drawers | 200-350ms | cubic-bezier(0.22, 1, 0.36, 1) |
| Move/slide on screen | 200-300ms | cubic-bezier(0.25, 1, 0.5, 1) |
| Page transitions | 250-400ms | enter or move curve |
| Hover (colour/opacity) | 200ms | ease |
| Hover (transform/scale) | 100-150ms | enter curve |
| Illustrative/marketing | Up to 1000ms | Spring or custom |
Keep routine UI under 300ms; scale duration with distance (a full-screen slide can exceed 300ms, a 6px tooltip shift stays under 150ms).
Named curves
cubic-bezier(0.22, 1, 0.36, 1) for entrances and transform-based hovercubic-bezier(0.25, 1, 0.5, 1) for slides, drawers, panelscubic-bezier(0.32, 0.72, 0, 1)Avoid ease-in for UI: it starts slow, so the element lags the user's action and feels sluggish. Prefer custom curves from easing.dev over built-in ease/ease-out, whose gentle acceleration reads soft, not decisive.
Match the UI element first, then pick the recipe from references/transition-recipes.md:
| UI pattern | Recipe | |---|---| | Trigger + floating dot/count | Notification badge | | Trigger + anchored surface | Menu dropdown | | Centred surface on top of page | Modal dialog | | Panel sliding into existing container | Panel reveal | | List ↔ detail or wizard steps | Page side-by-side slides | | Element dimension changes | Card resize | | Text updating in place | Text state swap | | Two icons in same slot | Icon swap | | Number updating | Number pop-in | | Confirmation / success moment | Success celebration | | Hovering item in horizontal stack | Avatar group hover | | Form validation error | Error state shake |
Prefer lower-overhead transitions (CSS-only) unless the design requires JS orchestration.
transform-origin at the trigger (modals stay center), dialog/menu entrances from scale(0.85-0.9) not scale(0), and 30-50ms staggers (total under 300ms, most important element leading). Full rules and code in references/component-patterns.md and references/contextual-animations.md.prefers-reduced-motion: reduce path: disable transform/keyframe motion, keep instant state changes or opacity-only fades. All recipes include the guard.@media (hover: hover) and (pointer: fine), or touch devices replay hover on tap. Tailwind v4 hover: utilities apply this automatically; skip the manual query there.IntersectionObserver; they burn GPU even when invisible.will-change only during heavy motion and only for transform/opacity; remove it after. Each promotion costs compositor memory; permanent promotion across many elements is worse than none.transform directly on the moving element.x/y values are the default for axis movement and drag (they bypass React re-renders). Use a full transform string only when one owner must combine multiple transform functions or interop with non-Motion code.High-signal failures not covered above:
Copy and track:
Animation progress:
- [ ] Step 1: Decide whether the interaction should animate
- [ ] Step 2: Choose purpose, easing, and duration
- [ ] Step 3: Pick the implementation style
- [ ] Step 4: Load the relevant component or technique reference
- [ ] Step 5: Validate timing, interruption, and device behavior
Produce evidence for each check (DevTools observations, not "looks fine"):
width, height, top, left) and transition: all.transform-origin issues invisible at full speed.prefers-reduced-motion: reduce (DevTools Rendering panel) and confirm every animation has a reduced path.will-change is toggled around animations, not permanently set, and looping animations pause off-screen.Use this branch to measure an existing animation from a screen recording, then emit code and a handoff spec that reproduce it. The scripts under scripts/ are the canonical, deterministic path; run them rather than reconstructing their logic.
Dependencies: ffmpeg for frame extraction (brew install ffmpeg); Python with pip install opencv-python numpy scipy for tracking and curve fitting. Degrades gracefully: with only ffmpeg you can extract frames and reason visually; tracking and fitting need the Python packages.
Reverse-engineer progress:
- [ ] Step 1: Extract frames + contact sheet (per direction if open differs from close)
- [ ] Step 2: Vision pass: identify element, effects, phases
- [ ] Step 3: Decide precision (eye-only vs scripted)
- [ ] Step 4: Track motion and fit curves (if escalating)
- [ ] Step 5: Annotate choreography (delays, asymmetry)
- [ ] Step 6: Emit code for the target(s)
- [ ] Step 7: Validate against the recording
python3 scripts/extract_frames.py <video> <outdir>. Trim to just the transition with --start/--duration; if the interaction has both an open and a close, trim two windows and run the pipeline once per direction (they are almost never mirror images). Match --fps to the source (probe with ffprobe), never sampling above the source rate. Open contact_sheet.png first.references/measurement-guide.md.python3 scripts/track_motion.py <outdir> for metrics.json (pass --bbox X,Y,W,H to isolate one element), then python3 scripts/fit_curves.py <outdir>/metrics.json for spring params, cubic-bezier, and per-property fit error. Pass the same --fps you extracted with. Read references/curve-fitting.md to pick the model; high error on both means multi-phase motion (split and fit each segment).references/choreography.md. Build the timing-offset table (when each property starts and settles); lead/lag gaps and over-stretch carry more feel than any single curve.references/code-output.md for the target. Keep movement on transform/opacity. Emit two transitions when open and close differ, plus the consolidated handoff spec so it can be implemented without the video.extract_frames.py, and compare contact sheets side by side. Slow to 0.1x to confirm phase order and over-stretch survive. Confirm the code only animates transform, opacity, and filter.Reverse-engineer gotchas:
fit_curves.py defaults to --fps 30: extract at 60 but fit at the default and every duration_ms doubles while fitted stiffness drops to a quarter. Always pass the extraction fps to the fit.metrics.json. Probe and match the source rate.references/choreography.md). Treat a fit error above 0.08 as suspect.ui-design: visual direction, palettes, typography; settle the visual system before tuning motion.ui-audit: page/feature-level UI quality audit; its motion findings route back here for fixes.animate-text skill where installed: curated named text effects (typewriter, line reveal, stagger builds) with exact JSON specs.development
Fans out four concurrent review agents over the current diff, then APPLIES fixes directly to the working tree and verifies the build. Mutates code; it does not produce a report. Covers reuse (duplicate logic, hand-rolled stdlib, reinvented platform features), quality (hacky patterns, React/TypeScript hygiene, over-memoisation, exhaustive-deps, `any`, dead code, `CLAUDE.md`/`AGENTS.md` violations), efficiency (unnecessary work, missed concurrency, hot-path bloat), and test discipline (bug fixes without a repro test, useless tests to delete, missing tests only when they prevent a named failure). Use when the user says "tidy this up", "simplify", "clean up this diff", "polish my changes", "check for duplication", or "any reuse opportunities?", i.e. when the intent is to have the changes made automatically. For a read-only report that lists findings without touching files, use `pr-reviewer` instead. This skill edits code; for the PR's title, description, or commit history, use `pr-creator`.
development
Decides what an interface should do before UI is built or audited: interaction choice, action scope and consequence, reachable states, resilience, and accessibility as task completion. Works from a brief, spec, mockup, intent, or existing UI. Use when asked "is this the right interaction", "design the flow", "what control should this use", "what should this action affect", "which states should this have", "make this resilient", or "what breaks here". For building or styling use ui-design; for built-code audits use ui-audit; for copy wording use copywriting.
development
Builds and stress-tests implementation plans in two modes. Create mode scans code and docs, asks one question at a time with a recommended answer, runs a blindspot pass when the user is new to the area, then writes a plan file. Review mode scores completeness, feasibility, scope, testability, risk, and assumptions, verifies checkable claims, and writes resolutions back until every dimension reaches 5/5. Use when asked to "create a plan", "plan this feature", "I want to build X", "grill me", "think this through", "blindspot pass", "unknown unknowns", "this is new to me", "review my plan", "rubber duck this", "stress test this plan", "is this plan ready", "get this plan to 5/5", "what am I missing", "verify this claim", "prove this plan", "fact-check this plan", or when the user explicitly wants a plan artifact before implementation. For code review use pr-reviewer; for architecture briefs use define-architecture.
tools
Audits the smallest relevant developer-facing surface of a library, CLI, SDK, or npm package across API contracts, errors, CLI behavior, public types, onboarding, and config. Uses candidate-first rule loading, bounded local evidence, and compact root-cause findings. Use when asked to "audit my CLI", "make this CLI agent-friendly", "is this API ergonomic", "review the developer experience", "improve these errors", "simplify first run", or "review my SDK". For end-user UI use ui-audit, for agentic-app trust use ax-audit, for docs prose use docs-writing, for README work use readme-creator, and for repo architecture use define-architecture. Inside a product that also ships a UI, this is the skill for the developer-facing half, so pick it when the complaint is about an import, command, error string, exported type, or config rather than a screen.