@gent8/brandkit
v0.3.1
Published
One SVG → 14 ship-ready brand assets (favicon, og-image, PWA icons, Chrome Web Store) — all palette-locked to brand.json. CLI + MCP server with a CI gate that exits 1 on any off-palette color.
Maintainers
Readme
One source SVG → every asset you need to ship a brand. brandkit is a CLI + MCP server that turns a single SVG into 14 standard files — favicon, apple-touch-icon, maskable PWA icon, og-image, Chrome Web Store assets — all palette-locked to your brand.json. The verify command is a CI gate: it exits 1 on any off-palette color, so a drift can't sneak into the bundle the same way ESLint catches a typo.
$ brandkit export dist/logo.svg --brand brand.json --out ./assets
✓ wrote 14 files to ./assets (icon-16.png … cws-marquee-920x680.png)
✓ all colors in palette (5)
$ brandkit verify ./assets/og-image.png # works on any output
✓ all colors in paletteUse export to ship the bundle. Use verify as a CI gate. Use recolor as a pure SVG-in/SVG-out transform. Use gen for the full prompt → palette-locked SVG pipeline. Every tool is also exposed over MCP, so any Claude / Anthropic-SDK / MCP-compatible agent can call them inline.
What's actually different
1. One command, every surface. Other dev tools cover one slice (pwa-asset-generator does favicons, RealFaviconGenerator does favicons, AI logo SaaS gives you a brand kit but no CI hook). Nothing else gives you the full bundle from one SVG, palette-locked, scriptable:
| Tool | Steps to ship-ready palette-locked SVG + asset bundle | Steps |
|---|---|---|
| DALL-E 3 / Nano Banana 2 / Midjourney v7 | gen raster · vectorize · recolor · verify · render every asset size | 5 |
| Recraft v3 (vector) | gen vector (off-palette) · recolor · verify · render every asset size | 4 |
| Raw Ideogram v3 | gen raster · vectorize · recolor · verify · render every asset size | 5 |
| RealFaviconGenerator / pwa-asset-generator | favicons only · no og-image · no palette enforcement · still need verify | 3+ |
| Looka / Brandmark / LogoAI | rich brand kit but SaaS-locked · no CLI · no verify | n/a |
| brandkit | brandkit gen then brandkit export (or one MCP call for the gen part) | 1–2 |
brandkit export writes 14 files from one source SVG: icons (16/32/48/128), favicon (svg + multi-res ico), apple-touch-icon, android-chrome (192/512), maskable-512, og-image (1200×630), and the three Chrome Web Store assets. All palette-locked, no manual rasterization step.
2. The bundle is verified, not just generated. Same source raster, fed through "vectorize only" vs "vectorize + recolor + verify":
| Pipeline | verify exit | Off-palette colors |
|---|---|---|
| Vectorize-only (Recraft on the raster) | 1 | 48 |
| brandkit (vectorize + recolor + verify) | 0 | 0 ✓ |
The drift is 3–15 RGB points per color — invisible to the eye, but accumulates across favicon, og-image, print, merch, and breaks brand QA downstream. verify catches it at build time. Numbers from brandkit verify against the checked-in fixtures. See src/palette.js.
How the pipeline works
- Generate — provider call with palette as a strong hint (Ideogram v3 via fal.ai by default).
- Vectorize — raster → SVG (Recraft API by default).
- Recolor — every
#hexandrgb()in the SVG snapped to the nearest palette member. - Verify — drop any candidate that still has off-palette colors. Exit
1on drift. - Trim — vectorizers pad the mark inside a much larger square; trim rewrites the root viewBox to the rendered geometry bbox so favicons, og-images, and print all fill their canvas.
Install
CLI (npm)
npm install -g @gent8/brandkit
brandkit --helpOr run from source:
git clone https://github.com/gent8/brandkit
cd brandkit
npm install
npm link
brandkit --helpMCP server (Claude Code, Claude Desktop, etc.)
The MCP server lives in mcp/. Build the Docker image and add it to your MCP client config:
docker build -t brandkit-mcp:latest -f mcp/Dockerfile .Claude Code config:
{
"mcpServers": {
"brandkit": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "FAL_KEY",
"-e", "RECRAFT_API_KEY",
"brandkit-mcp:latest"
]
}
}
}brand.json
{
"name": "Acme Co",
"palette": ["#FAFAF9", "#5B21B6", "#10B981", "#F43F5E", "#1C1917"],
"paletteTokens": {
"bg": "#FAFAF9",
"primary": "#5B21B6",
"accent": "#10B981",
"warn": "#F43F5E",
"ink": "#1C1917"
},
"negativePrompt": ["leaves", "plants", "sparkles"]
}The palette array is the law. Anything outside it gets snapped to the nearest member.
CLI commands
| Command | What it does | Side effects |
|---|---|---|
| recolor | Snap every color in an SVG to the nearest palette member | SVG-in / SVG-out |
| verify | Exit 1 on any off-palette color — use as CI gate | None (read-only) |
| trim | Tighten root viewBox so the mark fills its canvas | SVG-in / SVG-out |
| export | One SVG → 14 ship-ready assets (icons, favicons, og-image, CWS) | Writes files |
| gen | Full pipeline: prompt → palette-locked SVG candidates | API calls + files |
brandkit recolor <input.svg> [--brand brand.json] [-o out.svg]
Snap every hex in the SVG to the nearest palette color. 3-digit hex auto-expanded. Errors on hsl() or named colors with a list of offenders — convert those upstream first. Idempotent.
brandkit verify <input.svg> [--brand brand.json]
Exit 0 if every color is in the palette, 1 with a list of offenders otherwise. Use as a precommit / CI gate.
brandkit trim <input.svg> [-o out.svg] [--pad <pct>] [--strip-bg]
Tighten the root viewBox to the rendered geometry bbox so the mark fills its canvas at every output size. Rewrites preserveAspectRatio to xMidYMid meet and drops fixed width/height so consumers can size via container. Pure SVG-in / SVG-out, idempotent. --strip-bg also removes shapes that fill the entire viewBox (typically vectorizer-added background rects).
Why this exists: vectorizers (Recraft, vectorizer.ai) emit marks padded inside a much larger square — easily 40–60% empty. Run on a vectorize output before brandkit export and every favicon, og-image, and print render fills its canvas instead of inheriting the padding. Wired into brandkit gen automatically; standalone for SVGs that came from elsewhere.
brandkit export <input.svg> [--brand brand.json] [--out ./assets] [--bg <hex>]
From one source SVG, produces:
icon-{16,32,48,128}.png— Chrome extensionfavicon.svg,favicon.ico(multi-res 16+32+48),apple-touch-icon.png(180×180)android-chrome-{192,512}.png,maskable-512.pngog-image.png(1200×630, mark centered onpalette[0]or--bg)cws-icon-128.png,cws-tile-440x280.png,cws-marquee-920x680.png
Rasterization via @resvg/resvg-js. ICO via png-to-ico.
brandkit gen --prompt "..." [--brand brand.json] [--out ./assets] [--count 4] [--dry-run]
Full pipeline:
- Provider call (fal.ai Ideogram v3) with prompt + locked palette + negative-prompt list.
- Download N rasters →
./assets/_raw/. - Vectorize each (Recraft) →
./assets/_svg/. - Recolor each to snap to palette.
- Verify each; drop any that still fail.
- Write
candidates.htmlgallery for human selection. - After picking, run
brandkit export <chosen.svg>.
--dry-run prints the requests it would make without sending them.
Providers
Wired:
| Var | Used by |
|---|---|
| FAL_KEY | gen --provider fal (default) |
| RECRAFT_API_KEY | gen --vectorize recraft (default) |
Stubbed (throw new Error("not implemented")) — PRs welcome:
--provider replicate— needsREPLICATE_API_TOKEN--vectorize vectorizer— needsVECTORIZER_AI_API_ID/VECTORIZER_AI_API_SECRET
If a key is missing for the chosen provider, brandkit exits 1 with a clear set $VAR message.
MCP tools
When run as an MCP server, three tools are exposed:
brandkit_gen(prompt, palette, count?, extraNegative?, dryRun?)— full pipeline in one call. Returns survivor SVGs inline as text. No filesystem handoff needed.brandkit_recolor(svg, palette)— pure text-in/text-out. Snaps every hex ANDrgb()/rgba()color to the nearest palette member.brandkit_verify(svg, palette)— pure text-in/JSON-out gate; reports off-palette offenders with normalized hex + suggestion.brandkit_trim(svg, padPct?, stripBackground?, keepDimensions?)— pure text-in/text-out. Rewrites the root viewBox to the rendered geometry bbox so the mark fills its canvas. SetstripBackground=trueto drop vectorizer-added canvas-fill rects.
recolor/verify accept both #hex and rgb() color forms (Recraft's vectorize output uses rgb()). Named CSS colors and hsl() are still rejected.
export is intentionally CLI-only — it writes 14+ files to disk, which doesn't fit the MCP text-in/text-out model.
Example session
cp example/brand.json ./brand.json
$EDITOR brand.json # set name + palette
brandkit gen --prompt "minimalist wordmark for a moving-storage app, geometric, clean" --count 6
open ./assets/candidates.html
# pick candidate-3.svg
brandkit export ./assets/_svg/candidate-3.svg --out ./assets/final
ls ./assets/final
# → icon-16.png ... cws-marquee-920x680.pngCI gate
brandkit verify dist/logo.svg --brand brand.json # exit 1 on driftStatus
[!NOTE] Solo-maintained, best-effort. See STATUS.md. Issues may sit; PRs are read on a slow cadence.
Comparison
See brandkit.run for a side-by-side: same prompt, same palette, fed through Claude-direct SVG / raw Ideogram / brandkit. Spoiler: only the brandkit column passes verify.
License
Apache-2.0. Use it commercially, embed it, fork it — the only ask is that you keep the copyright notice and the license file.
