Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill should be used when the user wants to create, fix, or convert SVG assets in any project — banners, badges, icons, logos, window/IDE mockups, terminal screenshots, or diagram nodes. Common triggers include "create an SVG", "make a banner", "design an icon", "draw a logo", "fix the viewBox", "convert SVG to PNG", and "preview this SVG". Bakes in edge-to-edge viewBox discipline (`viewBox="0 0 W H"` — no padding, no offsets), hardcoded hex colors (SVG cannot read CSS variables), a Node- b
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 95% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 93% | 0% |
End-to-end procedure for creating SVG assets that render correctly across every consumer (GitHub README, slide deck, web app, mobile email, social card). Each section explains the why so you can adapt — don't follow them mechanically.
svg<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 W H" width="W" height="H">
The viewBox must start with 0 0 and use the actual content dimensions. No offsets like viewBox="120 40 660 420". No padding.
Why: padding inside an asset can't be removed downstream. It bakes empty pixels into every usage, fights object-fit: contain, breaks alignment in any consumer's layout, and makes the asset unreusable at different sizes. Layout spacing belongs in the layout (CSS, README, slide template), not in the asset.
What this means in practice: if you want a "window inside a workspace" look, the workspace is the asset — make the workspace itself the bounding box. Don't simulate "the window inside a frame" by offsetting the window inside a larger viewBox.
bashnode scripts/preflight.mjs
Checks Node ≥ 20, that sharp is resolvable from cwd, and whether the chrome-devtools MCP is configured. Prints fix commands and exits 1 if any required dep is missing. Add --install (with optional --yes for non-interactive use) to install missing deps via the repo's package manager (auto-detects pnpm / yarn / bun / npm).
The chrome-devtools MCP is optional but recommended — it lets the agent take screenshots of the preview to verify the asset before declaring it done. Without it, fall back to opening the preview URL in a real browser.
Three decisions before you write any SVG:
not the consumer's display size. Sensible defaults:
| Use case | Recommended | | --------------------------- | -------------------- | | GitHub repo banner (5:1) | 1280×256 | | Square badge / icon | 96×96 or 128×128 | | Window / IDE / CLI mockup | 660×420 | | Wide hero strip (16:5) | 1920×400 | | Open Graph / social card | 1200×630 | | Favicon source (downscaled) | 512×512 |
(banner-active.svg, not BannerActive.svg). Match the consuming repo's convention.
already follows. Common: assets/, public/, art/, docs/img/.
Apply the viewBox rule above. Three craft notes:
Colors — SVGs cannot read CSS variables. Hardcode hex values. references/colors.md is a starter palette; override per project.
Typography — pick fonts upfront. system-ui, sans-serif is safest for cross-machine consistency. For pixel-perfect text across machines, embed a webfont via <style>@font-face{...}</style> with a base64-encoded payload inside the SVG.
Recipes — references/patterns.md has copy- pasteable patterns for repo banners, badges, window chrome, terminals, diagram nodes, arrows, and icons — including viewBox math and common anti-patterns.
bashnode scripts/lint-viewbox.mjs path/to/asset.svg # or recursively over a directory: node scripts/lint-viewbox.mjs assets/
Checks: viewBox exists, is well-formed, starts with 0 0, has positive width/height. Exits 1 with a fix hint on failure. Edit and re-run until clean.
bashnode scripts/preview-server.mjs path/to/asset.svg
Picks a free port from a small pool of uncommon defaults (5879, 6321, 7843, 8765, 9876, 31415, 42420, 47821, 51234, 61234), falls back to OS-assigned if all bound. Serves an HTML page that renders the SVG via <img src="/svg"> — the same path real consumers use, so what you see is what you'll ship. Endpoints: /, /svg, /health. Run in background so the agent can keep working.
If you have the chrome-devtools MCP, don't write-and-stop: navigate to the printed URL, take a screenshot, and inspect. Catching an off-by-one viewBox via screenshot is cheap; fixing it after the user notices is not.
1. node scripts/preview-server.mjs path/to/asset.svg (background)
2. chrome-devtools MCP: navigate_page → http://localhost:<port>/
3. chrome-devtools MCP: take_screenshot
4. inspect — does it look right? clipping? wrong colors? phantom whitespace?If you don't have chrome-devtools MCP, open the URL in a real browser instead. Either way, verify visually before declaring the asset done.
Most consumers eventually need a PNG fallback (READMEs, slide decks, social cards). Don't auto-convert — ask the user whether they want a raster sibling, in which format, and at what dimensions.
bashnode scripts/convert.mjs path/to/asset.svg node scripts/convert.mjs path/to/asset.svg --format webp --width 800 node scripts/convert.mjs path/to/asset.svg --format jpeg --quality 85 --out preview.jpg
Defaults: PNG, sibling path, density 144 DPI for font crispness, native SVG dimensions. Supports png, jpeg, webp, avif.
Tell the user:
<path>/<name>.svg)If anything in steps 4–6 failed, surface it explicitly — don't claim success when only the SVG was written.
<example> <input> "make me a github readme banner for my project 'TaskForge' — 1280x256, dark theme with purple gradient" </input> <output>
banner-taskforge.svg, target assets/.viewBox="0 0 1280 256", full-bleed gradient <rect width="1280" height="256" fill="url(#bg)">, hardcoded hex stops #7268F0 → #B667EF, hardcoded font Geist, system-ui, sans-serif. (See references/patterns.md → "GitHub repo banner".)node scripts/lint-viewbox.mjs assets/banner-taskforge.svg → ✓ pass.node scripts/preview-server.mjs assets/banner-taskforge.svg & → http://localhost:5879/navigate_page then take_screenshot — confirm gradient covers full bounds, text doesn't clip.node scripts/convert.mjs assets/banner-taskforge.svg.assets/banner-taskforge.svg, preview at http://localhost:5879/, 1280×256."</output> </example>
<example> <good>
svg<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 420" width="660" height="420"> <rect width="660" height="420" rx="10" fill="#1A1A1A"/> <!-- content positioned from origin --> </svg>
</good>
<bad>
svg<svg xmlns="http://www.w3.org/2000/svg" viewBox="120 40 660 420" width="660" height="420"> <rect x="120" y="40" width="660" height="420" rx="10" fill="#1A1A1A"/> <!-- content offset by 120/40 — bakes phantom padding into the asset --> </svg>
</bad>
The <bad> example fails the lint and renders with phantom whitespace when consumed via <img> or object-fit: contain. Fix: shift every child's coordinate by the offset and reset the viewBox to 0 0 660 420. </example>
references/colors.md — starter palette and typography rulesreferences/patterns.md — copy-pasteable SVG recipes (banners, badges, windows, terminals, icons, arrows)SKILL.md — this filescripts/preflight.mjs — runtime dependency checkscripts/preview-server.mjs — local Node HTTP preview serverscripts/convert.mjs — sharp-based SVG → PNG / JPEG / WebP / AVIF converterscripts/lint-viewbox.mjs — viewBox lint (enforces 0 0 W H)references/colors.md — color and typography starter palettereferences/patterns.md — copy-pasteable SVG recipesOther measured skills in the registry, with their headline benchmark lift.