Skip to content
cccskills
FreePublished on GitHub

animating-icons

Author, review, or batch-produce SVG icon hover gestures and icon↔icon morphs (Hugeicons/Lucide/Phosphor). Use for animated icon, icon hover, per-icon gesture, icon morph, menu↔cross, stroke-dashoffset draw-on, pathLength, transform-box. Not for page transitions, Lottie, or general UI chrome motion.

The English summary is being prepared. Showing the original description.

Works withPlaywright
See how to install

Files included (19)

  • SKILL.md9.5 KB
  • LICENSE1.1 KB
  • references/EASING.md6.7 KB
  • references/EXAMPLES.md11.2 KB
  • references/FAILURES.md16.3 KB
  • references/FAMILIES.md20.4 KB
  • references/FILL.md3.6 KB
  • references/ICON-MORPH.md6.5 KB
  • references/MORPH.md16.5 KB
  • references/TECHNIQUE.md14.8 KB
  • references/VERIFY.md17.7 KB
  • scripts/contact-sheet.mjs3.9 KB
  • scripts/contact-sheet.sh232 B
  • scripts/fixtures/broken.html684 B
  • scripts/fixtures/icon.html1.4 KB
  • scripts/seek-shot.mjs1.9 KB
  • scripts/seek-shot.sh268 B
  • scripts/smoke.mjs4.5 KB
  • scripts/verify-icon.mjs9.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Animating Icons

An icon is not a picture you move. It is an object that does something, and the animation is that thing happening once.

Core principle: the gesture is the icon's meaning acting on itself. A bell rings, a plug disconnects, a package lands. The motion has to be something only that object could do. Whole-<svg> shakes that would look identical on any glyph are decoration — delete them.

Read next

SituationOpen
Hover gesture on one glyphreferences/FAMILIES.md, then references/EXAMPLES.md
Shape itself bends (book, hand, flap)references/MORPH.md
Icon A becomes icon B (menu↔cross)references/ICON-MORPH.md
Authoring CSS / SVG / driverreferences/TECHNIQUE.md + references/FAILURES.md
Fill (Phosphor-style) inkreferences/FILL.md
Easing / peak speedreferences/EASING.md
Done codingreferences/VERIFY.md + scripts/verify-icon.mjs

The swap test

Could these exact keyframes be pasted onto a different icon and still look fine? If yes, it is decoration. Run twice: once on the idea, once on the finished icon.

FailsPasses
Whole <svg> shakes / bounces / spinsOne part moves against another part
Every icon in a batch got the same recipeThe recipe was chosen from the icon's verb
"Pulse" or "wiggle" applied to a nounThe object's own mechanism operates

The four gates

Every icon passes all four. Verify by rendering — never by reasoning. Details in VERIFY.md.

  1. Swap test — gesture unique to this icon's meaning.
  2. Rest test — frame 0 and final frame are pixel-identical to the source glyph (difference overlay). Licensed exception: a deliberate redraw, stated out loud.
  3. Strip test — every intermediate frame still reads as the icon.
  4. Motion test — peak speed under the aliasing ceiling; min speed ~0 in any beat meant to stop; claimed ratios measured off the computed matrix.

Landing on rest

Gate 2 shapes the gesture. Name which landing you use before designing beats:

  1. Symmetry — end on a rotation/reflection that maps the drawing onto itself. Measure the symmetry, never count the lobes (optical adjustment often lowers the true order).
  2. Congruence — identical parts (up to translation) can end swapped; check absolute point sets, not command fingerprints.
  3. Re-arm while invisible — jump state outside the clip / behind a part / at opacity: 0 / scale(0) on a hundredth-of-a-percent frame; prove invisibility by measurement.
  4. Return — go out and come back with a different character on the return leg (not a plain undo).

If you cannot name which of the four, you do not have a gesture yet.

Choose the mechanism

What is happeningReach forNot
Part slides / turns / drops; shape unchangedCSS transform on a wrapper <g>a morph
Shape genuinely changes (paper bends, book closes)d-morph (MORPH.md)scaleY/skew fakery
Something goes inside / in front / is a holemask or clipPathopacity; z-order
A line is written or erasedstroke-dashoffseta fade
Any icon becomes any other in one UI slotthree-line morph (ICON-MORPH.md)a crossfade

