PluginBench
Skill
Review
Audit score 70

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
Prerequisites
  • 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)
Claude Code
Cursor
Windsurf
Cline

How to use micro-interaction

  1. 1.Choose your approach: Framer Motion for complex state-driven motion or pure CSS for simple enter/exit
  2. 2.For Framer Motion, wrap conditionally-rendered elements in AnimatePresence and define initial/animate/exit states
  3. 3.Use whileHover, whileTap, and whileFocus for gesture-based feedback with spring transitions (stiffness: 400, damping: 30 recommended)
  4. 4.For layout animation, add the layout prop to elements that change position/size; use layoutId for shared-element transitions
  5. 5.For CSS, define @starting-style to set pre-animation values and add transition-behavior: allow-discrete for display toggles
  6. 6.Always include @media (prefers-reduced-motion: reduce) to respect accessibility preferences
  7. 7.Test with ?state= URL parameters to freeze deterministic states and screenshot across idle/hover/open/closed states

Use cases

Good for
  • 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
Who it's for
  • 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

What duration should micro-interactions use?

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).

Should I animate width/height or use scale instead?

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.

How do I make exit animations run before an element unmounts?

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.

What's the difference between layout and layoutId?

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.

Can I do micro-interactions without JavaScript?

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 transform and opacity only — they are GPU-composited (no layout/paint). Avoid animating width/height/top/left; use scale or layout animation instead.
  • Give instant press feedback: scale: 0.96 on 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 is cubic-bezier(0.34, 1.56, 0.64, 1); standard move cubic-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 3 freezes the ?t=N harness and screenshots each moment; scripts/contact-sheet.sh sheet.png frame-*.png tiles them for one-glance review. See scripts/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 .html file: your markup plus either CSS transitions/@starting-style or an inline <script type="module"> importing motion from 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:

  1. Opens standalone — no console errors, CDN React/motion (if used) resolves.
  2. The ?state= (or controlled-prop) freeze lands a deterministic, settled state.
  3. Screenshotted across states — idle / active / open — matches the brief, no artifacts.
  4. prefers-reduced-motion honored — large movement dropped, opacity/feedback kept.
  5. Easing is intentional — enter ease-out, exit ease-in, durations in the 100–250ms band.

Quick reference

NeedApproach
Press feedbackwhileTap={{ scale: 0.96 }} spring 400/30
Enter/exitAnimatePresence + initial/animate/exit
Reorder / resizelayout prop (spring)
Magic moveshared layoutId
Toast stackAnimatePresence mode="popLayout"
No-JS enter@starting-style + transition
Animate to display:nonetransition-behavior: allow-discrete
Accessibilityprefers-reduced-motion guard always

Reference files

  • references/framer-motion-recipes.md — variants + stagger, AnimatePresence modes, layout/layoutId magic move, Reorder drag-to-sort, drag constraints, gesture composition, and useReducedMotion.
  • references/css-recipes.md — @starting-style, transition-behavior: allow-discrete, popover/dialog exit animation, keyframe spinners/toggles, @property for animatable custom properties, and prefers-reduced-motion patterns.