micro-interaction
iart-ai/web-animation-skills
Add responsive UI motion: hover effects, toggles, toasts, drawers, and layout transitions with Framer Motion or CSS.
What is micro-interaction?
Micro-interactions are small, functional animations that make interfaces feel responsive and alive. Use this skill for hover/press feedback, animated toggles, toasts, drawers, modals, list reordering, and shared-element transitions. It covers both Framer Motion (motion/react) and modern CSS approaches.
- Animate hover, press, and focus feedback with instant response (100–250ms duration)
- Create enter/exit animations for toasts, drawers, modals, and accordions using AnimatePresence
- Animate list reordering and add/remove with layout prop for automatic transform-based motion
- Implement shared-element (magic move) layout transitions across components
- Build state-driven animations (loading → success → error) that morph continuously
- Use pure CSS with @starting-style and transition-behavior for no-JS animations
How to install micro-interaction
npx skills add https://github.com/iart-ai/web-animation-skills --skill micro-interaction- For Framer Motion: React project with motion/react (v11+) or framer-motion installed
- For pure CSS: modern browser support (Chrome/Edge/Safari for @starting-style and transition-behavior)
How to use micro-interaction
- 1.Choose your approach: Framer Motion for complex state-driven motion or pure CSS for simple enter/exit
- 2.For Framer Motion, wrap conditionally-rendered elements in AnimatePresence and define initial/animate/exit states
- 3.Use whileHover, whileTap, and whileFocus for gesture-based feedback with spring transitions (stiffness: 400, damping: 30 recommended)
- 4.For layout animation, add the layout prop to elements that change position/size; use layoutId for shared-element transitions
- 5.For CSS, define @starting-style to set pre-animation values and add transition-behavior: allow-discrete for display toggles
- 6.Always include @media (prefers-reduced-motion: reduce) to respect accessibility preferences
- 7.Test with ?state= URL parameters to freeze deterministic states and screenshot across idle/hover/open/closed states
Use cases
- Add a scale-down press effect to a button with spring physics
- Slide a toast notification in from the bottom and out before unmounting
- Animate a drawer or modal opening with fade and slide, respecting prefers-reduced-motion
- Reorder list items smoothly when the order changes, using layout animation
- Create a shared-element transition where a card thumbnail expands into a detail view
- React developers building interactive UIs with Framer Motion
- Frontend engineers implementing CSS-only micro-interactions
- Product designers and developers polishing UI feedback and state transitions
- Teams needing accessible animations that respect reduced-motion preferences
micro-interaction FAQ
100–250ms is the sweet spot. Anything over ~400ms feels laggy for a click response. Enter animations are slightly slower (ease-out), exit animations faster (ease-in).
Always animate transform (scale, translate) and opacity—they're GPU-composited and don't trigger layout recalculation. Avoid animating width, height, top, left; use scale or layout animation instead.
Wrap conditionally-rendered elements in AnimatePresence, give each a stable key, and define exit animations. For CSS, use transition-behavior: allow-discrete so display:none participates in the animation.
layout animates any position/size change within a component using transforms. layoutId creates a shared-element transition: two elements with the same layoutId animate as if one morphs into the other when one mounts and the other unmounts.
Yes. Modern CSS with @starting-style and transition-behavior: allow-discrete handles enter/exit and display toggles. Use this for simple interactions; reach for Framer Motion for complex state-driven motion.
Full instructions (SKILL.md)
Source of truth, from iart-ai/web-animation-skills.
name: micro-interaction description: This skill should be used when the user asks to "add a hover/press effect", "animate a toggle or switch", "build an animated like button", "make a toast/snackbar slide in", "animate a drawer or modal", "animate list reordering or add/remove", "do a shared-element layout transition", or "polish UI feedback". Covers UI motion with Framer Motion (motion/react) and modern CSS. version: 0.1.0
Micro-interactions (UI Motion)
Small, functional motion that makes an interface feel responsive and alive: hover/press/focus feedback, toggles, toasts, drawers, and list/layout animation. The goal is feedback and continuity, not decoration.
When to use
- Hover/press/focus feedback; toggles, checkboxes, like/heart buttons
- Toasts/snackbars, drawers, modals, tooltips, accordions (enter/exit)
- List add/remove/reorder; shared-element ("magic move") layout transitions
- Loading → success → error state transitions
Principles (apply to all of it)
- Duration: UI micro-interactions live in 100–250ms. Anything over ~400ms feels laggy for a click response.
- Animate
transformandopacityonly — they are GPU-composited (no layout/paint). Avoid animatingwidth/height/top/left; usescaleor layout animation instead. - Give instant press feedback:
scale: 0.96on tap with a fast spring. - Asymmetric timing: enter slightly slower (ease-out), exit faster (ease-in). Things should arrive gracefully and leave promptly.
- Respect
prefers-reduced-motion: gate non-essential motion; keep opacity changes, drop large movement. - Easing defaults: ease-out for entrances
cubic-bezier(0.16, 1, 0.3, 1); a tasteful overshoot iscubic-bezier(0.34, 1.56, 0.64, 1); standard movecubic-bezier(0.4, 0, 0.2, 1).
Framer Motion (motion/react) essentials
As of v11+, the package is imported as motion/react (the framer-motion name still works).
Gestures and springs
import { motion } from "motion/react";
<motion.button
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.96 }}
whileFocus={{ boxShadow: "0 0 0 3px rgba(59,130,246,.5)" }}
transition={{ type: "spring", stiffness: 400, damping: 30 }}
/>
Spring intuition: higher stiffness = faster; higher damping = less bounce. stiffness: 400, damping: 30 is a snappy UI default. For visible bounce, lower damping (e.g. damping: 12).
Enter / exit with AnimatePresence
Exit animations require AnimatePresence wrapping conditionally-rendered children, each with a stable key.
import { AnimatePresence, motion } from "motion/react";
<AnimatePresence>
{open && (
<motion.div
key="panel"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 8 }}
transition={{ duration: 0.2, ease: [0.16, 1, 0.3, 1] }}
/>
)}
</AnimatePresence>
For toasts/lists, use mode="popLayout" so removed items don't hold space while exiting.
Layout animation (the superpower)
layout automatically animates any layout change (position/size) using transforms — perfect for reordering, expanding cards, and grid<->list.
<motion.li layout transition={{ type: "spring", stiffness: 500, damping: 40 }} />
Shared-element transition across components: give two elements the same layoutId and Framer Motion animates between them as one mounts and the other unmounts.
{!open && <motion.div layoutId="card" onClick={() => setOpen(true)} />}
<AnimatePresence>
{open && <motion.div layoutId="card" />} {/* "magic moves" from the source */}
</AnimatePresence>
Gotcha: a plain layout element distorts border-radius and text during scale. Add layout to direct children that should counter-scale, and prefer borderRadius/boxShadow as motion values, or use layout="position" to animate position only.
Pure CSS path (no JS, including discrete properties)
Modern CSS can animate enter/exit and even display toggles without a library.
.toast {
transition: opacity 0.2s ease, transform 0.2s ease;
/* allow animating to/from display:none and from initial render */
transition-behavior: allow-discrete;
}
.toast[hidden] { display: none; }
/* animate FROM these values on first render / when entering */
@starting-style {
.toast { opacity: 0; transform: translateY(8px); }
}
@starting-style defines the "before-open" values so an element animates in on first appearance; transition-behavior: allow-discrete lets display (and overlay for popovers/dialogs) participate so exit animations run before the element is removed. Baseline in Chrome/Edge/Safari; provide a no-animation fallback for older browsers.
Always include a reduced-motion guard:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
State, not just decoration
Loading → success → error should be one continuous motion (e.g. a button morphs spinner → checkmark), not a jump cut. Keep the element mounted and animate between states so the eye tracks the same object.
Deliver & verify (standalone HTML)
Packaged helper (
scripts/):scripts/seek-shot.sh anim.html 0 1.5 3freezes the?t=Nharness and screenshots each moment;scripts/contact-sheet.sh sheet.png frame-*.pngtiles them for one-glance review. Seescripts/README.md.
For a self-contained interaction demo (toggle, like button, toast, drawer) the deliverable is one HTML file that opens directly in a browser. Pure-CSS interactions ship as-is; for a Framer Motion demo, load React + motion from CDN (esm.sh) into one inline module — no build step. One file is the right tier; don't reach for a bundler.
Output contract:
- One
.htmlfile: your markup plus either CSS transitions/@starting-styleor an inline<script type="module">importingmotionfrom CDN. - A way to land on the resolved end state for a screenshot — interactions are state-driven, not time-driven, so freeze the state rather than a clock.
Seek harness — pin a deterministic state. A micro-interaction's "frames" are its states (idle / hover / pressed / open). ?state=open applies the target state on load so a screenshot captures it settled:
<script>
const s = new URLSearchParams(location.search).get("state");
if (s) document.documentElement.dataset.state = s; // CSS keys off [data-state="open"]
// Framer Motion: set the controlled prop from `s` (e.g. const [open]=useState(s==="open"))
// For a CSS @keyframes loop instead, freeze it: el.style.animationDelay=(-N)+"s"; el.style.animationPlayState="paused";
window.__ready = true;
</script>
Verify loop — render → set state → screenshot → check: open each meaningful state (?state=idle, ?state=hover, ?state=open), screenshot, and check fidelity (press feedback reads instant, exit runs before unmount) plus artifacts (layout distorting border-radius/text, toast holding space after exit, FOUC, jank). Any headless tool works:
npx playwright screenshot --wait-for-timeout=400 "file://$PWD/demo.html?state=open" open.png
Before you finish:
- Opens standalone — no console errors, CDN React/
motion(if used) resolves. - The
?state=(or controlled-prop) freeze lands a deterministic, settled state. - Screenshotted across states — idle / active / open — matches the brief, no artifacts.
prefers-reduced-motionhonored — large movement dropped, opacity/feedback kept.- Easing is intentional — enter ease-out, exit ease-in, durations in the 100–250ms band.
Quick reference
| Need | Approach |
|---|---|
| Press feedback | whileTap={{ scale: 0.96 }} spring 400/30 |
| Enter/exit | AnimatePresence + initial/animate/exit |
| Reorder / resize | layout prop (spring) |
| Magic move | shared layoutId |
| Toast stack | AnimatePresence mode="popLayout" |
| No-JS enter | @starting-style + transition |
| Animate to display:none | transition-behavior: allow-discrete |
| Accessibility | prefers-reduced-motion guard always |
Reference files
references/framer-motion-recipes.md— variants + stagger,AnimatePresencemodes,layout/layoutIdmagic move,Reorderdrag-to-sort, drag constraints, gesture composition, anduseReducedMotion.references/css-recipes.md—@starting-style,transition-behavior: allow-discrete, popover/dialog exit animation, keyframe spinners/toggles,@propertyfor animatable custom properties, andprefers-reduced-motionpatterns.
Related skills
More from iart-ai/web-animation-skills and the wider catalog.

page-transition-animation
Animate page transitions in Next.js App Router with Framer Motion or View Transitions API.

svg-animation
Animate SVGs with draw-on strokes, morphing paths, motion-along-path, and gradient effects.

60fps-animation
Fix janky animations by animating only transform and opacity, skipping expensive layout and paint work.

accessible-animation
Respect prefers-reduced-motion with tiered degradation, not all-or-nothing disabling.

baseline-ui
Enforce UI baseline constraints to prevent AI-generated interface slop.

create-design-md
Extract and document design systems from repositories or live websites into persistent DESIGN.md files.