ui-motion
Skill0 starsChanged 16 days agoMITView on GitHub
Use when an animation, transition or motion effect is added to or fixed in a web UI (CSS, WAAPI, View Transitions, a React motion library); when the user says an animation is janky, choppy, stutters or drops frames; when asked to make a page or component \"feel alive\", smoother or more polished; when reviewing or auditing existing motion, or looking for places that should or should not animate; when the user describes an effect without knowing its name. Two modes: BUILD (writes the motion, cheapest tool first, with concrete easings and durations, reduced-motion and performance handled) and REVIEW (read-only audit with file:line findings, exact fix values and a verdict). Plain HTML/CSS/JS first; React only as an aside.
9 other projects use a version of this.
Adopted by
Most recently taken up by
- whoamnesia/astudio-motiontheir own version, first seen Nov 8, 2026
- linyi-ui/ui-motion-skilltheir own version, first seen Nov 8, 2026
- qxryz/workflowgeneratortheir own version, first seen Oct 25, 2026
- thewyattbrocato/design-and-copy-skillstheir own version, first seen Oct 17, 2026
- adnanrahim110/ui-motion-skilltheir own version, first seen Oct 15, 2026
- designhouseme/AdvancedSkillstheir own version, first seen Oct 13, 2026
Other versions9 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 9
- qxryz/workflowgenerator★ 48same name
- linyi-ui/ui-motion-skill★ 42same name
- thakur698/universal-ui-skills★ 5same name
- Adhamxon/claude-code-skills★ 2same name
- dafaalz/agent-skills★ 2same name
- thewyattbrocato/design-and-copy-skills★ 2same name
- designhouseme/AdvancedSkills★ 1same name
- whoamnesia/astudio-motion★ 1same name
- adnanrahim110/ui-motion-skill★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- apps-configSkill
- architect-flowSkill
- bootstrap-aiSkill
- code-healthSkill
- csharp-toolsSkill
- headless-ui-smokeSkill
- launch-and-deploySkill
- macos-local-codesignSkill
- mobile-webSkill
- portable-projectSkill
- review-sweepSkill
- tech-researchSkill
- test-tiersSkill
- threat-modelingSkill
- ui-craftSkill
- ui-doctorSkill
- ui-stress-testSkill
- ui-variantsSkill
- web-accessibilitySkill
- web-art-directionSkill
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 6 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 8 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
BUILD: add motion, or fix motion that feels wrong, stutters or ignores reduced motion.
- Distinctive: line 34
Write down: the easing and duration tokens in use (reuse them.
- Distinctive: line 51
If the honest answer is "it would just look nice", leave it static and say so.
- Distinctive: line 74
A JS library: only if one is already in the project, or the user agrees to add it (gesture physics with velocity, complex orchestration).
- Very specific: line 80
Write the transitioned properties explicitly — never transition: all, which animates whatever changes next (colour from a theme switch, …
- Distinctive: line 99
Avoid ease-in for UI: it starts slow, exactly when the user is waiting for a response.
- Very specific: line 159
Height to auto: prefer interpolate-size: allow-keywords (progressive.
- Distinctive: line 199
No more than three flashes per second, ever.
- Very specific: line 217
When the main thread is busy (a stream rendering, a big list), CSS and WAAPI animations of transform/opacity keep running on the compositor.
- Very specific: line 233
Run it on the project's own dev server on 127.0.0.1 or open the file via file://.
- Very specific: line 240
Check once with reduced motion on (DevTools › Rendering › Emulate prefers-reduced-motion), once at 4× CPU throttling, and on a real phone …
“Common” is measured: kept by at least 6 in 10 of the 8 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
UI motion
Motion is a tool for explaining change: where a thing came from, that an action was received, what is still loading. Used for decoration it slows people down. Most of the craft is deciding not to animate, and making the few remaining animations short, interruptible and cheap.
Two modes. Pick one from the request and say which:
- BUILD: add motion, or fix motion that feels wrong, stutters or ignores reduced motion. Writes code.
- REVIEW: audit existing motion, or list places that should (and should not) animate. Read-only until the user asks to apply the findings. Checklist and report template: REVIEW.md.
The user describes an effect loosely ("the bouncy popover thing", "the iOS stretch")? Name it first with VOCABULARY.md, then build or review.
Recon first (two minutes, both modes)
Learn what the project already has:
rg -n "transition|@keyframes|animation:|cubic-bezier|linear\(" --glob "*.{css,scss,html,js,ts,jsx,tsx}"
rg -n "prefers-reduced-motion|startViewTransition|view-transition|\.animate\(|requestAnimationFrame"
rg -n "\-\-(ease|duration|motion)" --glob "*.{css,scss}" # existing motion tokens
rg -n "\"(framer-motion|motion|gsap|animejs|@react-spring/web|auto-animate)\"" package.jsonWrite down: the easing and duration tokens in use (reuse them; add tokens only if none exist), any animation library already installed, whether reduced motion is handled anywhere, and the product's tone. A dense tool UI (dashboards, settings, chat logs) wants calm, quick motion; a landing page can afford more. Match what is there unless it is the problem.
BUILD: the decisions, in order
1. Should it animate at all?
- Frequency. Something seen hundreds of times a day (a menu opened by shortcut, a list row's hover, a tab switch in a tool) gets little or no motion: 100 ms of opacity at most. Anything triggered from the keyboard should feel instant.
- Density. Data-dense screens stay still. Numbers that update every second do not flash or slide on each tick; highlight a change only when it matters to the reader.
- First load. Content the user came for appears at once; no staged entrance on a work screen.
If the honest answer is "it would just look nice", leave it static and say so.
2. Name its purpose
One of: orientation (where did this panel come from, where did it go), feedback (the press, the save, the copy registered), continuity (the same object across two states or pages), attention (one thing needs a look now). No purpose, no animation. The purpose picks the motion: orientation moves along the axis of origin, feedback is small and fast, continuity morphs, attention is brief and never loops.
3. Cheapest tool that does the job
- CSS transition between two states (class, attribute,
:hover,:popover-open). Retargets on its own when the state flips mid-flight. @starting-style+transition-behavior: allow-discreteto animate entering and leavingdisplay: none, popovers and<dialog>without JavaScript (example below).@keyframesfor one-shot sequences or a loop (a spinner). Do not use them for state that can flip back: they restart instead of reversing.- Web Animations API (
el.animate(keyframes, options)) when JS decides the values (a measured height, a FLIP delta) or needsawait anim.finished. - View Transitions API (
document.startViewTransition(update)) for crossfades and shared-element morphs across a DOM swap; feature-detect and callupdate()directly if missing. - A JS library only if one is already in the project, or the user agrees to add it (gesture physics with velocity, complex orchestration). Never add one silently.
4. Properties: transform and opacity
Animate transform (translate, scale, rotate) and opacity; they skip layout and paint.
Write the transitioned properties explicitly — never transition: all, which animates
whatever changes next (colour from a theme switch, height from new content) and costs
layout.
Set transform-origin to where the element comes from. A dropdown under its trigger grows
from top (or top left when left-aligned); a menu opening upward grows from bottom; a
modal centred on screen scales from its centre. A popover that grows from its own centre
looks detached from the button that opened it.
5. Curve and duration
| Situation | Easing | Duration |
| Enters, or responds to input | ease-out | 120–200 ms small UI, 200–300 ms panels/sheets |
| Moves from A to B while on screen | ease-in-out | 200–300 ms |
| Leaves | ease-out or ease, shorter | ~70% of the entrance (e.g. 200 → 140 ms) |
| Press / hover feedback | ease-out | 80–150 ms |
| Constant motion (spinner, progress) | linear | as needed |
Avoid ease-in for UI: it starts slow, exactly when the user is waiting for a response. Keep frequent actions under ~300 ms. If the project has no tokens, these are good defaults:
:root {
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--dur-fast: 120ms; /* feedback */
--dur-base: 180ms; /* popovers, tooltips */
--dur-slow: 260ms; /* panels, sheets */
}Springs fit gesture-driven motion: the element keeps the finger's velocity when released
and settles naturally. CSS has no physical spring; linear() can approximate a fixed spring
curve for a non-gesture entrance, while real velocity hand-off needs JS (a library already
present, or a small damped-spring step function on requestAnimationFrame). Keep overshoot
subtle (a few percent) in product UI.
6. Interruption
People click again before an animation ends. Transitions retarget from the current value;
keyframe animations jump back to their start. So anything that toggles (open/close, expand,
hover) is a transition, not a pair of keyframe animations. With WAAPI, start the next
animation from getComputedStyle(el).transform of the running one, then cancel the old.
7. Enter, exit, and lists
The popover and dialog pattern, no JS, exit faster than entrance:
.menu[popover] {
transform-origin: top left;
opacity: 0;
transform: scale(0.96) translateY(-4px);
/* closed state: these timings drive the EXIT */
transition: opacity 120ms ease-out, transform 120ms ease-out,
overlay 120ms allow-discrete, display 120ms allow-discrete;
}
.menu[popover]:popover-open {
opacity: 1;
transform: none;
/* open state: these timings drive the ENTRANCE */
transition-duration: 180ms;
transition-timing-function: var(--ease-out, ease-out);
}
@starting-style {
.menu[popover]:popover-open { opacity: 0; transform: scale(0.96) translateY(-4px); }
}The same shape works for dialog[open] (add ::backdrop with its own opacity transition) and
for any element toggled between display: none and visible. Browsers that lack
@starting-style simply show the element without the entrance, which is an acceptable
fallback.
Stagger list entrances by 20–40 ms per item and cap it: past 6–8 items, the rest arrive together, so the whole sequence stays under ~300 ms. Never stagger on every re-render or filter keystroke.
Height to auto: prefer interpolate-size: allow-keywords (progressive; no animation where
unsupported) or the grid-template-rows: 0fr → 1fr wrapper. Both run layout per frame; fine
for one accordion, not for fifty rows.
Interaction details
@media (hover: hover) and (pointer: fine) {
.card:hover { transform: translateY(-2px); }
}
.button { transition: transform 120ms var(--ease-out, ease-out); }
.button:active { transform: scale(0.97); }Gate hover effects so touch screens do not keep a stuck hover after a tap. Press feedback at
scale(0.97) reads as "pressed" without looking squashed; skip it on large surfaces (a whole
card list) where it feels like the page moved. Disabled controls do not animate.
Reduced motion
prefers-reduced-motion: reduce means less movement, not no feedback. Replace translation,
scaling, parallax and morphs with an opacity fade or an instant change; keep state changes
visible (an open menu still has to look open).
@media (prefers-reduced-motion: reduce) { /* after the rules above: it also wins in @starting-style */
.menu[popover], .menu[popover]:popover-open { transform: none; }
::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
animation-duration: 1ms;
}
}- The 0 s trap. A transition whose duration and delay are both 0 never starts, so
transitionendnever fires and code waiting for it hangs (a panel never unmounts). Use1msinstead of0s, or do not depend on the event: WAAPI'sfinishedpromise resolves either way, and a fallback timer covers the rest. - In JS:
matchMedia('(prefers-reduced-motion: reduce)').matches, and listen forchange. - Anything that moves on its own for more than 5 seconds (carousel, marquee, looping background) needs a visible pause/stop control (WCAG 2.2.2). Default: no autoplay loops.
- No more than three flashes per second, ever.
Performance
A frame has 16.7 ms at 60 Hz and 8.3 ms at 120 Hz, shared with your JavaScript. The browser runs style → layout → paint → composite; the later the first stage a property touches, the cheaper it is.
- Layout every frame:
width,height,top/left,margin,padding, font size. Usetransforminstead (scale a box, translate a position; FLIP for reflows). - Paint every frame:
box-shadow,background,color,filter: blur()andbackdrop-filteron large areas. Fade a pseudo-element that carries the shadow instead of animating the shadow; keep blurred areas small and static. - Animating a CSS custom property on a parent restyles every descendant that inherits it.
Register it as non-inherited, or animate the child directly:
@property --progress { syntax: "<number>"; inherits: false; initial-value: 0; } will-change: transformonly on a few elements, only shortly before and during the animation, removed afterwards. Leaving it everywhere costs memory and can blur text.- When the main thread is busy (a stream rendering, a big list), CSS and WAAPI animations of
transform/opacity keep running on the compositor;
requestAnimationFrameloops do not.
Diagnose in this order: reproduce with DevTools CPU throttling (4×); record in the
Performance panel and look for long tasks and purple Layout / Recalculate Style blocks during
the animation; turn on Rendering › Paint flashing and Layer borders; slow the animation in the
Animations panel (10–25%) to see the curve itself. Fix the property first, then the JS doing
work per frame, and only then reach for will-change.
React aside: never setState on every frame or pointer move; that re-renders the tree 60
times a second. Write to a ref or a motion value (Motion/framer-motion's useMotionValue,
useSpring) and let the library update the style. A transform written as one string can be
handed to the compositor more readily than separate x/y values.
Preview and check
- Run it on the project's own dev server on 127.0.0.1 or open the file via
file://. Nothing is sent anywhere; no new dependency without asking; no git worktrees. - Screenshots show end states only. To inspect mid-flight with the headless-ui-smoke skill,
trigger the state, then freeze every animation at a known time before the shot:
--eval "document.getAnimations().forEach(a => { a.pause(); a.currentTime = 60 }); 0". The samedocument.getAnimations()lists each running animation's duration and easing, which makes a cheap assertion. - Check once with reduced motion on (DevTools › Rendering › Emulate
prefers-reduced-motion), once at 4× CPU throttling, and on a real phone when touch or gestures are involved. - Accessibility beyond motion (focus, roles, contrast) belongs to the web-accessibility skill; visual polish beyond motion to the ui-craft skill.
Report
BUILD: what animates now, its purpose, the values, reduced-motion behaviour and how you checked it. REVIEW: the report from REVIEW.md. Say plainly what you could not run.
Background: distilled from Emil Kowalski's animation skills and Vercel's web-interface-guidelines; rewritten, nothing copied.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

