@clawnify/logokit

中文文档
0.3.0发布于 2026/07/29MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2792

Brand logos as SVG, with a raster fallback for everything else. 6,492 vectors, offline resolution, zero dependencies.

原始使用文档
文档目录16 节
  1. The one thing to know
  2. API
  3. logo(input, options?) → Logo | null
  4. logoUrl(input, options?) → string | null
  5. fetchLogo(input, options?) → Promise<FetchedLogo | null>
  6. Metadata
  7. In React
  8. Staleness
  9. CLI
  10. Providers
  11. Built in
  12. How resolution works
  13. Weight
  14. Licensing
  15. Credits
  16. License

@clawnify/logokit

Brand logos as SVG, with a raster fallback for everything else.

npm i @clawnify/logokit
import { logo } from "@clawnify/logokit";

logo("stripe.com");
// { url: "https://thesvg.org/icons/stripe/default.svg",
//   format: "svg", vector: true, source: "thesvg", slug: "stripe", verified: true }

logo("some-tiny-startup.io");
// { url: "https://t0.gstatic.com/faviconV2?...", 
//   format: "png", vector: false, source: "gstatic", size: 128, verified: false }

Zero dependencies. No API key. Resolution is offline — the brand index ships with the package, so nothing round-trips just to find out whether a logo exists.


The one thing to know

A result is not always a vector. 6,492 brands have real SVG. Everything else falls back to a favicon service that only ever returns PNG.

So format is part of the contract, not decoration:

const hit = logo(input);
if (hit?.vector) {
  // safe to scale, recolor, inline
} else {
  // raster favicon — fine at small sizes, blurry if you scale it
}

If a raster is useless to you, say so and get null instead:

logo("some-tiny-startup.io", { format: "vector" }); // null

Libraries that paper over this hand you a 16×16 PNG and let you discover the blur in production.


API

logo(input, options?) → Logo | null

Synchronous. No network. This is the one you usually want.

Accepts the four shapes you realistically have on hand:

logo("stripe.com");              // domain
logo("https://stripe.com/docs"); // URL
logo("[email protected]");          // email — the company domain is in there
logo("Stripe");                  // brand name

Subdomains and www. are stripped, so docs.stripe.com resolves like stripe.com.

OptionDefault
variant"default"mono, wordmark, light, dark, … Falls back to default when a brand doesn't have it, rather than handing you a URL that 404s.
size128Raster size hint. Snapped to a size the service actually renders (see below).
format"any""vector" restricts the chain to SVG sources.
providers[theSVG(), gstatic()]Ordered chain; first hit wins.

logoUrl(input, options?) → string | null

Just the URL.

fetchLogo(input, options?) → Promise<FetchedLogo | null>

Downloads the image and confirms it exists, walking the chain until a response passes that provider's own check. Adds body: ArrayBuffer, plus svg: string when the result is a vector.

const hit = await fetchLogo("stripe.com");
hit.svg; // "<svg viewBox=..."

Use this when you need to know before rendering — embedding in a PDF, an email, an OG image. For <img>, prefer logo() and let the browser fetch.

Google's favicon endpoint sends no CORS headers, so fetchLogo reaches it from a server but not from a browser. logo() + <img src> works everywhere.

Metadata

Imported separately so URL-only callers don't pay for it:

import { meta } from "@clawnify/logokit/meta";

meta("stripe"); // { slug, title: "Stripe", hex: "635BFF", license: "CC0-1.0" }

Also exported from the main entry: variantsOf(slug), hasVariant(slug, variant), allSlugs(), brandCount(), lookup(query), normalize(input).


In React

Replaces the favicon helper most projects end up writing by hand:

import { logo } from "@clawnify/logokit";
import { useState } from "react";

export function BrandMark({ domain, size = 16 }: { domain?: string | null; size?: number }) {
  const [failed, setFailed] = useState(false);
  const hit = domain ? logo(domain, { size: size * 2 }) : null;
  if (!hit || failed) return null;

  return (
    <img
      src={hit.url}
      alt=""
      width={size}
      height={size}
      loading="lazy"
      onError={(e) => {
        // Step down the chain before giving up — see "Staleness" below.
        if (hit.fallback) e.currentTarget.src = hit.fallback.url;
        else setFailed(true);
      }}
    />
  );
}

Known brands render as crisp SVG at any size; the rest degrade to a favicon.


Staleness

The index is a snapshot of a registry that moves — upstream added brands on 25 separate days in the last fortnight. That's mostly harmless: a brand added after your version shipped simply isn't found, and resolution falls through to the favicon tier on its own.

The case that isn't harmless is a brand renamed upstream. Resolution still succeeds against the stale index, and the URL 404s. Nothing synchronous can detect that — logo() does no I/O by design.

So every result carries the rest of the chain:

const hit = logo("stripe.com");
hit.url;             // theSVG vector
hit.fallback.url;    // the favicon, if the vector is dead

Wire fallback into onError and a stale entry degrades to a favicon instead of to a blank space. fetchLogo() already handles this itself — it verifies each provider and walks past any that fails.

fallback is absent when nothing else can serve the query: with format: "vector" (raster is excluded on purpose), or for a brand-name query, since the favicon tier needs a domain.

