{"id":"1256c174-d303-4730-978a-3cec703e8e63","shortId":"bVwR6f","kind":"skill","title":"graphic-gif","tagline":"Creates animated looping GIFs from CSS animations (default) or AI image-to-video. 800×800px default, 6 animation types, 4 style presets. Trigger when user says \"create an animated gif\", \"make a looping gif\", \"animated banner\", \"CSS animation gif\", \"social media animation\", \"make ","description":"# graphic-gif\n\nGenerates an animated looping GIF from CSS animations or an AI image-to-video API. Output: `animation.gif`.\n\nUnlike every other `graphic-` skill that outputs a static PNG or PDF, this skill outputs an animated `.gif`. Uses CSS `@keyframes` animations captured frame-by-frame via Playwright and the Web Animations API (Option A, default), or an AI image-to-video pipeline via Kling (Option B).\n\n---\n\n## Critical Rules (read before every generation)\n\n1. **Default is css-animated.** Never use `ai-generated` unless explicitly requested.\n2. **Canvas is 800×800px square.** All `clamp()` values computed at 800px (1vw = 8px).\n3. **Single self-contained HTML.** All CSS inline in `<style>`. Font CDN `<link>` only external dependency.\n4. **Never dump HTML in chat.** Save to file, show summary only.\n5. **Frame capture uses Web Animations API seeking.** NOT `setTimeout` loops, NOT `animation-delay` tricks.\n6. **Exact frame count:** `Math.floor(duration_seconds * fps)` frames. The frame at `t=duration_ms` MUST NOT be captured — it duplicates `t=0` and causes a visible stutter at the loop point.\n7. **No placeholder boxes.** CSS-generated visuals only. No \"image goes here\" elements.\n8. **Simpler palettes = smaller files.** Use: `clean-slate`, `terminal`, `electric-burst`, `brutalist`.\n9. **No animation-delay for stagger.** Bake stagger into `@keyframes` percentages — frame seeking handles timing.\n10. **Commit to design direction before writing CSS.** Tone, signature element, motion style, unforgettable detail — all decided before first line of code.\n\n---\n\n## Step 1: Intake\n\n**Required:** `prompt` (content description AND motion brief)\n\n**Optional with defaults:**\n\n| Parameter | Default | Options |\n|---|---|---|\n| animation_type | css-animated | css-animated / ai-generated |\n| duration | 3.0 | seconds |\n| fps | 12 | frames per second |\n| loop | true | true / false |\n| style | clean-slate | clean-slate / terminal / electric-burst / brutalist |\n| dimensions | 800x800 | WxH in pixels |\n| optimization | balanced | quality / balanced / filesize |\n\n**If prompt is missing or lacks motion description, ask exactly:**\n\n> \"What should the GIF show? Describe the content AND the motion (e.g., 'Stats count up: 73% of buyers read 3+ pieces of content before purchase. Typewriter effect, one character at a time. Style: terminal. 3 seconds, 12fps.')\n>\n> Key settings (all optional, defaults shown):\n> - animation_type: css-animated (default) or ai-generated\n> - duration: 3.0 seconds\n> - fps: 12\n> - loop: true\n> - style: clean-slate (options: clean-slate / terminal / electric-burst / brutalist)\n> - dimensions: 800x800\n> - optimization: balanced (options: quality / balanced / filesize)\"\n\nIf all required info is present → skip directly to Step 2.\n\n---\n\n## Step 2: Internal Architecture (never shown to user)\n\n**For css-animated:**\n\n1. Choose animation type from: `fade-in`, `slide-in`, `typewriter`, `counter`, `pulse`, `loop-scroll`\n2. Read `references/animation-library.md` — find the chosen type's full HTML/CSS spec\n3. Read `references/style-presets.md` — load the chosen style's CSS token block\n4. Calculate frame count: `Math.floor(duration_seconds * fps)` — write this number down\n5. Commit to design direction:\n\n| Decision | Derive from |\n|---|---|\n| Tone | Emotional register for audience (mechanical / warm / electric / professional) |\n| Signature element | ONE visual device used consistently (cursor blink, ghost number, scan-line overlay, accent border) |\n| Motion style | Ease curve philosophy for this type (spring / linear / step / ease-in-out) |\n| Unforgettable detail | The ONE thing a viewer will remember about this GIF |\n\n**For ai-generated:**\n1. Generate base still frame HTML (poster-style layout for the canvas)\n2. Export as PNG using screenshot\n3. Call Kling API: `POST https://api.klingai.com/v1/videos/image2video` with `image_url` and `prompt` describing the motion\n4. Poll for job completion\n5. Download video → convert to GIF with ffmpeg:\n   ```bash\n   # Two-pass palette for best color quality\n   ffmpeg -i input.mp4 -vf \"fps=12,scale=800:800:flags=lanczos,palettegen=stats_mode=diff\" palette.png\n   ffmpeg -i input.mp4 -i palette.png -vf \"fps=12,scale=800:800:flags=lanczos,paletteuse=dither=bayer:bayer_scale=5\" output.gif\n   ```\n\n---\n\n## Step 3: HTML Generation (css-animated path)\n\nRead `references/animation-library.md` and `references/style-presets.md` before generating.\n\n**Canvas base — required on every GIF:**\n```css\n*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\nbody {\n  width: 800px;\n  height: 800px;\n  overflow: hidden;\n  background: var(--bg);\n  font-family: var(--font-body);\n}\n\n.canvas {\n  width: 800px;\n  height: 800px;\n  position: relative;\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n```\n\n**Animation rules:**\n- `animation-fill-mode: forwards` (or `both`) on ALL animated elements\n- Timing functions per type:\n  - `typewriter` → `steps(N, end)` where N = exact character count\n  - `counter` → `linear`\n  - `fade-in` → `cubic-bezier(0.22, 1, 0.36, 1)` (ease-out)\n  - `slide-in` → `cubic-bezier(0.34, 1.56, 0.64, 1)` (spring overshoot)\n  - `pulse` → `ease-in-out` with `animation-iteration-count: infinite`\n  - `loop-scroll` → `linear` with `animation-iteration-count: infinite`\n- For one-shot animations (fade-in, slide-in, typewriter, counter): `animation-iteration-count: 1` — looping happens at GIF level\n- **No `animation-delay`** — stagger is baked into `@keyframes` percentages\n\n**Typewriter N calculation:**\nCount every character including spaces, punctuation, numbers:\n- \"73% of buyers\" = 14 characters → `steps(14, end)`\n- \"Hello, World!\" = 13 characters → `steps(13, end)`\n\n**Counter CSS `@property` (required for counter type):**\n```css\n@property --num {\n  syntax: '<integer>';\n  inherits: false;\n  initial-value: 0;\n}\n.counter {\n  animation: countUp var(--duration) linear forwards;\n  counter-reset: num var(--num);\n}\n.counter::after { content: counter(num); }\n@keyframes countUp {\n  from { --num: 0; }\n  to   { --num: var(--target); }\n}\n```\n\n**Loop-scroll: content MUST be duplicated:**\n```html\n<!-- 4 original items + 4 duplicate items -->\n<div class=\"ticker-track\" style=\"--item-count: 4;\">\n  [item1][item2][item3][item4][item1][item2][item3][item4]\n</div>\n```\n`translateX(0 → -50%)` with `linear infinite`.\n\n**Design quality rules (from commit in Step 2):**\n- Named signature element MUST be present in CSS/HTML (not just described)\n- Typography: weight contrast minimum 2:1 (e.g., 700 vs 400) between display and supporting text\n- Background: no pure white `#fff` for dark styles — use the preset's exact `--bg` value\n- For terminal style: add scan-line overlay `::after` with `repeating-linear-gradient` at `opacity: 0.03`\n- For brutalist: thick border `4px solid #000` or `4px solid var(--accent)` on key element\n- Unforgettable detail: if it requires an extra element — add it now\n\n---\n\n## Step 4: Self-QA (fix every failure before Step 5)\n\n**Canvas:**\n- [ ] `body` and `.canvas` exactly 800×800px (or specified dimensions)\n- [ ] `overflow: hidden` on both `body` and `.canvas`\n- [ ] No elements overflowing the canvas boundary\n\n**Animations:**\n- [ ] NO `animation-delay` anywhere — stagger is in `@keyframes` percentages\n- [ ] All animations start at `t=0` (Web Animations API will seek from there)\n- [ ] `animation-fill-mode: forwards` or `both` on all animated elements\n- [ ] One-shot animations: `animation-iteration-count: 1`\n- [ ] Infinite animations (pulse, loop-scroll): `animation-iteration-count: infinite`\n\n**Type-specific checks:**\n- [ ] Typewriter: N in `steps(N, end)` = exact character count of text string\n- [ ] Counter: `@property --num` declared with `syntax: '<integer>'` and `initial-value: 0`\n- [ ] Counter: `counter-reset: num var(--num)` and `::after { content: counter(num) }`\n- [ ] Loop-scroll: content duplicated exactly once in HTML\n\n**Design:**\n- [ ] No placeholder boxes\n- [ ] Style preset tokens applied from `references/style-presets.md` — no free-floating hex colors\n- [ ] Signature element named in Step 2 is actually present in the HTML/CSS\n- [ ] Unforgettable detail from Step 2 is actually implemented\n- [ ] Font CDN `<link>` present for chosen style's font\n\n---\n\n## Step 5: Export\n\n**Determine slug from prompt** (kebab-case, ≤30 chars). Create output directory:\n```bash\nmkdir -p [slug]\n```\n\nSave HTML:\n```\n[slug]/animation.html\n```\n\nOpen in browser for quick visual check:\n```bash\nopen [slug]/animation.html\n```\n\nRun export script (replace `[skill-root]` with the actual path to this skill):\n```bash\nbash [skill-root]/scripts/export-gif.sh \\\n  [slug]/animation.html \\\n  [slug]/animation.gif \\\n  --duration [duration] \\\n  --fps [fps] \\\n  [--no-loop if loop=false] \\\n  --optimization [optimization] \\\n  --width [W] \\\n  --height [H]\n```\n\nThe script:\n1. Installs `gifenc`, `sharp` (or `jimp`), and `playwright` in a temp directory\n2. Downloads Chromium if not cached\n3. Runs `capture-and-encode.mjs` — pauses animations, seeks each frame, screenshots, assembles GIF\n4. Runs `gifsicle` optimization pass if available\n5. Reports file size and opens result\n\n**If export script not found** at `[skill-root]/scripts/export-gif.sh`, check that the skill was installed with its `scripts/` folder intact.\n\n---\n\n## Step 6: Output Summary\n\nShow after successful export:\n\n```\n## GIF: [1-line description]\nDate: [YYYY-MM-DD] | Style: [style] | Animation: [type] | [duration]s @ [fps]fps\nDimensions: [WxH] | Frames: [N] | Loop: [true/false]\n\nFiles\n  Source:   [slug]/animation.html\n  Output:   [slug]/animation.gif\n  Size:     [X] KB\n\nChecklist\n- [ ] Preview loops cleanly at start/end point (no stutter)\n- [ ] Text legible at intended display size\n- [ ] File size appropriate: email <500KB / social <3MB\n```\n\n---\n\n## AI-Generated Path (Option B)\n\nOnly use when `animation_type: ai-generated` is explicitly specified.\n\n**Requirements:**\n- Kling API key in environment: `KLING_API_KEY` (66 free credits/day, no credit card for free tier)\n- `ffmpeg` installed locally for video→GIF conversion\n\n**Workflow:**\n1. Generate a base still frame HTML matching the prompt (poster/graphic style at specified dimensions)\n2. Export still frame as PNG:\n   ```bash\n   # Quick screenshot via Playwright\n   node -e \"\n     const { chromium } = require('playwright');\n     (async () => {\n       const browser = await chromium.launch();\n       const page = await browser.newPage({ viewport: { width: W, height: H } });\n       await page.goto('file://[slug]/animation.html');\n       await page.screenshot({ path: '[slug]/base-frame.png' });\n       await browser.close();\n     })();\n   \"\n   ```\n3. Upload to Kling image-to-video endpoint\n4. Convert result to GIF with ffmpeg two-pass palette\n5. Apply gifsicle optimization\n\n**When Kling is unavailable:** Fall back to css-animated with a note to the user: \"AI generation requires a Kling API key (KLING_API_KEY). Falling back to css-animated. Set the key to enable AI generation.\"\n\n---\n\n## Prompt Tips (show when user asks for guidance)\n\n> \"Describe motion, not just content. 'Stats count up one by one' beats 'show stats'.\"\n>\n> \"Keep it simple for file size. 1–3 animated elements and a solid background.\"\n>\n> \"Think in loops. The animation should flow invisibly from end back to start.\"\n>\n> \"Specify the animation type explicitly. `typewriter` and `counter` are the most effective for social.\"\n>\n> ✅ Good: \"Create an animated GIF, css-animated, typewriter effect. Text: '73% of B2B buyers read 3+ pieces of content before contacting sales.' Each character types out one at a time. Style: terminal. 3 seconds, 12fps, loop=true.\"\n>\n> ❌ Bad: \"make an animated gif of marketing tips\"","tags":["graphic","gif","opendirectory","varnan-tech","agent-skills","gtm","hermes-agent","marketing-skills","openclaw-skills","skill-pack","skills","technical-seo"],"capabilities":["skill","source-varnan-tech","skill-graphic-gif","topic-agent-skills","topic-gtm","topic-hermes-agent","topic-marketing-skills","topic-openclaw-skills","topic-skill-pack","topic-skills","topic-technical-seo"],"categories":["opendirectory"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/Varnan-Tech/opendirectory/graphic-gif","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add Varnan-Tech/opendirectory","source_repo":"https://github.com/Varnan-Tech/opendirectory","install_from":"skills.sh"}},"qualityScore":"0.593","qualityRationale":"deterministic score 0.59 from registry signals: · indexed on github topic:agent-skills · 286 github stars · SKILL.md body (11,694 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:54:41.419Z","embedding":null,"createdAt":"2026-05-02T18:54:59.779Z","updatedAt":"2026-05-18T18:54:41.419Z","lastSeenAt":"2026-05-18T18:54:41.419Z","tsv":"'1':124 '1vw':150 '2':138 '3':152 '4':24 '6':21 '800':18,141 '800px':19,142,149 '8px':151 'ai':13,61,108,133 'ai-gener':132 'anim':5,10,22,33,39,42,46,53,58,85,90,101,129 'animation.gif':68 'api':66,102 'b':117 'banner':40 'canva':139 'captur':91 'clamp':145 'comput':147 'contain':156 'creat':4,31 'critic':118 'css':9,41,57,88,128,159 'css-anim':127 'default':11,20,105,125 'everi':70,122 'explicit':136 'frame':93,95 'frame-by-fram':92 'generat':51,123,134 'gif':3,7,34,38,43,50,55,86 'graphic':2,49,72 'graphic-gif':1,48 'html':157 'imag':15,63,110 'image-to-video':14,62,109 'inlin':160 'keyfram':89 'kling':115 'loop':6,37,54 'make':35,47 'media':45 'never':130 'option':103,116 'output':67,75,83 'pdf':80 'pipelin':113 'playwright':97 'png':78 'preset':26 'read':120 'request':137 'rule':119 'say':30 'self':155 'self-contain':154 'singl':153 'skill':73,82 'skill-graphic-gif' 'social':44 'source-varnan-tech' 'squar':143 'static':77 'style':25 'topic-agent-skills' 'topic-gtm' 'topic-hermes-agent' 'topic-marketing-skills' 'topic-openclaw-skills' 'topic-skill-pack' 'topic-skills' 'topic-technical-seo' 'trigger':27 'type':23 'unless':135 'unlik':69 'use':87,131 'user':29 'valu':146 'via':96,114 'video':17,65,112 'web':100","prices":[{"id":"6cd83703-278f-4280-b18d-73089848a37b","listingId":"1256c174-d303-4730-978a-3cec703e8e63","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"Varnan-Tech","category":"opendirectory","install_from":"skills.sh"},"createdAt":"2026-05-02T18:54:59.779Z"}],"sources":[{"listingId":"1256c174-d303-4730-978a-3cec703e8e63","source":"github","sourceId":"Varnan-Tech/opendirectory/graphic-gif","sourceUrl":"https://github.com/Varnan-Tech/opendirectory/tree/main/skills/graphic-gif","isPrimary":false,"firstSeenAt":"2026-05-02T18:54:59.779Z","lastSeenAt":"2026-05-18T18:54:41.419Z"}],"details":{"listingId":"1256c174-d303-4730-978a-3cec703e8e63","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"Varnan-Tech","slug":"graphic-gif","github":{"repo":"Varnan-Tech/opendirectory","stars":286,"topics":["agent-skills","gtm","hermes-agent","marketing-skills","openclaw-skills","skill-pack","skills","technical-seo"],"license":"mit","html_url":"https://github.com/Varnan-Tech/opendirectory","pushed_at":"2026-05-18T18:27:10Z","description":" AI Agent Skills built for Founders who hate Marketing","skill_md_sha":"7c362856daf79dc223912ddd5cbd213ebf3668d2","skill_md_path":"skills/graphic-gif/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/Varnan-Tech/opendirectory/tree/main/skills/graphic-gif"},"layout":"multi","source":"github","category":"opendirectory","frontmatter":{"name":"graphic-gif","description":"Creates animated looping GIFs from CSS animations (default) or AI image-to-video. 800×800px default, 6 animation types, 4 style presets. Trigger when user says \"create an animated gif\", \"make a looping gif\", \"animated banner\", \"CSS animation gif\", \"social media animation\", \"make this loop\", \"animated graphic\", or \"motion graphic\".","compatibility":"[claude-code, gemini-cli, github-copilot]"},"skills_sh_url":"https://skills.sh/Varnan-Tech/opendirectory/graphic-gif"},"updatedAt":"2026-05-18T18:54:41.419Z"}}