Ask every time: is the object changing shape, or my view of a rigid object? Almost always the second → transform. Prefer a hard-swap at a covered instant over a morph when coverage is provable.

Procedure

  1. Read the geometry — open the source; count elements; find hinges, seams, symmetry. Hugeicons free is usually pre-split. Hand-drawn storyboards: classify the second pose (byte-identical / translated / fuller redraw) before designing — see MORPH.md.
  2. Name the verb — one sentence the object does ("a plug comes out"), not a shape description.
  3. Pick the one part that carries the verb; the rest holds still as the frame.
  4. Choose a family from FAMILIES.md. Do not invent a gesture per icon.
  5. Decide the landing (symmetry / congruence / re-arm / return) — it often sets the numbers.
  6. Derive every number from the geometry. Measure, do not taste. Dwells/decay ratios come from the budget table below.
  7. Write it — non-negotiable constraints:
    • CSS keyframes, no JS animation library (exception: icon-swap / Motion).
    • No layout triggers. Prefer transform / opacity; stroke-dashoffset and discrete d / visibility OK. Never width/height/x/y/r/cx/cy.
    • No px on the root <svg> or HTML wrapper unless computed from size (TECHNIQUE.md § Units).
    • Last keyframe is rest. Declare rest in a base rule, not only at 0% (FAILURES.md #2).
    • Animation names prefixed ig- so the driver and harness can count them.
  8. Verify — run VERIFY.md; second pass with fresh eyes.

The budget

Gesture — plays once on hover (library browse = rare):

Duration, ONE event600–1100ms
Duration, N countable beats~350–400ms per beat
Dwell before start130ms
Abandoned mid-flightspeed up ×2.2, never cancel

Derive duration from the set you ship into, not vibe:

grep -oE 'animation: *ig-[a-z0-9-]+ +[0-9.]+s' . | grep -oE '[0-9.]+s' | sort -n | uniq -c

Beats below ~250ms blur into one event. Anticipation needs real time (~40ms+), not a percentage of a tiny beat.

Reduced — sidebar / 100+ hovers a day: 140–220ms, ≤1px translate, scale ≤1.10, rotate ≤16°, holds while hovered. Ask how often it is seen before picking a tier.

Playback contract

Play once on hover, guaranteed to finish. Drive off data-go + dwell, not bare :hover (TECHNIQUE.md § Driver). Honour prefers-reduced-motion: reduce with animation: none (safe only because of gate 2). Icon-swap under reduce: instant coordinate set, no tween.

Done checklist

  • Landing strategy named (one of four)
  • Family chosen from FAMILIES.md; numbers derived from this glyph
  • Rest declared in base rules; ig- prefix on animation names
  • scripts/verify-icon.mjs run (or equivalent strip + rest shots); sheet Read
  • Difference overlay clean at frame 0 and final
  • Motion: peak under ceiling; holds actually stop (~0 speed)
  • Swap test against another family's icon
  • Reviewed at 24px and 96px; prefers-reduced-motion sits at rest
  • Duplicate-id safe if the icon can appear twice
  • Second pass done

Full gate list: VERIFY.md.

Red flags — stop and re-derive

  • Keyframes written before opening the source d
  • Cannot name the landing
  • Curve chosen by name for a large turn without computing peak slope (EASING.md)
  • Transform on the root <svg>
  • Two icons share keyframe values not derived from matching geometry
  • A number ends in 0 or 5 with no geometric source
  • ease: linear on anything that is not a spin or marquee
  • Looks right only because it is small
  • Probe returned 0 and was not shown capable of returning non-zero

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Web (CSS/JS/React + GSAP/Motion): classify UI motion as communicative (sequence is the content) or decorative (decorating a result), then fix or apply prefers-reduced-motion so meaning survives under reduce. Use when auditing existing animation, gating new motion, verifying both preference states, catching a dead reduced-motion branch, snapping to an end state, or collapsing a transition without nuking essential cues. Not for React Native, full WCAG encyclopedias, keyboard traps, color contrast, focus management, or ARIA pattern audits.

The English summary is being prepared. Showing the original description.

SoraLabsOSS/skills28Updated Aug 28, 2026

See all skills by SoraLabsOSS

Report a problem with this skill