Upgrading the package refreshes the index. npm run build:index regenerates it against the live registry if you'd rather not wait for a release.


CLI

$ npx @clawnify/logokit stripe.com
https://thesvg.org/icons/stripe/default.svg
svg · thesvg · license: CC0-1.0

$ npx @clawnify/logokit "Hugging Face" --vector
$ npx @clawnify/logokit github.com --variant mono --json

Exits 1 when nothing is found, so it composes in scripts.


Providers

A provider is a plain object, and the chain is an ordered array. Adding a source is a local change — there's no registry to register with.

import { logo, lookup, gstatic } from "@clawnify/logokit";

// Simple Icons — CC0, keyed by the same slugs
const simpleIcons = {
  name: "simple-icons",
  vector: true,
  resolve(query) {
    const slug = lookup(query);
    if (!slug) return null;
    return {
      url: `https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/${slug}.svg`,
      format: "svg", vector: true, source: "simple-icons", slug, verified: false,
    };
  },
};

logo("stripe.com", { providers: [simpleIcons, gstatic()] });

Add an optional verify(response) → boolean to reject responses that look successful but aren't — see below for why that exists.

Built in

ProviderCoverageFormatKey
theSVG()6,492 brandsSVGnone
gstatic()any live domainPNGnone

theSVG({ origin }) accepts a different origin — a jsDelivr mirror is exported as THESVG_JSDELIVR, or point it at your own copy of the icon tree.


How resolution works

The package ships a generated index (~106 KB gzipped) built from theSVG's public registry, mapping 3,547 domains and 5,399 name forms onto slugs. Lookups are a Map hit, so a miss costs nothing and never burns a request.

Two details worth knowing, both learned from the endpoints rather than their docs:

Shared homepages are not guessed. Roughly 500 open-source projects list github.com as their homepage, ~630 Azure product marks list azure.microsoft.com. Mapping a shared domain to an arbitrary member would make logo("github.com") return, say, the axios mark. The build resolves a collision only when exactly one candidate is the domain; 71 domains that stay ambiguous are dropped and fall through to the favicon tier. A correct favicon beats a confidently wrong brand. Every dropped domain is listed in src/data/ambiguous-domains.json so the omissions are auditable rather than mysterious.

Two failure modes that look like success. Google's favicon endpoint answers a miss with 404 and a valid PNG body — a generic grey globe. Code that checks "did bytes arrive?" renders the globe and reports success, so gstatic's verify gates on status instead. Separately, sizes outside 16, 24, 32, 48, 64, 96, 128, 256 return 200 with a silent 16×16 image, so requested sizes are snapped before they reach the wire.


Weight

The index is what costs something. If you bundle, it's already free to avoid:

import { gstatic, normalize } from "@clawnify/logokit"; // 636 B gzipped
import { logo } from "@clawnify/logokit";               // 109 KB gzipped

Tree-shaking drops the index when nothing references it. Node and Workers don't tree-shake, though — they evaluate the whole module graph on import, so the main entry costs a few ms and ~1.4 MB of heap per cold start whether or not you ever resolve a brand.

If favicons are genuinely all you want, take the raster-only entry:

import { logo } from "@clawnify/logokit/gstatic";

logo("stripe.com"); // { format: "png", source: "gstatic", verified: false }

Same favicon tier, same size-snapping, same fetchLogo status check — it just never loads the index (~186 KB heap instead of ~1.4 MB). It resolves domains only; brand names need the index by definition.

Prefer the main entry unless you've measured and care. Vectors where they exist are the reason this library exists.


Licensing

The code is MIT.

The logos are not. They are trademarks of their respective owners, included for identification under nominative fair use. Upstream records a per-mark license, and it varies a lot — CC0-1.0, MIT, Apache-2.0, but also CC-BY-ND (no derivatives), Proprietary, non-commercial terms, and Unknown.

That field is exposed so you can act on it:

import { meta } from "@clawnify/logokit/meta";
meta("stripe").license; // "CC0-1.0"

If you redistribute marks, or modify them, check that value first. See NOTICE.md.


Credits

Vector artwork and the brand registry come from theSVG (glincker/thesvg). This package adds resolution, the fallback chain, and the provider interface — it does not host or modify the artwork.

Refresh the bundled index against the current registry with:

npm run build:index

License

MIT © logokit contributors

版本与发布记录

@clawnify/logokit 共发布 3 个版本,首次发布于 2026/07/29;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.0latest2026/07/293 个月前
0.2.02026/07/293 个月前
0.1.02026/07/293 个月前

关于 @clawnify/logokit 的常见问题

@clawnify/logokit 是什么?

Brand logos as SVG, with a raster fallback for everything else. 6,492 vectors, offline resolution, zero dependencies.

如何安装 @clawnify/logokit?

在项目目录运行 npm install @clawnify/logokit,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 logokit,可以用 npx logokit 直接运行。

@clawnify/logokit 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@clawnify/logokit 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@clawnify/logokit 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@clawnify/logokit 可以商用吗?

@clawnify/logokit 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@clawnify/logokit 的下载量有多少?

根据 npm 官方下载统计,@clawnify/logokit 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,792 次,日均约 93 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@clawnify/logokit 还在维护吗?

最新版本 0.3.0 发布于 2026/07/29(3 个月前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/07/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。