AgentStack
SKILL verified MIT Self-run

Animated Svg Icons

skill-convenientsolutions-animated-svg-icons-animated-svg-icons · by ConvenientSolutions

How to build, animate, debug, and optimize high-fidelity interactive SVG icons using Framer Motion or requestAnimationFrame. Make sure to use this skill whenever the user requests creating a new animated icon, fixing a sticky or glitchy hover transition on an SVG, implementing hover states for cards, or refining micro-animations, even if they don't explicitly mention 'Framer Motion' or 'requestAn…

No reviews yet
0 installs
7 views
0.0% view→install

Install

$ agentstack add skill-convenientsolutions-animated-svg-icons-animated-svg-icons

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Animated Svg Icons? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Animated SVG Icon System

This skill guides you through building premium, high-performance, micro-interactive SVG icons. These icons respond to cursor hovers and scroll-based triggers using two distinct stages:

  1. Stage 1 (Hover On / Active): The forward animation plays. The icon may end in a different visual pose than it started.
  2. Stage 2 (Hover Off / Settle): The icon returns smoothly to its exact initial state, ensuring visual consistency when the interaction ends.

Design Philosophy & Creative Motion

High-fidelity icons should be expressive, engaging, and creative rather than just doing basic linear rotations or scales. Delightful motion design brings icons to life through:

  • Expressive Movement & Physics: Leverage physics-based easing curves like squash-and-stretch, gravity swings, secondary actions, and overshoot bounces. For example, a briefcase swinging slightly under gravity, or a gear spinning with an elastic overshoot.
  • Storytelling & Interaction: Introduce micro-details that emerge during interaction to surprise and engage the user. For example, a document sliding out of a briefcase, or a head sliding into a hard hat.
  • Positional Swaps: It is highly effective and visually engaging to have parts of the icon switch positions during hover, as seen in quote mark animations (where quotes swap places on an arc path) or code tag <> animations (where brackets slide past each other to swap sides). This creates a playful, interactive loop.
  • Multi-layered Independence: Animate different sub-elements asynchronously. For instance, separate a figure's head from its body to allow a subtle head-bob/tilt while a background gear rotates.
  • Reuse in References: It is perfectly fine and encouraged to reuse, adapt, and reference these icons or code structures across different case studies, projects, and reference documentations.
  • A Guideline, Not a Constraint: These techniques should inspire delightful, creative details that elevate the overall user experience without being a rigid constraint.

1. Sizing and Layout Constraints

| Sizing Context | Icon Dimensions | Container Style | Default Stroke Width | Default Opacity | | :--- | :--- | :--- | :--- | :--- | | Trust Bar / KPI Cards | 18px × 18px | 38px × 38px, rounded, absolute positioning | 1.75 | 0.62 (inactive) / 1.0 (hovered) | | Capabilities Cards | 16px × 16px | 36px × 36px inline header row | 2.0 | 0.72 (inactive) / 1.0 (hovered) | | Process Steps | 16px × 16px | 36px × 36px on timeline margin | 2.0 | Magenta highlight when active |

Layout & SVG Structure Rules

  • No Layout Shifting: Always wrap the icon in a fixed-size container (e.g., h-[38px] w-[38px]) to ensure the page layout is locked.
  • Overflow Handling: If elements translate outside the standard viewBox, add style={{ overflow: "visible" }} to the SVG.
  • Isometric / Wireframe Shapes:
  • To prevent overlapping "layer" or "flat panel stack" bugs, do not use filled polygons for both the top and bottom of a wireframe (e.g. a cube).
  • Keep the bottom face as an open stroke-only path (e.g., using `` without closing it and without filling it) to allow internal vertical lines to connect to it cleanly.
  • Only fill the top face if needed to mask/hide internal overlapping segments.

2. Advanced Animation Patterns

Pattern A: Emerging Clipped Element (Emerging Shoulders/Body)

Use when a background element slides out from behind a foreground element, organic body extensions, or slide-out details. To make a background element emerge naturally without showing awkward overlapping paths:

  1. Clipped Group: Place the background element in a ` group clipped by a mask containing a ` aligned with the boundary.
  2. Double State Fade: Nest the element's paths inside two groups:
  • A partial group (fades out from opacity 1 to 0 on hover).
  • A full group (fades in from opacity 0 to 1 on hover).
  1. Dynamic Return Guard: Since Stage 1 ends in a different visual pose, use a pendingReturnRef and a timerRef to queue Stage 2 animations in case the user hovers off quickly during Stage 1.
  2. Unique Mask IDs: Always use React's useId() hook to generate unique IDs for `` elements to avoid ID collision bugs when multiple instances of the icon are on the same page.
