branding-engine 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +428 -73
- package/bin/cli.mjs +8 -5
- package/examples/severino-labs/README.md +95 -0
- package/examples/severino-labs/brand.json +30 -0
- package/examples/severino-labs/generated/cards/social-card.png +0 -0
- package/examples/severino-labs/generated/severino-labs/README.md +49 -0
- package/examples/severino-labs/generated/severino-labs/icons/apple-touch-icon.png +0 -0
- package/examples/severino-labs/generated/severino-labs/icons/favicon-192.png +0 -0
- package/examples/severino-labs/generated/severino-labs/icons/favicon-32.png +0 -0
- package/examples/severino-labs/generated/severino-labs/icons/favicon.ico +0 -0
- package/examples/severino-labs/generated/severino-labs/icons/favicon.svg +1 -0
- package/examples/severino-labs/generated/severino-labs/mark/mark-1024.png +0 -0
- package/examples/severino-labs/generated/severino-labs/mark/mark-512.png +0 -0
- package/examples/severino-labs/generated/severino-labs/mark/mark-transparent-dark.png +0 -0
- package/examples/severino-labs/generated/severino-labs/mark/mark-transparent-light.png +0 -0
- package/examples/severino-labs/generated/severino-labs/mark/mark.svg +1 -0
- package/examples/severino-labs/generated/severino-labs/sheet/overview.png +0 -0
- package/examples/severino-labs/generated/severino-labs/sheet/palette.png +0 -0
- package/examples/severino-labs/generated/severino-labs/sheet/sheet-mark.png +0 -0
- package/examples/severino-labs/generated/severino-labs/sheet/type-specimen.png +0 -0
- package/examples/severino-labs/generated/severino-labs/web/head.html +6 -0
- package/examples/severino-labs/generated/severino-labs/web/site.webmanifest +19 -0
- package/examples/severino-labs/generated/severino-labs/web/tokens.css +7 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark-caps-dark.png +0 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark-caps-light.png +0 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark-caps.svg +1 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark-dark.png +0 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark-light.png +0 -0
- package/examples/severino-labs/generated/severino-labs/wordmark/wordmark.svg +1 -0
- package/examples/severino-labs/studio.jpg +0 -0
- package/index.mjs +1 -0
- package/package.json +13 -6
- package/src/build.mjs +8 -6
- package/src/lib/extract-glyphs.mjs +65 -0
- package/src/lib/glyphs.mjs +6 -19
- package/src/lib/identity.mjs +11 -0
- package/src/lib/mark.mjs +28 -8
- package/src/lib/render.mjs +11 -2
- package/src/lib/wordmark.mjs +6 -1
- package/src/make-mark.mjs +2 -0
- package/src/make-sheet.mjs +2 -0
- package/src/make-web.mjs +2 -0
- package/src/make-wordmark.mjs +3 -1
- package/src/site.mjs +3 -1
- package/requirements.txt +0 -3
- package/src/lib/extract-glyphs.py +0 -75
package/README.md
CHANGED
|
@@ -1,129 +1,484 @@
|
|
|
1
1
|
# branding-engine
|
|
2
2
|
|
|
3
|
-
Generate a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
(pure vector, no raster source); only the social cards and brand sheet render
|
|
7
|
-
live text via a headless browser.
|
|
3
|
+
Generate a consistent brand kit from a compact alphanumeric mark and one accent
|
|
4
|
+
color. Outputs include favicons, vector and raster marks, wordmark lockups,
|
|
5
|
+
brand sheets, social cards, manifests, and CSS tokens.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
The mark and wordmark use real font outlines. The common website path requires
|
|
8
|
+
only Node.js; browser rendering is optional.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Example
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
Illustrative input using a non-production sample palette:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"name": "Severino Labs",
|
|
17
|
+
"identity": {
|
|
18
|
+
"slug": "severino-labs",
|
|
19
|
+
"color": "#6D5EF7",
|
|
20
|
+
"deep": "#352A8A",
|
|
21
|
+
"onColor": "#FFFFFF",
|
|
22
|
+
"glyph": "SL",
|
|
23
|
+
"wordmark": "Severino Labs"
|
|
24
|
+
},
|
|
25
|
+
"portrait": "./studio.jpg",
|
|
26
|
+
"cardPalette": {
|
|
27
|
+
"accent": "#9B8CFF",
|
|
28
|
+
"textSoft": "#E3DEFF",
|
|
29
|
+
"textMuted": "#B7AFE8"
|
|
30
|
+
},
|
|
31
|
+
"cards": [
|
|
32
|
+
{
|
|
33
|
+
"file": "social-card.png",
|
|
34
|
+
"width": 1200,
|
|
35
|
+
"height": 630,
|
|
36
|
+
"photoWidth": 420,
|
|
37
|
+
"eyebrow": "Severino Labs",
|
|
38
|
+
"name": "Brand systems, generated.",
|
|
39
|
+
"tagline": "Marks, wordmarks, sheets, web assets, and social cards from one config.",
|
|
40
|
+
"meta": "Illustrative branding-engine example",
|
|
41
|
+
"url": "github.com/jseverino/branding-engine"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Generated mark:
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
Generated wordmark:
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
Generated brand sheet:
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
Generated social card:
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
The complete input and committed generated output are in
|
|
64
|
+
[`examples/severino-labs`](./examples/severino-labs/).
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- Node.js 18 or newer
|
|
69
|
+
- `sharp`, OpenType.js, and the WOFF2 decoder, installed automatically
|
|
70
|
+
- Optional: `@playwright/test` plus Chromium for brand sheets and social cards
|
|
71
|
+
|
|
72
|
+
Install:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
14
75
|
npm install branding-engine
|
|
15
76
|
```
|
|
16
77
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
78
|
+
For a project-local CLI installation:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install --save-dev branding-engine
|
|
82
|
+
npx branding-engine --help
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The package can also be installed globally with
|
|
86
|
+
`npm install --global branding-engine`, though project-local installation keeps
|
|
87
|
+
the version reproducible for collaborators and CI.
|
|
88
|
+
|
|
89
|
+
For sheets and social cards:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install --save-dev @playwright/test
|
|
93
|
+
npx playwright install chromium
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Glyph Rules
|
|
97
|
+
|
|
98
|
+
`glyph` is the compact mark rendered inside the tile.
|
|
99
|
+
|
|
100
|
+
- Accepts 1-3 ASCII letters or digits
|
|
101
|
+
- Lowercase letters are normalized to uppercase
|
|
102
|
+
- Spaces, punctuation, symbols, and strings longer than three characters fail
|
|
103
|
+
- Layout dynamically adjusts by character count and caps narrow marks by height
|
|
104
|
+
|
|
105
|
+
Valid:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
A
|
|
109
|
+
AC
|
|
110
|
+
A3X
|
|
111
|
+
7
|
|
112
|
+
R2
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Invalid:
|
|
21
116
|
|
|
22
|
-
```
|
|
23
|
-
|
|
117
|
+
```text
|
|
118
|
+
ABCD
|
|
119
|
+
A C
|
|
120
|
+
A-C
|
|
121
|
+
@
|
|
24
122
|
```
|
|
25
123
|
|
|
26
|
-
##
|
|
124
|
+
## Quick Start: Add Branding to a Website
|
|
27
125
|
|
|
28
|
-
|
|
29
|
-
|
|
126
|
+
Use `init` and `generate` when a site needs favicons, a manifest, and CSS
|
|
127
|
+
tokens in its public directory.
|
|
30
128
|
|
|
31
|
-
```
|
|
32
|
-
npm
|
|
33
|
-
npx branding-engine init
|
|
34
|
-
# edit accent, glyph, name in brand.config.json, then:
|
|
35
|
-
npm run brand # generates into public/, prints the <head> snippet
|
|
129
|
+
```bash
|
|
130
|
+
npm install --save-dev branding-engine
|
|
131
|
+
npx branding-engine init
|
|
36
132
|
```
|
|
37
133
|
|
|
38
|
-
`brand.config.json
|
|
134
|
+
Edit the generated `brand.config.json`:
|
|
39
135
|
|
|
40
136
|
```json
|
|
41
|
-
{
|
|
137
|
+
{
|
|
138
|
+
"name": "My Site",
|
|
139
|
+
"accent": "#2563EB",
|
|
140
|
+
"deep": "#173B8F",
|
|
141
|
+
"onColor": "#FFFFFF",
|
|
142
|
+
"glyph": "MS"
|
|
143
|
+
}
|
|
42
144
|
```
|
|
43
145
|
|
|
44
|
-
|
|
45
|
-
|
|
146
|
+
Then generate the files:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npm run brand
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Default output:
|
|
46
153
|
|
|
47
154
|
```text
|
|
48
|
-
public/
|
|
49
|
-
|
|
155
|
+
public/
|
|
156
|
+
├── apple-touch-icon.png
|
|
157
|
+
├── brand-tokens.css
|
|
158
|
+
├── favicon-32.png
|
|
159
|
+
├── favicon-192.png
|
|
160
|
+
├── favicon.ico
|
|
161
|
+
├── favicon.svg
|
|
162
|
+
└── site.webmanifest
|
|
50
163
|
```
|
|
51
164
|
|
|
52
|
-
|
|
53
|
-
color change reproduces them exactly. Optional flags: `--public <dir>`,
|
|
54
|
-
`--config <file>`.
|
|
165
|
+
The command also prints the `<head>` links to add to the site.
|
|
55
166
|
|
|
56
|
-
|
|
167
|
+
### Website Config Reference
|
|
57
168
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
169
|
+
| Field | Required | Description |
|
|
170
|
+
|---|---:|---|
|
|
171
|
+
| `name` | yes | Application name used in `site.webmanifest` |
|
|
172
|
+
| `accent` | yes | Six-digit hex accent, with or without `#` |
|
|
173
|
+
| `glyph` | yes | One to three alphanumeric mark characters |
|
|
174
|
+
| `deep` | no | Dark palette shade; derived from `accent` when omitted |
|
|
175
|
+
| `onColor` | no | Glyph color on the accent; defaults to `#FFFFFF` |
|
|
61
176
|
|
|
62
|
-
|
|
63
|
-
branding-engine kit acme ff5733 AC "Acme Corp" --only mark,wordmark,web
|
|
177
|
+
Options:
|
|
64
178
|
|
|
65
|
-
|
|
66
|
-
branding-engine
|
|
179
|
+
```bash
|
|
180
|
+
branding-engine generate --config path/to/brand.config.json --public path/to/public
|
|
67
181
|
```
|
|
68
182
|
|
|
69
|
-
|
|
70
|
-
|
|
183
|
+
Generated files are deterministic and intended to be committed with the site.
|
|
184
|
+
|
|
185
|
+
### Astro
|
|
186
|
+
|
|
187
|
+
Astro serves files from `public/` at the site root, so the default generator
|
|
188
|
+
paths work without customization:
|
|
71
189
|
|
|
72
|
-
|
|
190
|
+
```bash
|
|
191
|
+
npm install --save-dev branding-engine
|
|
192
|
+
npx branding-engine init
|
|
193
|
+
npm run brand
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
In your shared layout, add the generated links and tokens:
|
|
197
|
+
|
|
198
|
+
```astro
|
|
199
|
+
<html lang="en">
|
|
200
|
+
<head>
|
|
201
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
202
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
203
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
204
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
205
|
+
<link rel="stylesheet" href="/brand-tokens.css" />
|
|
206
|
+
<meta name="theme-color" content="#2563EB" />
|
|
207
|
+
</head>
|
|
208
|
+
<body><slot /></body>
|
|
209
|
+
</html>
|
|
210
|
+
```
|
|
73
211
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
omit `font` to use the bundled Inter.
|
|
212
|
+
To regenerate before every production build, add it to the existing build
|
|
213
|
+
script:
|
|
77
214
|
|
|
78
|
-
```
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"scripts": {
|
|
218
|
+
"brand": "branding-engine generate",
|
|
219
|
+
"build": "npm run brand && astro build"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Plain HTML or Static Site
|
|
225
|
+
|
|
226
|
+
If the repository already publishes a `public/` directory, use the same
|
|
227
|
+
default commands as Astro. If the repository root itself is deployed:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npx branding-engine generate --public .
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Add the links printed by the command to the page `<head>`, plus the token
|
|
234
|
+
stylesheet:
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<link rel="stylesheet" href="/brand-tokens.css" />
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The generated CSS variables can then be used from any stylesheet:
|
|
241
|
+
|
|
242
|
+
```css
|
|
243
|
+
.button {
|
|
244
|
+
color: var(--brand-on-accent);
|
|
245
|
+
background: var(--brand-accent);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Full Brand Kit
|
|
250
|
+
|
|
251
|
+
Use `build` for a reusable config-driven kit:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
branding-engine build --config ./brand --out ./kits
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`--config` accepts either a `brand.json` path or a directory containing
|
|
258
|
+
`brand.json`. An optional `surfaces.json` can live beside it.
|
|
259
|
+
|
|
260
|
+
Minimal `brand.json`:
|
|
261
|
+
|
|
262
|
+
```json
|
|
79
263
|
{
|
|
80
264
|
"name": "Acme",
|
|
81
|
-
"wordmarkWeight": 700, // wordmark text weight (mark uses `weight`, default 800)
|
|
82
265
|
"identity": {
|
|
83
266
|
"slug": "acme",
|
|
84
|
-
"color": "#1E3A8A",
|
|
85
|
-
"deep": "#14245C", // optional; falls back to a darkened accent
|
|
86
|
-
"onColor": "#ffffff", // optional; color on the accent
|
|
267
|
+
"color": "#1E3A8A",
|
|
87
268
|
"glyph": "AC",
|
|
88
269
|
"wordmark": "Acme Corp"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Expanded `brand.json`:
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"name": "Acme",
|
|
279
|
+
"font": "./AcmeSans.ttf",
|
|
280
|
+
"weight": 800,
|
|
281
|
+
"wordmarkWeight": 700,
|
|
282
|
+
"identity": {
|
|
283
|
+
"slug": "acme",
|
|
284
|
+
"color": "#1E3A8A",
|
|
285
|
+
"deep": "#14245C",
|
|
286
|
+
"onColor": "#FFFFFF",
|
|
287
|
+
"glyph": "A3C",
|
|
288
|
+
"wordmark": "Acme Corp"
|
|
289
|
+
},
|
|
290
|
+
"portrait": "./portrait.jpg",
|
|
291
|
+
"cardPalette": {
|
|
292
|
+
"accent": "#5B82D6",
|
|
293
|
+
"textSoft": "#DDE6FB",
|
|
294
|
+
"textMuted": "#A9C0E8"
|
|
89
295
|
},
|
|
90
|
-
"
|
|
91
|
-
|
|
296
|
+
"cards": [
|
|
297
|
+
{
|
|
298
|
+
"file": "social-card.png",
|
|
299
|
+
"width": 1200,
|
|
300
|
+
"height": 630,
|
|
301
|
+
"photoWidth": 420,
|
|
302
|
+
"eyebrow": "Acme Corp",
|
|
303
|
+
"name": "Acme",
|
|
304
|
+
"tagline": "Built for what comes next.",
|
|
305
|
+
"meta": "Brand systems and engineering",
|
|
306
|
+
"url": "acme.example"
|
|
307
|
+
}
|
|
308
|
+
]
|
|
92
309
|
}
|
|
93
310
|
```
|
|
94
311
|
|
|
95
|
-
|
|
96
|
-
|
|
312
|
+
### Full Config Reference
|
|
313
|
+
|
|
314
|
+
| Field | Required | Description |
|
|
315
|
+
|---|---:|---|
|
|
316
|
+
| `name` | no | Human-readable brand name used in logs and fallbacks |
|
|
317
|
+
| `identity` | yes | Primary brand identity object |
|
|
318
|
+
| `identity.slug` | yes | Output directory name |
|
|
319
|
+
| `identity.color` | yes | Six-digit accent color |
|
|
320
|
+
| `identity.glyph` | yes | One to three alphanumeric mark characters |
|
|
321
|
+
| `identity.wordmark` | no | Text used for wordmark lockups and sheet title |
|
|
322
|
+
| `identity.deep` | no | Curated dark shade |
|
|
323
|
+
| `identity.onColor` | no | Glyph color on accent |
|
|
324
|
+
| `font` | no | Font path relative to `brand.json`; defaults to bundled Inter |
|
|
325
|
+
| `weight` | no | Mark font weight; defaults to `800` |
|
|
326
|
+
| `wordmarkWeight` | no | Wordmark font weight; defaults to `700` |
|
|
327
|
+
| `surfaces` | no | Inline additional surfaces; `surfaces.json` takes precedence |
|
|
328
|
+
| `portrait` | for cards | JPEG path relative to `brand.json` |
|
|
329
|
+
| `cardPalette` | for cards | Card accent and supporting text colors |
|
|
330
|
+
| `cards` | no | Social-card definitions rendered to `<out>/cards/` |
|
|
331
|
+
|
|
332
|
+
Additional surfaces inherit the primary glyph unless they override it:
|
|
97
333
|
|
|
98
334
|
```json
|
|
99
|
-
{
|
|
335
|
+
{
|
|
336
|
+
"support": {
|
|
337
|
+
"color": "#1F4D57",
|
|
338
|
+
"wordmark": "Acme Support"
|
|
339
|
+
},
|
|
340
|
+
"labs": {
|
|
341
|
+
"color": "#7C3AED",
|
|
342
|
+
"glyph": "A3",
|
|
343
|
+
"wordmark": "Acme Labs"
|
|
344
|
+
}
|
|
345
|
+
}
|
|
100
346
|
```
|
|
101
347
|
|
|
348
|
+
## One-Off Kit
|
|
349
|
+
|
|
350
|
+
Create a kit without a config file:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
branding-engine kit acme ff5733 AC "Acme Corp"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Three-character example:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
branding-engine kit prism 635bff P3X "Prism Works" \
|
|
360
|
+
--only mark,wordmark,web \
|
|
361
|
+
--out ./kits
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Syntax:
|
|
365
|
+
|
|
366
|
+
```text
|
|
367
|
+
branding-engine kit <slug> <hex> <glyph> ["Wordmark"] [options]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Stages
|
|
371
|
+
|
|
372
|
+
Select stages with a comma-separated `--only` value:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
branding-engine build \
|
|
376
|
+
--config ./brand.json \
|
|
377
|
+
--out ./kits \
|
|
378
|
+
--only mark,wordmark,web
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
| Stage | Browser needed | Output |
|
|
382
|
+
|---|---:|---|
|
|
383
|
+
| `mark` | no | Favicons, vector mark, PNG marks, transparent variants |
|
|
384
|
+
| `wordmark` | no | Vector and PNG title-case/all-caps lockups |
|
|
385
|
+
| `web` | no | CSS tokens, web manifest, and `<head>` snippet |
|
|
386
|
+
| `sheet` | yes | Brand overview poster, sections, and generated kit README |
|
|
387
|
+
| `cards` | yes | Configured social-card PNGs |
|
|
388
|
+
|
|
389
|
+
Without `--only`, all stages run.
|
|
390
|
+
|
|
391
|
+
## Output Layout
|
|
392
|
+
|
|
393
|
+
Each kit is written under `<out>/<slug>/`:
|
|
394
|
+
|
|
395
|
+
```text
|
|
396
|
+
<out>/<slug>/
|
|
397
|
+
├── icons/
|
|
398
|
+
├── mark/
|
|
399
|
+
├── sheet/
|
|
400
|
+
├── web/
|
|
401
|
+
└── wordmark/
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Social cards are written to `<out>/cards/`.
|
|
405
|
+
|
|
102
406
|
## Programmatic API
|
|
103
407
|
|
|
104
408
|
```js
|
|
105
|
-
import {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
409
|
+
import {
|
|
410
|
+
buildBrand,
|
|
411
|
+
buildKit,
|
|
412
|
+
generateSite,
|
|
413
|
+
markSvg,
|
|
414
|
+
normalizeGlyph,
|
|
415
|
+
wordmarkSvg,
|
|
416
|
+
} from 'branding-engine';
|
|
417
|
+
|
|
418
|
+
await buildKit({
|
|
419
|
+
slug: 'acme',
|
|
420
|
+
hex: '#FF5733',
|
|
421
|
+
glyph: 'a3x',
|
|
422
|
+
wordmark: 'Acme',
|
|
423
|
+
only: 'mark,wordmark,web',
|
|
424
|
+
outDir: 'public/brand',
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const glyph = normalizeGlyph('a3x'); // "A3X"
|
|
428
|
+
const mark = markSvg({ size: 64, bg: '#FF5733', glyph });
|
|
429
|
+
const lockup = wordmarkSvg({
|
|
430
|
+
tileHex: '#FF5733',
|
|
431
|
+
text: 'Acme',
|
|
432
|
+
glyph,
|
|
433
|
+
});
|
|
109
434
|
```
|
|
110
435
|
|
|
111
|
-
|
|
436
|
+
Main exports:
|
|
437
|
+
|
|
438
|
+
- `buildBrand(options)`
|
|
439
|
+
- `buildKit(options)`
|
|
440
|
+
- `initSite(options)`
|
|
441
|
+
- `generateSite(options)`
|
|
442
|
+
- `makeMark(options)`
|
|
443
|
+
- `makeWordmark(options)`
|
|
444
|
+
- `makeSheet(options)`
|
|
445
|
+
- `makeWeb(options)`
|
|
446
|
+
- `makeCards(options)`
|
|
447
|
+
- `markSvg(options)`
|
|
448
|
+
- `wordmarkSvg(options)`
|
|
449
|
+
- `normalizeGlyph(glyph)`
|
|
450
|
+
- `renderCard(browser, options)`
|
|
451
|
+
- `launchBrowser()`
|
|
112
452
|
|
|
113
|
-
|
|
453
|
+
## Fonts and Glyph Extraction
|
|
114
454
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
- `wordmark/`: `wordmark.svg` + light/dark PNGs, plus all-caps `wordmark-caps.*`
|
|
118
|
-
- `sheet/`: `overview.png` poster + section images, and a `README.md`
|
|
119
|
-
- `web/`: `tokens.css`, `site.webmanifest`, `head.html`
|
|
455
|
+
Bundled Inter caches include uppercase letters and digits for marks, plus
|
|
456
|
+
uppercase, lowercase, digits, and spaces for wordmarks.
|
|
120
457
|
|
|
121
|
-
|
|
458
|
+
Custom fonts and missing characters are extracted entirely in Node with
|
|
459
|
+
OpenType.js and a WebAssembly WOFF2 decoder. No Python, fonttools, native
|
|
460
|
+
binding, or system font utility is required. Supported input formats are TTF,
|
|
461
|
+
OTF, WOFF, and WOFF2.
|
|
462
|
+
|
|
463
|
+
Extracted caches are written under `.brand-cache/`, or the directory specified
|
|
464
|
+
by `BRAND_CACHE_DIR`. The installed package is never modified. If a variable
|
|
465
|
+
font cannot be instantiated at the requested `weight`, the build exits with the
|
|
466
|
+
font filename and parser error; use a static font file or another supported
|
|
467
|
+
variable font.
|
|
468
|
+
|
|
469
|
+
## Errors
|
|
470
|
+
|
|
471
|
+
The CLI exits nonzero with an actionable message for invalid glyphs, invalid
|
|
472
|
+
colors, missing configs, unavailable font glyphs, or missing optional browser
|
|
473
|
+
dependencies.
|
|
474
|
+
|
|
475
|
+
Example:
|
|
476
|
+
|
|
477
|
+
```text
|
|
478
|
+
Invalid glyph: "ABCD". Expected 1-3 letters or digits, e.g. A, AC, or A3X.
|
|
479
|
+
```
|
|
122
480
|
|
|
123
|
-
##
|
|
481
|
+
## License
|
|
124
482
|
|
|
125
|
-
The bundled Inter
|
|
126
|
-
|
|
127
|
-
demand and requires `python3` + `fonttools` (`pip install -r requirements.txt`);
|
|
128
|
-
the extracted cache is written under `.brand-cache/` (or `$BRAND_CACHE_DIR`),
|
|
129
|
-
never into the installed package.
|
|
483
|
+
MIT. The bundled Inter font includes its own notice under
|
|
484
|
+
`assets/fonts/inter/NOTICE.md`.
|
package/bin/cli.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// branding-engine init scaffold a site brand.config + npm script
|
|
4
4
|
// branding-engine generate [--public <dir>] [--config <file>] favicons + manifest + tokens -> public/
|
|
5
5
|
// branding-engine build [--config <dir|brand.json>] [--out <dir>] [--only a,b]
|
|
6
|
-
// branding-engine kit <slug> <hex> <
|
|
6
|
+
// branding-engine kit <slug> <hex> <glyph> ["Wordmark"] [--font f] [--out d] [--only a,b]
|
|
7
7
|
// Stages for --only: mark, wordmark, sheet, web, cards (mark includes favicons).
|
|
8
8
|
import { buildBrand, buildKit } from '../src/build.mjs';
|
|
9
9
|
import { generateSite, initSite } from '../src/site.mjs';
|
|
@@ -29,17 +29,20 @@ const USAGE =
|
|
|
29
29
|
' branding-engine init scaffold brand.config.json + a `brand` npm script\n' +
|
|
30
30
|
' branding-engine generate [--public <dir>] [--config <file>] favicons + manifest + tokens -> public/\n' +
|
|
31
31
|
' branding-engine build [--config <dir|brand.json>] [--out <dir>] [--only mark,wordmark,sheet,web,cards]\n' +
|
|
32
|
-
' branding-engine kit <slug> <hex> <
|
|
32
|
+
' branding-engine kit <slug> <hex> <glyph> ["Wordmark"] [--font <file>] [--out <dir>] [--only ...]\n' +
|
|
33
|
+
' <glyph> is 1-3 letters or digits, e.g. A, AC, or A3X.';
|
|
33
34
|
|
|
34
35
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
35
36
|
const { pos, opt } = parse(rest);
|
|
36
37
|
|
|
37
38
|
try {
|
|
38
|
-
if (cmd === '
|
|
39
|
+
if (!cmd || cmd === '--help' || cmd === '-h') {
|
|
40
|
+
console.log(USAGE);
|
|
41
|
+
} else if (cmd === 'init') {
|
|
39
42
|
const { created, headSnippet } = initSite({});
|
|
40
43
|
if (created.length) console.log('Created:\n' + created.map((c) => ' ' + c).join('\n'));
|
|
41
44
|
console.log('\nNext: edit brand.config.json (accent, glyph, name), then run `npm run brand`.');
|
|
42
|
-
console.log('\nAdd this to your site <head
|
|
45
|
+
console.log('\nAdd this to your site <head>:\n');
|
|
43
46
|
console.log(headSnippet + '\n');
|
|
44
47
|
} else if (cmd === 'generate') {
|
|
45
48
|
const { written, publicDir, headSnippet } = await generateSite({ config: opt.config, publicDir: opt.public });
|
|
@@ -58,7 +61,7 @@ try {
|
|
|
58
61
|
await buildKit({ slug, hex, glyph, wordmark, font: opt.font, outDir: opt.out, only: opt.only });
|
|
59
62
|
} else {
|
|
60
63
|
console.error(USAGE);
|
|
61
|
-
process.exit(
|
|
64
|
+
process.exit(1);
|
|
62
65
|
}
|
|
63
66
|
} catch (err) {
|
|
64
67
|
console.error(`\n${err.message}`);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Severino Labs example
|
|
2
|
+
|
|
3
|
+
This illustrative example uses the Severino Labs name with a non-production
|
|
4
|
+
sample palette. It runs the full-kit `build` command from
|
|
5
|
+
[`brand.json`](./brand.json):
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"name": "Severino Labs",
|
|
10
|
+
"identity": {
|
|
11
|
+
"slug": "severino-labs",
|
|
12
|
+
"color": "#6D5EF7",
|
|
13
|
+
"deep": "#352A8A",
|
|
14
|
+
"onColor": "#FFFFFF",
|
|
15
|
+
"glyph": "SL",
|
|
16
|
+
"wordmark": "Severino Labs"
|
|
17
|
+
},
|
|
18
|
+
"portrait": "./studio.jpg",
|
|
19
|
+
"cardPalette": {
|
|
20
|
+
"accent": "#9B8CFF",
|
|
21
|
+
"textSoft": "#E3DEFF",
|
|
22
|
+
"textMuted": "#B7AFE8"
|
|
23
|
+
},
|
|
24
|
+
"cards": [
|
|
25
|
+
{
|
|
26
|
+
"file": "social-card.png",
|
|
27
|
+
"width": 1200,
|
|
28
|
+
"height": 630,
|
|
29
|
+
"photoWidth": 420,
|
|
30
|
+
"eyebrow": "Severino Labs",
|
|
31
|
+
"name": "Brand systems, generated.",
|
|
32
|
+
"tagline": "Marks, wordmarks, sheets, web assets, and social cards from one config.",
|
|
33
|
+
"meta": "Illustrative branding-engine example",
|
|
34
|
+
"url": "github.com/jseverino/branding-engine"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Regenerate the committed output from the repository root:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
node bin/cli.mjs build \
|
|
44
|
+
--config examples/severino-labs/brand.json \
|
|
45
|
+
--out examples/severino-labs/generated
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Preview
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+

|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
## Generated files
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
generated/severino-labs/
|
|
62
|
+
├── icons/
|
|
63
|
+
│ ├── apple-touch-icon.png
|
|
64
|
+
│ ├── favicon-32.png
|
|
65
|
+
│ ├── favicon-192.png
|
|
66
|
+
│ ├── favicon.ico
|
|
67
|
+
│ └── favicon.svg
|
|
68
|
+
├── mark/
|
|
69
|
+
│ ├── mark-512.png
|
|
70
|
+
│ ├── mark-1024.png
|
|
71
|
+
│ ├── mark-transparent-dark.png
|
|
72
|
+
│ ├── mark-transparent-light.png
|
|
73
|
+
│ └── mark.svg
|
|
74
|
+
├── sheet/
|
|
75
|
+
│ ├── overview.png
|
|
76
|
+
│ ├── palette.png
|
|
77
|
+
│ ├── sheet-mark.png
|
|
78
|
+
│ └── type-specimen.png
|
|
79
|
+
├── web/
|
|
80
|
+
│ ├── head.html
|
|
81
|
+
│ ├── site.webmanifest
|
|
82
|
+
│ └── tokens.css
|
|
83
|
+
└── wordmark/
|
|
84
|
+
├── wordmark-caps-dark.png
|
|
85
|
+
├── wordmark-caps-light.png
|
|
86
|
+
├── wordmark-caps.svg
|
|
87
|
+
├── wordmark-dark.png
|
|
88
|
+
├── wordmark-light.png
|
|
89
|
+
└── wordmark.svg
|
|
90
|
+
|
|
91
|
+
generated/cards/
|
|
92
|
+
└── social-card.png
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
These files are generated by `branding-engine`; do not edit them by hand.
|