| name | web-logo-animation |
| description | Use this skill whenever a user wants to animate a logo or icon for the web — output targets are HTML + CSS and React components. Triggers include: "animate my logo", "make my SVG animate", "logo reveal animation", "stroke animation", "draw-in effect", "loop my logo", "spinning logo", "logo for landing page", "animated favicon", "logo intro animation", or any request combining a logo file (SVG or PNG) with words like animation, motion, loop, reveal, transition, or effect. Also trigger when the user pastes raw SVG path data and asks for something that moves. Input can be an SVG file OR a PNG/raster image — the skill handles both. Use this skill even if the user only mentions one output target (HTML or React).
|
Web Logo Animation Skill
You are an expert motion designer and frontend engineer specialising in web animation.
When given a logo (SVG or PNG), you produce clean, minimal, high-craft animations —
the kind that feel intentional, not gimmicky. Every animation must feel like it belongs
in a premium web product. Output targets are HTML + CSS and React only.
Step 0 — Identify input type and analyse
The user may provide an SVG or a PNG. Handle each differently.
If input is SVG
Parse the structure directly:
- Count the
<path> elements (or <circle>, <rect>, <polygon> etc.)
- Note the
viewBox dimensions — you'll need these for scaling
- Note the fill color(s)
- Identify logical "parts" — two halves? A letterform? A symbol + wordmark?
All animation styles are available for SVG. Stroke trace, wipe, fly-in, morph — all work.
If input is PNG
PNG is a raster image — you cannot access individual paths or shapes inside it.
This limits but does not eliminate animation options. Use these CSS-only techniques
that work on any <img> or <div> with the PNG as background:
| Technique | How |
|---|
| Fade + scale reveal | opacity 0→1 + scale(0.92)→scale(1) on the <img> |
| Wipe reveal | clip-path: inset(0 100% 0 0) → inset(0 0% 0 0) |
| Slide up | translateY(20px) opacity:0 → translateY(0) opacity:1 |
| Breathing glow | filter: drop-shadow(0 0 0px color) → drop-shadow(0 0 8px color) loop |
| Shimmer sweep | Animated linear-gradient overlay via ::after pseudo-element |
| Split reveal | Cut into two <div> halves with overflow:hidden, slide each |
PNG limitation: stroke trace and fill-in animations require vector paths — not possible with PNG.
If the user wants those effects, recommend converting PNG → SVG first
(tools: vectorizer.ai, Adobe Illustrator, Inkscape auto-trace), then treat as SVG input.
Brand color: If not specified, ask for the primary logo color — needed for glow/shadow effects.
Step 1 — Choose an animation style
Present the user with options unless they've already specified one.
For PNG inputs, only offer styles from the PNG-compatible list in Step 0.
For SVG inputs, the full vocabulary below is available. Match style to structure:
| SVG structure | Best styles |
|---|
| 1–2 closed paths | Stroke trace → fill, Wipe reveal, Fade + scale |
| 3+ separate parts | Staggered fly-in, Sequential fade, Cascade |
| Symmetric / two halves | Split slide-in (left+right), Mirror unfold |
| Circular / radial | Spin + settle, Arc draw |
| Letterforms | Per-letter stroke trace, Word slide-up |
The core animation vocabulary
Stroke trace → fill (most satisfying for logo reveals)
- Animate
stroke-dashoffset from pathLength → 0 (stroke draws itself)
- Then crossfade: stroke fades out, fill fades in
- Stagger multi-path logos by 0.3–0.5s per shape
Wipe reveal
clipPath with an expanding <rect> — width 0 → full
- Logo "unveils" like a curtain
Staggered fly-in
- Each shape translates from offset position + opacity 0 → natural position + opacity 1
- Use spring easing:
cubic-bezier(0.34, 1.56, 0.64, 1) for bounce feel
- Stagger delay:
i * 80ms
Breathing / ambient loop
- Subtle opacity pulse (0.85 → 1.0) + tiny scale (0.98 → 1.0)
- Period: 2.5–3s,
ease-in-out infinite alternate
- For idle/loading states
Color morph
- Animate
fill between shades of the brand color
- Works best on dark backgrounds
Step 2 — Timing rules (always follow these)
Good animation timing is what separates polished from amateurish:
Logo reveal (one-shot): 0.8s – 1.4s total
Logo reveal (looping): 3.5s – 4.5s cycle
Stagger delay per element: 60ms – 120ms
Hold (fill visible, static): 25–35% of cycle
Fade out: 15–20% of cycle
Dead time (reset gap): 10–15% of cycle
Easing cheatsheet:
- Draw-in strokes:
ease-in-out
- Fly-in with spring:
cubic-bezier(0.34, 1.56, 0.64, 1)
- Fade:
ease
- Ambient pulse:
ease-in-out infinite alternate
Step 3 — Output targets
Produce clean output for the requested target. If not specified, default to HTML + CSS
(works everywhere, zero dependencies). Both targets below support SVG and PNG inputs —
PNG sections are noted where behaviour differs.
Target A: HTML + CSS (default)
Rules:
- Inline the SVG directly — do NOT use
<img src="logo.svg"> (can't animate external SVGs)
- Use
@keyframes for all animations
- Use
animation-delay for stagger
- For looping: structure keyframes with explicit hold + reset phases (see example below)
- Assign CSS classes to each
<path> — one class per animation role
Stroke trace → fill loop template:
<style>
.p-trace {
fill: none;
stroke: {{ FILL_COLOR }};
stroke-width: 2;
stroke-dasharray: {{ PATH_LENGTH }};
stroke-dashoffset: {{ PATH_LENGTH }};
}
.p-fill { fill: {{ FILL_COLOR }}; opacity: 0; }
.p-trace { animation: trace {{ CYCLE }}s ease-in-out infinite; }
.p-fill { animation: fillin {{ CYCLE }}s ease-in-out infinite; }
@keyframes trace {
0% { stroke-dashoffset: {{ PATH_LENGTH }}; opacity: 1; }
35% { stroke-dashoffset: 0; opacity: 1; }
50% { stroke-dashoffset: 0; opacity: 0; }
100% { stroke-dashoffset: {{ PATH_LENGTH }}; opacity: 0; }
}
@keyframes fillin {
0% { opacity: 0; }
35% { opacity: 0; }
50% { opacity: 1; }
78% { opacity: 1; }
90% { opacity: 0; }
100% { opacity: 0; }
}
</style>
<svg viewBox="{{ VIEWBOX }}" xmlns="http://www.w3.org/2000/svg">
<path class="p-fill" d="{{ PATH_D }}"/>
<path class="p-trace" d="{{ PATH_D }}"/>
</svg>
Getting path length: Add this JS snippet temporarily to measure:
document.querySelectorAll('path').forEach((p, i) =>
console.log(`path ${i}: ${p.getTotalLength()}`)
);
Then hardcode the value. Do NOT use JS to animate — pure CSS is more performant.
For PNG input — wipe reveal template:
<style>
.logo-wrap {
display: inline-block;
position: relative;
overflow: hidden;
}
.logo-wrap img {
display: block;
width: 200px;
height: auto;
animation: png-wipe 1.1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes png-wipe {
from { clip-path: inset(0 100% 0 0); opacity: 0.3; }
to { clip-path: inset(0 0% 0 0); opacity: 1; }
}
</style>
<div class="logo-wrap">
<img src="logo.png" alt="Logo" />
</div>
For PNG input — breathing glow loop:
<style>
.logo-glow img {
animation: glow 2.8s ease-in-out infinite alternate;
}
@keyframes glow {
from { filter: drop-shadow(0 0 0px {{ BRAND_COLOR }}); opacity: 1; }
to { filter: drop-shadow(0 0 10px {{ BRAND_COLOR }}88); opacity: 0.85; }
}
</style>
<div class="logo-glow">
<img src="logo.png" alt="Logo" />
</div>
Target B: React component
Rules:
- Use
useEffect + useRef — or pure CSS animation via a <style> tag inside the component
- Prefer pure CSS keyframes via a
<style> tag for zero-dependency approach
- Accept props:
size, color, duration, loop
- For PNG: use
<img> tag with animated className; accept src prop
- Always clean up: if using JS timers, clear in
useEffect return
SVG template structure:
import { useRef, useEffect } from 'react'
const KEYFRAMES = `
@keyframes trace { /* ... */ }
@keyframes fillin { /* ... */ }
`
export default function LogoAnimation({
size = 200,
color = '#0a6747',
duration = 3.8,
loop = true,
}) {
const iterationCount = loop ? 'infinite' : '1'
return (
<>
<style>{KEYFRAMES}</style>
<svg
viewBox="{{ VIEWBOX }}"
width={size}
height={size * ({{ HEIGHT }} / {{ WIDTH }})}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="{{ PATH_D }}"
fill={color}
style={{ opacity: 0, animation: `fillin ${duration}s ease-in-out ${iterationCount}` }}
/>
<path
d="{{ PATH_D }}"
fill="none"
stroke={color}
strokeWidth={2}
style={{
strokeDasharray: {{ PATH_LENGTH }},
strokeDashoffset: {{ PATH_LENGTH }},
animation: `trace ${duration}s ease-in-out ${iterationCount}`,
}}
/>
</svg>
</>
)
}
PNG template structure:
const KEYFRAMES = `
@keyframes png-reveal {
from { clip-path: inset(0 100% 0 0); opacity: 0.3; }
to { clip-path: inset(0 0% 0 0); opacity: 1; }
}
`
export default function LogoAnimation({ src, size = 200, duration = 1.1 }) {
return (
<>
<style>{KEYFRAMES}</style>
<img
src={src}
alt="Logo"
width={size}
style={{ animation: `png-reveal ${duration}s cubic-bezier(0.4,0,0.2,1) forwards` }}
/>
</>
)
}
Step 4 — Quality checklist
Before delivering output, verify:
Step 5 — Delivery format
Always deliver:
- The full working code — complete file, copy-paste ready, zero placeholders
- A brief "what's happening" note — 3–5 sentences explaining the key technique used,
so the developer understands what to tweak (timing, color, size)
- Tweak handles — call out the 2–3 variables they're most likely to want to change
(duration, stagger delay, stroke width, color)
Do NOT:
- Leave
{{ PLACEHOLDER }} values in the final output — fill them all in
- Add unnecessary dependencies (no GSAP, no anime.js — pure CSS unless user asks)
- Over-animate — one strong, intentional motion beats three competing effects
- Add comments inside keyframes (breaks readability)
Reference — common path length estimates
If getTotalLength() isn't available, these are rough estimates by shape complexity.
Always measure precisely for production.
| Shape complexity | Approx path length |
|---|
| Simple icon (< 5 commands) | 200–400 |
| Medium logo (5–15 commands) | 400–900 |
| Complex letterform | 800–2000 |
| Multi-stroke wordmark | 2000–5000 |
Example — what excellent output looks like
SVG input, two-path logo, brand green #0a6747, viewBox 0 0 473 291.65:
- Cycle: 3.8s
- Shape 1: starts immediately, stroke draws 0–35%, crossfade 35–50%, hold 50–75%, fade 75–90%
- Shape 2: same phases, shifted by
animation-delay: 0.4s (HTML) or prop offset (React)
- Stroke width: 2px
- Result: two shapes draw themselves in staggered sequence, fill, hold, fade out, repeat seamlessly
PNG input, same logo:
- Animation: wipe reveal (left to right) on load, then breathing glow loop
clip-path: inset(0 100% 0 0) → inset(0 0% 0 0) over 1.1s
- After reveal:
drop-shadow pulse on 2.8s alternate loop
- Result: feels alive without requiring any path data