import { motion, useAnimation } from "framer-motion";
import { useEffect, useRef, useId } from "react";

export function AnimatedUsersIcon({ hovered }: { hovered: boolean }) {
  const rightCtrl = useAnimation();
  const partialCtrl = useAnimation();
  const fullCtrl = useAnimation();
  const playingRef = useRef(false);
  const pendingReturnRef = useRef(false);
  const timerRef = useRef>();
  const clipId = useId();

  useEffect(() => () => clearTimeout(timerRef.current), []);

  const playReturn = () => {
    rightCtrl.start({ x: 0, transition: { duration: 0.35, ease: [0.34, 1.56, 0.64, 1] } });
    fullCtrl.start({ opacity: 0, transition: { duration: 0.12 } });
    partialCtrl.start({ opacity: 1, transition: { duration: 0.12 } });
  };

  useEffect(() => {
    if (hovered) {
      clearTimeout(timerRef.current);
      playingRef.current = true;
      pendingReturnRef.current = false;
      
      rightCtrl.set({ x: 0 });
      partialCtrl.set({ opacity: 1 });
      fullCtrl.set({ opacity: 0 });
      
      rightCtrl.start({ x: 5, transition: { duration: 0.35, ease: [0.34, 1.56, 0.64, 1] } });
      partialCtrl.start({ opacity: 0, transition: { duration: 0.12 } });
      fullCtrl.start({ opacity: 1, transition: { duration: 0.12 } });
      
      timerRef.current = setTimeout(() => {
        playingRef.current = false;
        if (pendingReturnRef.current) {
          pendingReturnRef.current = false;
          playReturn();
        }
      }, 400);
    } else {
      if (playingRef.current) {
        pendingReturnRef.current = true;
      } else {
        playReturn();
      }
    }
  }, [hovered]);

  return (
    
      
        
          
        
      
      {/* Clipped background figure */}
      
        
          
            
            
          
          
            
            
          
        
      
      {/* Front figure */}
      
        
        
      
    
  );
}

Pattern B: Visually Identical Swap Loop (Symmetric Swap with Conditional Settle)

Use when an icon has symmetric or identical sub-elements that swap places on hover (like double quote marks). Because the target swapped state looks identical to the initial resting state, a quick hover-off should not play the return animation, whereas a standard slow hover-off should.

Logic Rules:
  1. Normal Hover-off: If the cursor enters and stays past the end of Stage 1 (playing finishes), hovering off plays a smooth Stage 2 return animation back to 0.
  2. Quick Hover-off (Aborted Settle): If the cursor leaves while Stage 1 is still actively playing, we do not trigger or queue Stage 2. Instead, Stage 1 completes, and we instantly/silently reset all coordinates back to 0.
  3. Tracking State:
  • playingRef = useRef(false): tracks whether Stage 1 is currently animating.
  • hoveredRef = useRef(hovered): tracks latest hovered state inside asynchronous timeout handlers.
import { motion, useAnimation } from "framer-motion";
import { useEffect, useRef } from "react";

export function AnimatedQuote({ hovered }: { hovered: boolean }) {
  const a = useAnimation();
  const b = useAnimation();
  const playingRef = useRef(false);
  const hoveredRef = useRef(hovered);
  const timerRef = useRef>();

  useEffect(() => {
    hoveredRef.current = hovered;
  }, [hovered]);

  useEffect(() => () => clearTimeout(timerRef.current), []);

  const playReturn = () => {
    a.start({ x: 0, y: 0, transition: { duration: 0.5, ease: "easeOut" } });
    b.start({ x: 0, y: 0, transition: { duration: 0.5, ease: "easeOut" } });
  };

  useEffect(() => {
    if (hovered) {
      clearTimeout(timerRef.current);
      playingRef.current = true;

      // Stage 1: swap places
      a.start({ x: [0, 12], y: [0, -3.5, 0], transition: { duration: 0.5 } });
      b.start({ x: [0, -12], y: [0, 3.5, 0], transition: { duration: 0.5 } });

      timerRef.current = setTimeout(() => {
        playingRef.current = false;
        
        // If user hovered off during Stage 1, silently reset without animating Stage 2
        if (!hoveredRef.current) {
          a.set({ x: 0, y: 0 });
          b.set({ x: 0, y: 0 });
        }
      }, 500);
    } else {
      // Hover off: only play Stage 2 return if Stage 1 has finished playing
      if (!playingRef.current) {
        playReturn();
      }
    }
  }, [hovered]);

  return (
    
      
      
    
  );
}

Pattern C: Background Head Silhouette with Opaque Foreground Masking

