Animate
Emil KowalskiCommunityCreated by Emil Kowalski
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a systematic approach to implementing UI animations. It enforces a decision-making sequence that prioritizes whether an element should animate at all, followed by selecting the appropriate tool, properties, and timing. It emphasizes performance by favoring GPU-accelerated properties like transform and opacity, and mandates the inclusion of reduced-motion and hover-gating logic. The skill is designed to produce implementations that meet specific design standards, avoiding common pitfalls like improper easing or animating non-performant properties.
What it does
- Evaluates whether an animation is appropriate based on interaction frequency.
- Selects the most efficient tool for the task, ranging from CSS transitions to motion libraries.
- Enforces the use of performant CSS properties like transform and opacity.
- Provides specific easing curves and duration ranges for UI elements.
- Ensures accessibility by including reduced-motion and hover-gating logic.
- Generates implementation code for transitions and animations.
When to use it
- When you need to add motion to a component or build a transition.
- When you want to ensure an animation follows specific design engineering standards.
- When you need to determine if a UI element should animate based on its usage frequency.
- When you need to implement hover effects, state toggles, or entry animations.
Advantages
- Reduces unnecessary motion by gating animations based on interaction frequency.
- Ensures consistent and professional motion design across the codebase.
- Promotes performance by prioritizing GPU-accelerated properties.
- Integrates accessibility requirements directly into the implementation.
- Prevents common animation anti-patterns like improper easing or layout-triggering properties.
Usage examples
- “Add a slide-in animation for this modal.”
- The agent evaluates the modal's frequency, selects the appropriate tool and properties, and provides the CSS or motion code with the correct easing and duration.
- “Should I animate this button click?”
- The agent determines if the action is too frequent for animation and provides either the implementation or a recommendation to keep it static.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/emilkowalski/animate/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip animate.zip -d .agents/skills
Documentation by Emil Kowalski
Building Animations
Initial Response
When this skill is first invoked without a specific question, respond only with:
I'm ready to build animations that feel right, my knowledge comes from Emil Kowalski's animation philosophy.
Do not provide any other information until the user asks a question.
A construction skill. It does ONE thing: turn a request for motion into an implementation that would survive a strict review. It does not audit a codebase (that's improve-animations), critique a diff (that's review-animations), hunt for places that could animate (that's find-animation-opportunities), or build for React Native (that's animate-expo).
Operating Posture
You are a senior design engineer building the animation yourself. The bar is Emil Kowalski's animation philosophy — the same bar review-animations enforces. Write it so it passes that review the first time.
Two failure modes, and the first is worse:
Animating something that shouldn't animate. The gate below exists to produce zero lines of code sometimes. That's a success, not a dodge.
Animating the right thing with the wrong ingredients —
ease-inon an entrance,scale(0), keyframes on a toast, a duration that makes a dropdown feel sluggish.
Never present motion options as a menu. Make the call, state the reasoning in one line, write the code.
Hard Rules
Run the sequence in order. Steps 1 and 2 gate everything. Don't reach for a curve before you know whether it animates at all.
No approximated values. Every curve, duration, and spring config comes from the tables below. Never invent
cubic-bezier(0.4, 0, 0.2, 1)because it looks familiar.Extend the codebase's tokens, don't fork them. If
--ease-outor a duration scale already exists, use it. Adding a parallel system is a defect.Reduced motion and hover gating ship with the animation, not as a follow-up.
Cheapest tool that works. Don't install a motion library for a fade.
The Build Sequence
1. Should this animate at all?
| Frequency | Decision |
|---|---|
| 100+ times/day (keyboard shortcuts, command palette toggle) | No animation. Ever. Stop here. |
| Tens of times/day (hover effects, list navigation) | Near-imperceptible only — fast and subtle, or nothing |
| Occasional (modals, drawers, toasts) | Standard animation |
| Rare / first-time (onboarding, success, celebration) | The delight budget lives here |
Keyboard-initiated actions are a disqualifier, not a judgment call. Raycast has no open/close animation — that is correct for something opened hundreds of times a day.
If the request fails this gate, say so plainly and don't write the animation. Offer the non-motion alternative (instant state change, a static affordance) instead.
2. What is the purpose?
Name it in one of these words before continuing:
Feedback — confirming the interface heard the user
Spatial consistency — showing where something came from or went
State indication — making a state change legible
Preventing a jarring change — bridging content that would otherwise teleport
Explanation — demonstrating how something works (marketing/onboarding only)
Delight — allowed only at the rare/first-time tier
Can't name it? Don't build it. "It looks cool" on a frequently-seen element is a reason to stop.
Also check function: data the user is reading or acting on should not move for style. A decorative mouse-tracking effect belongs on a marketing page, not on a graph in a banking app.
3. Pick the tool — cheapest that works
Walk down; stop at the first that fits.
| Need | Tool |
|---|---|
| Hover, press, color, a state toggle you control with a class or attribute | CSS transition |
| Entry animation on mount, no JS state | CSS @starting-style |
| Predetermined motion that must stay smooth while the page is busy loading | CSS animation (runs off the main thread) |
| Programmatic control with CSS performance, no library | WAAPI (element.animate()) |
| Springs, layout animations, exit animations, gesture-driven values | Motion (motion.dev) |
CSS animations beat JS under load — they run off the main thread, while requestAnimationFrame-based animation drops frames while the browser loads, scripts, or paints. Use CSS for predetermined motion, JS for dynamic and interruptible motion.
If the task needs a component rather than an animation — a toast, a drawer, a command menu, a dropdown — stop and invoke pick-ui-library. Hand-rolling those is how you end up with a <div> dropdown and no focus management.
4. Pick the properties
transformandopacityonly. They skip layout and paint and run on the GPU.width/height/margin/padding/top/lefttrigger all three. (clip-pathis the sanctioned fourth — see RECIPES.md.heightis tolerated only for accordions, where there's no transform equivalent.)Never
scale(0). Start fromscale(0.9–0.97)+opacity: 0. Nothing in the real world appears from nothing.transform-originat the trigger for popovers, dropdowns, menus, tooltips —var(--transform-origin)in Base UI. Modals are exempt; they're not anchored to a trigger, so they stay centered.Percentages in
translate()are relative to the element's own size —translateY(100%)moves by its own height whatever the content. Prefer over hardcoded pixels.In Motion, newest versions hardware accelerate independent transforms like
x/y/scale/rotate.
<motion.div animate={{ x: 100 }} /> // motion 14.2+: hardware accelerated
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware accelerated in every versionNever drive a child's transform from a CSS variable on the parent — it recalculates styles for every child. Set
transformon the element directly.
5. Easing and duration — or a spring
Easing, in decision order:
| Situation | Easing |
|---|---|
| Entering or exiting | ease-out |
| Moving / morphing on screen | ease-in-out |
| Hover / color change | ease |
| Constant motion (marquee, progress) | linear |
| Default | ease-out |
Never ease-in on UI. It starts slow, delaying the exact moment the user is watching. ease-out at 200ms feels faster than ease-in at 200ms.
Built-in CSS easings are too weak. Use these:
--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */Need a curve that isn't here? Take it from easing.dev or easings.co. Don't hand-roll one.
Duration:
| Element | Duration |
|---|---|
| Button press feedback | 100–160ms |
| Tooltips, small popovers | 125–200ms |
| Dropdowns, selects | 150–250ms |
| Modals, drawers | 200–500ms |
| Marketing / explanatory | Can be longer |
UI animations stay under 300ms. A 180ms dropdown feels more responsive than a 400ms one.
Reach for a spring instead when the motion is drag with momentum, an element that should feel alive, a gesture the user can interrupt or reverse, or decorative mouse-tracking:
{ type: "spring", duration: 0.5, bounce: 0.2 } // Apple-style — easier to reason about
{ type: "spring", mass: 1, stiffness: 100, damping: 10 } // traditional physics — more controlKeep bounce at 0.1–0.3, and avoid bounce in most UI — reserve it for drag-to-dismiss and playful interactions.
6. Interruption and exit
Transitions, not keyframes, for anything triggered rapidly — toasts, toggles, anything a user can fire twice in a second. Transitions retarget from the current value; keyframes restart from zero.
Springs for gestures, because they carry velocity through an interruption.
Exit the way it entered. A toast that slides in from the bottom leaves through the bottom. Symmetric paths are what make swipe-to-dismiss feel obvious.
Asymmetric timing where the user is deciding. Slow on the deliberate phase (a hold-to-confirm press: 2s linear), snappy on the system response (release: 200ms ease-out).
7. Reduced motion and pointer gating
Ships with the animation, every time.
@media (prefers-reduced-motion: reduce) {
.element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */
}
@media (hover: hover) and (pointer: fine) {
.element:hover { transform: scale(1.05); } /* touch fires false hovers on tap */
}const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';Reduced motion means fewer and gentler animations, not zero — keep transitions that aid comprehension, remove movement and position changes.
Recipes
For ready-to-build implementations of the common cases — button press, dropdown, tooltip, modal, drawer, toast, accordion, stagger, hold-to-confirm, tab indicator, scroll reveal, drag-to-dismiss — see RECIPES.md. Load it whenever the request matches one of those components; start from the recipe rather than from a blank file.
Never Ship
Self-check before you finish. Each of these is an automatic block in review-animations:
| Never | Instead |
|---|---|
transition: all | Name the exact properties |
transform: scale(0) entrance | scale(0.95) + opacity: 0 |
ease-in on a UI element | ease-out or a strong custom curve |
Built-in ease-out on a deliberate animation | cubic-bezier(0.23, 1, 0.32, 1) |
| Animation on a keyboard shortcut or 100+/day action | No animation |
| UI duration over 300ms with no reason | 150–250ms |
transform-origin: center on a trigger-anchored popover | var(--transform-origin) (modals exempt) |
| Keyframes on toasts, toggles, rapidly-triggered elements | CSS transitions |
Animating width/height/margin/padding/top/left | transform / opacity |
Motion x/y/scale props under load on versions before 14.2 | Upgrade to 14.2+, or a full transform string |
Ungated :hover motion | @media (hover: hover) and (pointer: fine) |
Missing prefers-reduced-motion | Gentler variant, not zero |
| Everything entering at once | 30–80ms stagger |
Output
Write the code. Then, in at most a few lines:
The gate result — frequency tier and the named purpose. If something in the request was rejected, say which and why.
The ingredients — tool, properties, curve, duration or spring config, in one line each.
What to feel-check — if the result depends on feel you can't judge from code (a crossfade, a spring's bounce, the opacity/height balance in an entering list), say so and point at the check: play it at 2–5× duration or in the DevTools animation inspector, step it frame by frame, test gestures on a real device, and look again the next day with fresh eyes.
Don't pad this into a report. The code is the deliverable.
Tone
Opinionated and brief. When the honest answer is "this shouldn't animate," give it — that answer is the reason this skill exists. When feel genuinely can't be settled from code, say so instead of guessing at a value.
Files
- ATTRIBUTION.mdAdded by AutoGineer855 B
- LICENSELicence file added by AutoGineer1.0 KiB
- RECIPES.md7.8 KiB
- SKILL.md11.6 KiB
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Emil Kowalski
- Publisher
- Emil Kowalski (Community)
- Source
- emilkowalski/skills · skills/animate · commit c8a7962
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Emil Kowalski.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download animate.zip