add-brand-logo
Add 1 or 500 brand logos to any registry, with a 3-tier fallback that never breaks.
Agent-agnostic. Works with Claude Code, Codex, Gemini CLI, Cursor, or standalone via bun.
When to use
Use this skill when you need to add brand artwork to a UI and you want:
- A drop-in
<BrandLogo>component that never shows a broken image. - A way to decide — for each brand — whether you actually need to commit a curated SVG, or whether a free CDN favicon is good enough.
- A bulk path: import 100+ brands from a CSV, get back a quality-scored report telling you which 80% are auto-OK and which 20% need manual curation.
- A consistent registry entry across projects that have different formats (TS object, YAML files, JSON registry).
Do not use this for: a single one-off image asset (just commit it), or for cases where you need pixel-perfect curated artwork on every brand (this skill assumes graceful fallback is acceptable).
The 3-tier fallback (the core idea)
Every logo render goes through this chain. The skill is structured around the question "for THIS brand, where does it land?"
| Tier | Source | When it fires | Cost to maintain |
|---|---|---|---|
| 1 | Curated artwork in your repo | You have a clean SVG/PNG | Manual: 5–15 min per brand |
| 2 | https://www.google.com/s2/favicons?domain={domain}&sz=128 | Tier 1 missing or 404 | Zero. Just register the domain. |
| 3 | Letter circle in brand color | Tier 1+2 missing | Zero. Auto-generated. |
Insight that makes this scale: for ~80% of well-known brands, Tier 2 is good enough. The skill exists to tell you which 20% need Tier 1, fast, instead of you guessing or curating everything defensively.
How this skill is invoked
Claude Code
Read ~/.claude/skills/add-brand-logo/SKILL.md and add brand logos for: [LIST OR CSV PATH]
Or, if the skill is registered as a slash command in your project:
/add-brand-logo
Other agents (Codex, Gemini CLI, Cursor, Antigravity)
git clone https://github.com/sonpiaz/add-brand-logo ./add-brand-logo
cd add-brand-logo && bun install
Then tell the agent:
Read ./add-brand-logo/SKILL.md and follow the 6-phase workflow for the brands I will paste.
Standalone CLI (no agent)
You can run the probe + bulk scripts directly without an agent:
bun scripts/probe.ts --brand "Stripe" --domain stripe.com
bun scripts/bulk.ts --in brands.csv --out report.json
The 6-phase workflow
Each phase ends with a GATE — do not advance until the user confirms or says "skip gate".
Phase 1 — REGISTRY (5 min)
Goal: identify the project's registry format so generated entries match.
Look for one of these patterns in the user's repo:
| Pattern | Example file | Adapter |
|---|---|---|
| TS object map | lib/models.ts → CREATORS | adapters/kyma-typescript.md |
| YAML files per entry | programs/{slug}.yaml | adapters/open-affiliate-yaml.md |
| Plain JSON registry | data/brands.json | adapters/generic-json.md |
| Other | — | Ask user, write a 1-page adapter spec |
If none match, ask the user: where does brand metadata live? (file path + format).
GATE — confirm the registry path + format with user.
Phase 2 — INVENTORY (5 min)
Goal: list what brands are already registered and what's missing.
Read the registry file. Diff against the user's input list (CSV, paste, or "all from this API endpoint").
Produce a table:
brand | status | domain | tier guess
----------------+-------------+-----------------+------------
stripe | already ✓ | stripe.com | (n/a)
openai | already ✓ | openai.com | (n/a)
new-brand-x | MISSING | brand-x.com | TBD (probe)
unknown-startup | MISSING | ??? | needs domain inference
For MISSING brands without a domain, attempt domain inference (try {slug}.com, {slug}.ai, {slug}.io, then ask user if all fail).
GATE — confirm the missing list + domains before probing.
Phase 3 — PROBE (auto, ~1s per brand)
Goal: for each missing brand, score what fallback tier it lands on.
Run the probe script:
bun scripts/bulk.ts --in missing.csv --out report.json
The script for each brand:
- Downloads
https://www.google.com/s2/favicons?domain={domain}&sz=128 - Scores: file size, dimensions, dominant-color entropy, generic-globe hash check
- Categorizes:
- AUTO-OK — favicon is a real brand mark, ship as Tier 2
- WEAK — favicon exists but is small/generic, recommend curated asset
- MISSING — no favicon at all, falls through to Tier 3 (letter circle)
- HTTP-FAIL — domain unreachable, double-check the domain
Output: report.json + a printable summary.
Phase 4 — REVIEW (manual, ~30s per WEAK item)
Goal: human eyeballs the borderline cases.
Open the probe-downloaded images for any WEAK items:
open .add-brand-logo-cache/probe/*.png
For each WEAK item, mark in report.json:
pass— favicon is acceptable, treat as AUTO-OKcurate— needs Tier 1 curated asset (proceed to Phase 5)letter-only— skip artwork, let Tier 3 handle it
AUTO-OK and MISSING items skip this phase entirely.
GATE — user confirms the curation list.
Phase 5 — CURATE (5–15 min per item, only for curate-marked)
Goal: source a real SVG/PNG for items that fall here.
Try sources in order:
- Brandfetch —
https://cdn.brandfetch.io/{domain}/w/200/h/200/logo(free tier, no key for low volume) - Vendor's own brand assets page — search
"{brand} brand assets"or"{brand} press kit" - HuggingFace model card (for AI model brands) —
huggingface.co/{org}often has SVG figures - GitHub org avatar (for OSS) —
https://github.com/{org}.png?size=200 - Wikipedia infobox SVG — for very large brands
- Manual eyedrop — last resort, screenshot the wordmark from the homepage and trace
Save to the project's logo folder (e.g., public/logos/{slug}.svg).
Quality bar — reject and re-source if:
- SVG embeds raster images (defeats the point)
- Wide wordmark gets squished by
object-containat h-5/w-5 - Doesn't render correctly on both light AND dark backgrounds
Never draw your own letter-on-color SVG. Tier 3 (auto letter circle) is purpose-built for that case and will be visually consistent. If you're tempted, stop. Mark the item letter-only instead.
Phase 6 — REGISTER + VERIFY (5 min)
Goal: write the registry entries + spot-check render.
For each brand, generate the entry per the project's adapter:
TypeScript map (adapters/kyma-typescript.md):
brandSlug: { display: "Brand Name", color: "#hexhex", domain: "brand.com", logo: "/logos/brand.svg" /* optional */ },
YAML file (adapters/open-affiliate-yaml.md):
slug: brand-slug
name: Brand Name
domain: brand.com
brand_color: "#hexhex"
logo: brand-slug.svg # optional
Then verify by running the dev server and visually scanning the new rows:
bun dev # or npm run dev / pnpm dev / etc.
# → open the page that renders the brand list
GATE — user confirms the rendered output looks right. Skill done.
Outputs the workflow produces
For a session adding N brands, you get:
.add-brand-logo-cache/
├── probe/
│ ├── stripe.png # downloaded favicon
│ ├── openai.png
│ └── ...
└── report.json # full per-brand probe + decision
your-project/
├── (registry file edited — entries appended)
└── public/logos/
├── curated-brand-1.svg # only the items marked `curate` in Phase 4
└── curated-brand-2.svg
The .add-brand-logo-cache/ folder belongs in .gitignore (the skill ships a sample .gitignore snippet).
Drop-in component
The skill ships a <BrandLogo> component implementing the 3-tier chain. Two variants:
components/BrandLogo.tsx— React (Next.js client component, "use client" included)- `compo