Use when an element slides underneath a hollow/transparent outlined shape (e.g. a head sliding into a helmet, or document sliding into a folder) and the foreground shape needs to block the overlapping background paths.

Logic Rules:
  1. Opaque Mask Paths: Put filled mask shapes (fill="var(--color-surface)" stroke="none") inside the foreground container group to block overlapping visual paths, without styling the outer strokes.
  2. Order of Rendering: Render the moving background element first, and the masking foreground container on top of it.
  3. Proportions: Ensure the moving element sits in a natural physical position relative to the foreground shape.
import { motion, useAnimation } from "framer-motion";
import { useEffect, useRef } from "react";

export function AnimatedHardHat({ hovered }: { hovered: boolean }) {
  const helmet = useAnimation();
  const top = useAnimation();
  const head = useAnimation();
  const timerRef = useRef>();

  const playReturn = () => {
    helmet.start({ y: 0, scaleY: 1, scaleX: 1, transition: { duration: 0.45 } });
    head.start({ y: 8, opacity: 0, transition: { duration: 0.45 } });
  };

  useEffect(() => {
    if (hovered) {
      clearTimeout(timerRef.current);
      helmet.start({
        y: [-1, -3.5, 0.5, 0],
        scaleY: [1, 1.06, 0.95, 1],
        scaleX: [1, 0.94, 1.05, 1],
        transition: { duration: 0.72, ease: "easeInOut" },
      });
      head.start({
        y: 0,
        opacity: 1,
        transition: { duration: 0.62, ease: [0.34, 1.56, 0.64, 1], delay: 0.08 }
      });
    } else {
      playReturn();
    }
  }, [hovered]);

  return (
    
      {/* Sliding head is rendered underneath */}
      
        
        
        
      

      {/* Helmet with opaque masks is rendered on top */}
      
        
        
        
        {/* Outlined helmet strokes */}
        
        
        
        
      
    
  );
}

3. The Two Animation Engines & Dependency Rules

A. Framer Motion (framer-motion)

Best for complex multi-element sequences, keyframe arrays, and staggered reveals.

  • Rule: Always reset the position using .set() if restarting the same target transition.

B. requestAnimationFrame (rAF)

Best for ultra-high-performance mathematical easing, zero React re-render overhead, and fully interruptible, state-tracking physics (e.g., squashing, spinning).

> [!CAUTION] > Avoid Recursive Render Loops: > Do NOT include ticking state variables (like offset or dy) inside your useEffect dependency array. The dependency array must only be [hovered]. > Ticking variables should be stored in useRef objects (e.g. dyRef, offsetRef) and animated by mutating the DOM elements directly using React refs. This prevents triggering the effect recursively.

Example: Controlled rAF Slide on Hover & Return on Hover-off
import { useEffect, useRef } from "react";

export function AnimatedGlobeIcon({ hovered }: { hovered: boolean }) {
  const meridianRef = useRef(null);
  const equatorRef = useRef(null);
  const rafRef = useRef(0);
  const offsetRef = useRef(0); // tracks position without triggering re-renders

  useEffect(() => {
    cancelAnimationFrame(rafRef.current);
    const start = performance.now();
    const from = offsetRef.current;

    if (hovered) {
      function tickOn(now: number) {
        const progress = Math.min((now - start) / 400, 1);
        const eased = progress 
      
      
      
        
      
    
  );
}

4. Easing and Timing Reference

| Ease Name | Keyframe Array / Config | Character | | :--- | :--- | :--- | | Bouncy | [0.34, 1.56, 0.64, 1] | 56% overshoot, snappy settle. Perfect for slides and pops. | | Expo-out | [0.16, 1, 0.3, 1] | Fast start, gradual deceleration. Great for structural reveals. | | Linear | "linear" string | Uniform speed. Reserved for path drawing animations. | | Wobble Decay | Keyframe rotation list | Vibrate effect (e.g. [0, -12, 12, -12, 12, -8, 8, -4, 4, 0]). |


5. Implementation Checklist

  • [ ] Wrapper Constraints: Is the icon sized correctly and wrapped in a fixed-size container?
  • [ ] Unmount Cleanup: Are all clearTimeout, clearInterval, and cancelAnimationFrame IDs properly cleared in a useEffect return function?
  • [ ] Quick Hover-off Guard: If Stage 1 ends in a custom state, did you implement pendingReturnRef and timerRef to prevent sticking?
  • [ ] rAF Hook Dependencies: Did you ensure useEffect hooks running rAF loops only depend on [hovered], using refs to hold animated coordinates?
  • [ ] Aesthetics and Layers: For wireframes, are the base segments open stroke-only paths to avoid awkward filled layer overlaps?

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.