Add motion-design skills from iart-ai/motion-design-skills

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TAaJBzbmePTECuv62BXYX
This commit is contained in:
Claude committed 2026-10-14 23:16:56 +00:00
1 parent 6bd8165e6d
commit d4728061a9
47 files changed
+4385

No files matched your search

Whitespace-only changes.
+148
View File
@@ -0,0 +1,148 @@
---
name: logo-animation
description: This skill should be used when the user asks to "animate our logo", "make a logo intro/stinger", "build a brand reveal", "create an app splash animation", "loop a loader logo", "draw on our SVG mark", "morph an icon into the wordmark", or "sync the logo to a sound-logo". Covers web (SVG/Lottie), video, and app-splash delivery.
version: 0.1.0
---
# Logo Animation
Bring a brand mark to life with one clear idea, on-brand geometry, and a crisp settle. Animate for web (SVG/Lottie), video intros/outros, or app splash screens, and always ship a static end-frame fallback.
## When to use
- Build an intro stinger, sign-off, app splash, or loader idle loop.
- Reveal a brand mark on a landing hero or sizzle reel.
- Draw on an SVG mark, wipe/build it in piece by piece, or morph an icon into a wordmark.
- Sync the final settle of the mark to a sound-logo beat.
## Core principle
Pick ONE technique per logo — draw OR build OR morph — not all at once. Reveals run 0.8–2.5s. Never distort brand geometry; scale and fade are safe, skew and squash usually are not. Respect clearspace (keep the mark's minimum margin clear of motion debris). Always provide a `prefers-reduced-motion` path that shows the final mark with no motion.
## Technique map
| Technique | Best for | Mechanism |
|---|---|---|
| Stroke draw-on | Line marks, monograms, signatures | `stroke-dashoffset` 1→0 |
| Mask wipe / reveal | Filled shapes, gradients | animate a `<clipPath>` / `<mask>` rect |
| Build-on (stagger) | Multi-piece marks, grids | per-group opacity+transform, staggered |
| Morph | Icon ↔ wordmark, shape→logo | GSAP MorphSVG or matched path interpolation |
| Idle loop | Loaders, ambient splash | seamless cyclic transform (rotate/pulse) |
| Wordmark | Logotypes | per-letter mask/translate stagger |
## Quick start: SVG stroke draw-on
The fastest on-brand web reveal. Each path draws itself, then fills settle in.
```html
<svg viewBox="0 0 200 80" id="logo">
<path class="ink" d="M10 60 L40 20 L70 60" fill="none"
stroke="#111" stroke-width="6" stroke-linecap="round"
pathLength="1" stroke-dasharray="1" stroke-dashoffset="1"/>
</svg>
<style>
@keyframes draw { to { stroke-dashoffset: 0; } }
#logo .ink { animation: draw 1.1s cubic-bezier(.65,0,.35,1) forwards; }
@media (prefers-reduced-motion: reduce) {
#logo .ink { animation: none; stroke-dashoffset: 0; }
}
</style>
```
`pathLength="1"` normalizes every path to length 1, so one `stroke-dasharray`/`offset` value works regardless of geometry. Stagger multiple strokes with `animation-delay`.
## Quick start: build-on with stagger (GSAP)
```js
import gsap from "gsap";
// each <g class="piece"> is one mark component
gsap.from(".piece", {
opacity: 0, scale: 0.85, transformOrigin: "50% 50%",
duration: 0.5, ease: "back.out(1.6)", stagger: 0.08,
onComplete: () => gsap.to("#mark", { scale: 1, duration: 0.2, ease: "power2.out" })
});
```
Group order matters: reveal the base/container first, then accents, then the wordmark last so the eye lands on the name.
## Sound-logo sync
When audio exists, the brand's final settle frame must land exactly on the sonic accent (the "ding"/whoosh peak). Decide the audio first, find the peak timecode, then time the reveal so the mark snaps to full opacity/scale on that frame. A 1.0s reveal landing a 1.2s mnemonic feels premium; a settle that floats free of the sound feels cheap. Add a tiny overshoot-and-settle (scale 1.04→1.00 over ~0.15s) on the beat for impact.
## Delivery targets
- **Web, interactive / hero**: inline SVG + CSS or GSAP. Smallest payload, crisp at any size, scriptable.
- **Product / app, designer-made**: Lottie (`.json` exported from After Effects via Bodymovin). Use `lottie-web` or `@lottiefiles/dotlottie-web`; set `loop`, `autoplay`, and a poster fallback frame.
- **Video intro/outro**: Remotion (React) or After Effects render to ProRes/H.264. Pre-render at 2x for crispness.
```js
// Lottie delivery with reduced-motion guard
import { DotLottie } from "@lottiefiles/dotlottie-web";
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const dl = new DotLottie({
canvas: document.querySelector("#logo-canvas"),
src: "/logo.lottie", autoplay: !reduce, loop: false,
});
if (reduce) dl.addEventListener("load", () => dl.setFrame(dl.totalFrames - 1));
```
## Output checklist
- Animated mark in the target format (SVG/Lottie/video).
- Static final-frame fallback (SVG or PNG) for reduced-motion and posters.
- One clear technique, reveal ≤2.5s, brand geometry intact, clearspace respected.
- Sound-logo settle aligned to the audio peak (if audio present).
## 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`.
Logo animation spans web/SVG/Lottie, video, and app splash. **For web/SVG/Lottie delivery use this standalone-HTML loop; for a video logo sting, render via the remotion-video verify loop instead.**
For web/SVG/Lottie, ship **one `.html` file that opens directly in a browser** — inline SVG markup plus the reveal (CSS or one CDN GSAP `<script>`), self-contained. One file is the right tier.
**Output contract:**
- One `.html` file: the inline SVG (or Lottie canvas) + the reveal in inline `<style>`/`<script>`.
- One animation driver — the CSS `@keyframes` draw/build, or a single GSAP timeline; not both.
- Include the freeze harness below, matched to the technique, so any moment can be screenshotted deterministically.
**Freeze harness — pin a frame for screenshots.** Match the mechanism to the technique:
```html
<script>
const t = new URLSearchParams(location.search).get("t");
if (t !== null) {
const T = parseFloat(t);
// CSS stroke-draw / build-on:
document.querySelectorAll("#logo *").forEach(el => {
el.style.animationDelay = (-T) + "s";
el.style.animationPlayState = "paused";
});
// GSAP timeline instead? → tl.pause(); tl.seek(T);
// SMIL <animate>? → const s = document.querySelector("svg"); s.pauseAnimations(); s.setCurrentTime(T);
// Lottie? → dl.setFrame(Math.round(T * fps));
}
window.__ready = true; // ready signal for headless wait
</script>
```
**Verify loop — render → freeze → screenshot → check:**
1. Open the file frozen at start / mid / end of the reveal (≤2.5s): `…/logo.html?t=0`, `?t=<dur/2>`, `?t=<dur>`.
2. Screenshot each frozen frame.
3. Check **fidelity** (matches the mark, one clear technique) and **artifacts** — brand geometry undistorted, clearspace clear of motion debris, no half-drawn strokes left at the end frame, the static final frame is the true logo.
4. **Iterate:** if any check fails (e.g. a half-drawn stroke at `?t=<dur>` or distorted geometry mid-reveal), fix the stroke-dashoffset/duration/easing, re-render the **same** `?t=N`, and re-screenshot to confirm the fix — loop until every frame is clean.
```bash
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/logo.html?t=1.1" frame-mid.png
```
**Before you finish:**
1. Opens standalone in a browser — no console errors, no missing CDN.
2. One technique/driver; `?t=N` freezes the exact frame correctly.
3. Screenshotted at start / mid / end — geometry intact, clearspace respected, final frame is the clean mark.
4. `prefers-reduced-motion` honored (final static mark shown, no motion) + static end-frame fallback shipped.
5. Reveal ≤2.5s; if audio present, the settle lands on the sound-logo peak.
## Reference files
- `references/logo-patterns.md` — runnable code for every technique: stroke draw-on, mask wipe build-on, GSAP MorphSVG morph, seamless idle loop, Lottie delivery, plus timing tables (0.8–2.5s), brand clearspace rules, and reduced-motion fallbacks.
@@ -0,0 +1,199 @@
# Logo Animation Patterns — Cookbook
Self-contained, runnable recipes for every logo technique. Pick ONE per logo.
## Timing guidance
| Beat | Duration | Easing |
|---|---|---|
| Stroke draw-on (per path) | 0.6–1.2s | `cubic-bezier(.65,0,.35,1)` (easeInOutCubic) |
| Mask wipe reveal | 0.5–0.9s | `cubic-bezier(.22,1,.36,1)` (easeOutQuint) |
| Build-on per piece | 0.4–0.6s, stagger 0.06–0.10s | `back.out(1.5)` |
| Morph | 0.6–1.0s | `power2.inOut` |
| Final settle / overshoot | 0.12–0.20s | scale 1.04→1.00, `power2.out` |
| Full reveal envelope | 0.8–2.5s total | — |
| Idle loop cycle | 1.5–4s, infinite | `sine.inOut` / `linear` for spin |
Rule of thumb: longer marks (wordmarks, detailed monograms) sit near 2.0–2.5s; simple icons near 0.8–1.4s. Hold the settled mark at least 0.4s before any sign-off cut.
## Brand clearspace & geometry rules
- **Clearspace**: keep a margin equal to the mark's cap-height (or the brand's defined unit) clear of moving particles, wipes, or overshoot. Motion must not visually crop into the safe margin.
- **No distortion**: scale uniformly, fade, translate, rotate-on-axis, and mask are safe. Avoid non-uniform `scaleX≠scaleY`, skew, and squash on the final mark — they read as off-brand. A squash-and-settle is acceptable ONLY as a sub-0.2s micro-overshoot that returns to 1.0.
- **Color**: never animate brand color away from spec mid-reveal. Reveal in brand color, or grayscale→brand as an intentional, approved beat.
- **Lockup integrity**: animate the symbol and wordmark with a clear hierarchy (symbol leads, wordmark follows) but land both in the exact locked relationship.
## 1. Stroke draw-on (SVG + CSS)
```html
<svg viewBox="0 0 240 80" id="mark">
<path class="ink" pathLength="1" fill="none" stroke="#0A84FF"
stroke-width="7" stroke-linecap="round" stroke-linejoin="round"
d="M12 64 L48 16 L84 64 M120 16 L120 64 M156 16 H204"/>
</svg>
<style>
#mark .ink {
stroke-dasharray: 1; stroke-dashoffset: 1;
animation: draw 1.1s cubic-bezier(.65,0,.35,1) forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce){
#mark .ink { animation: none; stroke-dashoffset: 0; }
}
</style>
```
Draw-then-fill (line settles into a solid mark):
```css
#mark .ink { fill: transparent; }
@keyframes fill { to { fill: #0A84FF; stroke-width: 0; } }
#mark .ink { animation: draw 1.1s cubic-bezier(.65,0,.35,1) forwards,
fill .3s ease .9s forwards; }
```
GSAP equivalent with per-path stagger via DrawSVGPlugin:
```js
import gsap from "gsap";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";
gsap.registerPlugin(DrawSVGPlugin);
gsap.fromTo("#mark .ink",
{ drawSVG: "0%" },
{ drawSVG: "100%", duration: 1.0, ease: "power2.inOut", stagger: 0.15 });
```
## 2. Mask wipe / build-on (clipPath)
Reveal a filled mark by sweeping a clip rect across it.
```html
<svg viewBox="0 0 200 80">
<defs>
<clipPath id="wipe"><rect id="wiper" x="0" y="0" width="0" height="80"/></clipPath>
</defs>
<g clip-path="url(#wipe)">
<!-- the full filled logo art here -->
<path d="M10 10 H190 V70 H10 Z" fill="#111"/>
</g>
</svg>
<style>
#wiper { animation: wipe .8s cubic-bezier(.22,1,.36,1) forwards; }
@keyframes wipe { to { width: 200px; } }
@media (prefers-reduced-motion: reduce){ #wiper{ animation:none; width:200px; } }
</style>
```
Directional variants: animate `y`/`height` for a top-down build, or use a radial `<clipPath>` with a growing `<circle r>` for an iris reveal.
## 3. Build-on with stagger (GSAP timeline)
```js
import gsap from "gsap";
const tl = gsap.timeline({ defaults: { ease: "back.out(1.6)" } });
tl.from("#base", { scale: .8, opacity: 0, transformOrigin: "50% 50%", duration: .5 })
.from(".accent", { scale: .7, opacity: 0, transformOrigin: "50% 50%",
duration: .45, stagger: 0.08 }, "-=0.2")
.from(".word > *", { yPercent: 120, opacity: 0, duration: .4,
stagger: 0.04, ease: "power3.out" }, "-=0.15")
.to("#mark", { scale: 1.04, duration: .12, ease: "power2.out" })
.to("#mark", { scale: 1.0, duration: .14, ease: "power2.inOut" });
```
## 4. Morph (GSAP MorphSVG)
Morph requires two paths; MorphSVG handles differing point counts.
```js
import gsap from "gsap";
import { MorphSVGPlugin } from "gsap/MorphSVGPlugin";
gsap.registerPlugin(MorphSVGPlugin);
// #shape: a simple start shape (e.g. circle path) -> #logoPath: the brand mark
gsap.to("#shape", {
duration: .9, ease: "power2.inOut",
morphSVG: { shape: "#logoPath", shapeIndex: "auto" }
});
```
No-plugin fallback (Flubber for interpolation + requestAnimationFrame):
```js
import { interpolate } from "flubber";
const from = "M10,10 ...", to = "M0,0 ...";
const lerp = interpolate(from, to, { maxSegmentLength: 2 });
const path = document.querySelector("#shape");
const dur = 900, t0 = performance.now();
(function frame(now){
const k = Math.min(1, (now - t0) / dur);
const e = k < .5 ? 2*k*k : 1 - Math.pow(-2*k+2, 2)/2; // easeInOutQuad
path.setAttribute("d", lerp(e));
if (k < 1) requestAnimationFrame(frame);
})(t0);
```
## 5. Seamless idle loop (loader)
Loop must have identical start and end frames — design the cycle so frame 0 == frame N.
```css
#loader { animation: spin 1.4s linear infinite, pulse 2.8s ease-in-out infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { 0%,100%{ opacity:.85 } 50%{ opacity:1 } }
@media (prefers-reduced-motion: reduce){
#loader { animation: pulse 2.8s ease-in-out infinite; } /* drop rotation */
}
```
GSAP `yoyo` loop for a breathing mark:
```js
gsap.to("#mark", { scale: 1.03, duration: 1.4, ease: "sine.inOut",
yoyo: true, repeat: -1 });
```
## 6. Lottie delivery (designer-made)
Export from After Effects via Bodymovin to `.json`, or compressed `.lottie`.
```html
<canvas id="logo-canvas" width="512" height="512"></canvas>
<script type="module">
import { DotLottie } from "https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web/+esm";
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const dl = new DotLottie({
canvas: document.querySelector("#logo-canvas"),
src: "/logo.lottie", autoplay: !reduce, loop: false, speed: 1,
});
// Reduced motion: jump to the final settled frame, no playback.
if (reduce) dl.addEventListener("load", () => dl.setFrame(dl.totalFrames - 1));
// Sound-logo sync: fire audio when the settle frame is reached.
dl.addEventListener("frame", ({ currentFrame }) => {
if (currentFrame >= SETTLE_FRAME && !audioFired) { audio.play(); audioFired = true; }
});
</script>
```
Classic `lottie-web` (SVG renderer, sharpest for vector logos):
```js
import lottie from "lottie-web";
const anim = lottie.loadAnimation({
container: document.getElementById("logo"),
renderer: "svg", loop: false, autoplay: !reduce, path: "/logo.json",
});
if (reduce) anim.goToAndStop(anim.totalFrames - 1, true);
```
Lottie tips: keep layers/effects Bodymovin-supported (no native blurs/expressions unless baked), name layers cleanly, and request a poster PNG of the final frame from the designer for the static fallback.
## 7. Reduced-motion fallback (always ship)
Every delivery must degrade to the static, fully-revealed mark:
- **CSS/SVG**: `@media (prefers-reduced-motion: reduce)` sets the final state (`stroke-dashoffset:0`, `width:full`, `opacity:1`) with `animation:none`.
- **Lottie**: `goToAndStop(totalFrames-1)` / `setFrame(last)`.
- **Video**: serve a poster image of the last frame; only autoplay motion when motion is allowed.
- **JS gate**: branch on `matchMedia("(prefers-reduced-motion: reduce)").matches` before building any timeline.
---
Match the reveal to the delivery target and a logo lands across web, video, and app. Built by **[iart.ai](https://iart.ai/?utm_source=github&utm_medium=readme&utm_campaign=motion-design-skills&utm_content=skill_footer&utm_term=logo-animation)** — the AI motion agent for editable, on-brand motion graphics.