Best for
- Use when asked to animate something, add motion, make a component feel alive, or build a transition.
emilkowalski/skills/skills/animate/SKILL.md
Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at all, what purpose, which tool, which properties, which curve and duration, how it interrupts, how it exits. Writes the implementation. Use when asked to animate something, add motion, make a component feel alive, or build a transition. For critiquing existing motion use review-animations; for auditing a whole codebase use improve-animations.
Decision brief
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), or hunt for places that could animate (that's find-…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/emilkowalski/skills --skill "skills/animate"Inspect the Agent Skill "animate" from https://github.com/emilkowalski/skills/blob/de33dbed000212b54400a33767d1e4d03654db2a/skills/animate/SKILL.md at commit de33dbed000212b54400a33767d1e4d03654db2a. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
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.
1. 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. 2. 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…
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.
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.
Name it in one of these words before continuing:
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 25,890 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
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), or hunt for places that could animate (that's find-animation-opportunities).
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:
ease-in on 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.
cubic-bezier(0.4, 0, 0.2, 1) because it looks familiar.--ease-out or a duration scale already exists, use it. Adding a parallel system is a defect.| 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.
Name it in one of these words before continuing:
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.
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.
transform and opacity only. They skip layout and paint and run on the GPU. width/height/margin/padding/top/left trigger all three. (clip-path is the sanctioned fourth — see RECIPES.md. height is tolerated only for accordions, where there's no transform equivalent.)scale(0). Start from scale(0.9–0.97) + opacity: 0. Nothing in the real world appears from nothing.transform-origin at 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.translate() are relative to the element's own size — translateY(100%) moves by its own height whatever the content. Prefer over hardcoded pixels.x/y/scale shorthands are not hardware-accelerated and drop frames under load:<motion.div animate={{ x: 100 }} /> // drops frames under load
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware accelerated
transform on the element directly.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 control
Keep bounce at 0.1–0.3, and avoid bounce in most UI — reserve it for drag-to-dismiss and playful interactions.
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.
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.
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 | 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 |
Write the code. Then, in at most a few lines:
Don't pad this into a report. The code is the deliverable.
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.