Generate a consistent brand kit from a compact alphanumeric mark and one accent color. Outputs include favicons, vector and raster marks, wordmark lockups, brand sheets, social cards, manifests, and CSS tokens.
The mark and wordmark use real font outlines. The common website path requires only Node.js; browser rendering is optional.
Illustrative input using a non-production sample palette:
{
"name": "Severino Labs",
"identity": {
"slug": "severino-labs",
"color": "#6D5EF7",
"deep": "#352A8A",
"onColor": "#FFFFFF",
"glyph": "SL",
"wordmark": "Severino Labs"
},
"portrait": "./studio.jpg",
"cardPalette": {
"accent": "#9B8CFF",
"textSoft": "#E3DEFF",
"textMuted": "#B7AFE8"
},
"cards": [
{
"file": "social-card.png",
"width": 1200,
"height": 630,
"photoWidth": 420,
"eyebrow": "Severino Labs",
"name": "Brand systems, generated.",
"tagline": "Marks, wordmarks, sheets, web assets, and social cards from one config.",
"meta": "Illustrative branding-engine example",
"url": "github.com/joeseverino/branding-engine"
}
]
}Generated mark:
Generated wordmark:
Generated brand sheet:
Generated social card:
The complete input and committed generated output are in
examples/severino-labs.
- Node.js 18 or newer
sharp, OpenType.js, and the WOFF2 decoder, installed automatically- Optional:
@playwright/testplus Chromium for brand sheets and social cards
Install:
npm install branding-engineFor a project-local CLI installation:
npm install --save-dev branding-engine
npx branding-engine --helpThe package can also be installed globally with
npm install --global branding-engine, though project-local installation keeps
the version reproducible for collaborators and CI.
For sheets and social cards:
npm install --save-dev @playwright/test
npx playwright install chromiumglyph is the compact mark rendered inside the tile.
- Accepts 1-3 ASCII letters or digits
- Lowercase letters are normalized to uppercase
- Spaces, punctuation, symbols, and strings longer than three characters fail
- Layout dynamically adjusts by character count and caps narrow marks by height
Valid:
A
AC
A3X
7
R2
Invalid:
ABCD
A C
A-C
@
Use init and generate when a site needs favicons, a manifest, and CSS
tokens in its public directory.
npm install --save-dev branding-engine
npx branding-engine initEdit the generated brand.config.json:
{
"name": "My Site",
"accent": "#2563EB",
"deep": "#173B8F",
"onColor": "#FFFFFF",
"glyph": "MS"
}Then generate the files:
npm run brandDefault output:
public/
├── apple-touch-icon.png
├── brand-tokens.css
├── favicon-32.png
├── favicon-192.png
├── favicon.ico
├── favicon.svg
└── site.webmanifest
The command also prints the <head> links to add to the site.
| Field | Required | Description |
|---|---|---|
name |
yes | Application name used in site.webmanifest |
accent |
yes | Six-digit hex accent, with or without # |
glyph |
yes | One to three alphanumeric mark characters |
deep |
no | Dark palette shade; derived from accent when omitted |
onColor |
no | Glyph color on the accent; defaults to #FFFFFF |
Options:
branding-engine generate --config path/to/brand.config.json --public path/to/publicGenerated files are deterministic and intended to be committed with the site.
Astro serves files from public/ at the site root, so the default generator
paths work without customization:
npm install --save-dev branding-engine
npx branding-engine init
npm run brandIn your shared layout, add the generated links and tokens:
<html lang="en">
<head>
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="stylesheet" href="/brand-tokens.css" />
<meta name="theme-color" content="#2563EB" />
</head>
<body><slot /></body>
</html>To regenerate before every production build, add it to the existing build script:
{
"scripts": {
"brand": "branding-engine generate",
"build": "npm run brand && astro build"
}
}If the repository already publishes a public/ directory, use the same
default commands as Astro. If the repository root itself is deployed:
npx branding-engine generate --public .Add the links printed by the command to the page <head>, plus the token
stylesheet:
<link rel="stylesheet" href="/brand-tokens.css" />The generated CSS variables can then be used from any stylesheet:
.button {
color: var(--brand-on-accent);
background: var(--brand-accent);
}Use build for a reusable config-driven kit:
branding-engine build --config ./brand --out ./kits--config accepts either a brand.json path or a directory containing
brand.json. An optional surfaces.json can live beside it.
Minimal brand.json:
{
"name": "Acme",
"identity": {
"slug": "acme",
"color": "#1E3A8A",
"glyph": "AC",
"wordmark": "Acme Corp"
}
}Expanded brand.json:
{
"name": "Acme",
"font": "./AcmeSans.ttf",
"weight": 800,
"wordmarkWeight": 700,
"identity": {
"slug": "acme",
"color": "#1E3A8A",
"deep": "#14245C",
"onColor": "#FFFFFF",
"glyph": "A3C",
"wordmark": "Acme Corp"
},
"portrait": "./portrait.jpg",
"cardPalette": {
"accent": "#5B82D6",
"textSoft": "#DDE6FB",
"textMuted": "#A9C0E8"
},
"cards": [
{
"file": "social-card.png",
"width": 1200,
"height": 630,
"photoWidth": 420,
"eyebrow": "Acme Corp",
"name": "Acme",
"tagline": "Built for what comes next.",
"meta": "Brand systems and engineering",
"url": "acme.example"
}
]
}| Field | Required | Description |
|---|---|---|
name |
no | Human-readable brand name used in logs and fallbacks |
identity |
yes | Primary brand identity object |
identity.slug |
yes | Output directory name |
identity.color |
yes | Six-digit accent color |
identity.glyph |
yes | One to three alphanumeric mark characters |
identity.wordmark |
no | Text used for wordmark lockups and sheet title |
identity.deep |
no | Curated dark shade |
identity.onColor |
no | Glyph color on accent |
font |
no | Font path relative to brand.json; defaults to bundled Inter |
weight |
no | Mark font weight; defaults to 800 |
wordmarkWeight |
no | Wordmark font weight; defaults to 700 |
surfaces |
no | Inline additional surfaces; surfaces.json takes precedence |
portrait |
for cards | JPEG path relative to brand.json |
cardPalette |
for cards | Card accent and supporting text colors |
cards |
no | Social-card definitions rendered to <out>/cards/ |
Additional surfaces inherit the primary glyph unless they override it:
{
"support": {
"color": "#1F4D57",
"wordmark": "Acme Support"
},
"labs": {
"color": "#7C3AED",
"glyph": "A3",
"wordmark": "Acme Labs"
}
}Create a kit without a config file:
branding-engine kit acme ff5733 AC "Acme Corp"Three-character example:
branding-engine kit prism 635bff P3X "Prism Works" \
--only mark,wordmark,web \
--out ./kitsSyntax:
branding-engine kit <slug> <hex> <glyph> ["Wordmark"] [options]
Select stages with a comma-separated --only value:
branding-engine build \
--config ./brand.json \
--out ./kits \
--only mark,wordmark,web| Stage | Browser needed | Output |
|---|---|---|
mark |
no | Favicons, vector mark, PNG marks, transparent variants |
wordmark |
no | Vector and PNG title-case/all-caps lockups |
web |
no | CSS tokens, web manifest, and <head> snippet |
sheet |
yes | Brand overview poster, sections, and generated kit README |
cards |
yes | Configured social-card PNGs |
Without --only, all stages run.
Each kit is written under <out>/<slug>/:
<out>/<slug>/
├── icons/
├── mark/
├── sheet/
├── web/
└── wordmark/
Social cards are written to <out>/cards/.
import {
buildBrand,
buildKit,
generateSite,
markSvg,
normalizeGlyph,
wordmarkSvg,
} from 'branding-engine';
await buildKit({
slug: 'acme',
hex: '#FF5733',
glyph: 'a3x',
wordmark: 'Acme',
only: 'mark,wordmark,web',
outDir: 'public/brand',
});
const glyph = normalizeGlyph('a3x'); // "A3X"
const mark = markSvg({ size: 64, bg: '#FF5733', glyph });
const lockup = wordmarkSvg({
tileHex: '#FF5733',
text: 'Acme',
glyph,
});Main exports:
buildBrand(options)buildKit(options)initSite(options)generateSite(options)makeMark(options)makeWordmark(options)makeSheet(options)makeWeb(options)makeCards(options)markSvg(options)wordmarkSvg(options)normalizeGlyph(glyph)renderCard(browser, options)launchBrowser()
Bundled Inter caches include uppercase letters and digits for marks, plus uppercase, lowercase, digits, and spaces for wordmarks.
Custom fonts and missing characters are extracted entirely in Node with OpenType.js and a WebAssembly WOFF2 decoder. No Python, fonttools, native binding, or system font utility is required. Supported input formats are TTF, OTF, WOFF, and WOFF2.
Extracted caches are written under .brand-cache/, or the directory specified
by BRAND_CACHE_DIR. The installed package is never modified. If a variable
font cannot be instantiated at the requested weight, the build exits with the
font filename and parser error; use a static font file or another supported
variable font.
The CLI exits nonzero with an actionable message for invalid glyphs, invalid colors, missing configs, unavailable font glyphs, or missing optional browser dependencies.
Example:
Invalid glyph: "ABCD". Expected 1-3 letters or digits, e.g. A, AC, or A3X.
MIT. The bundled Inter font includes its own notice under
assets/fonts/inter/NOTICE.md.



