nimiq-branding-cli 1.0.1 → 1.1.0
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/PRINCIPLES.md +153 -0
- package/README.md +10 -0
- package/bin/nq.js +87 -0
- package/package.json +1 -1
package/PRINCIPLES.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# The Nimiq Design Principles
|
|
2
|
+
|
|
3
|
+
**This is the soul of this tool.** Every component in the registry — and every NEW thing
|
|
4
|
+
generated with it — must come from these principles, not just resemble their output.
|
|
5
|
+
|
|
6
|
+
Sources: the **NIMIQ Style Guide (October 2018)**, the
|
|
7
|
+
[A New Visual Identity](https://www.nimiq.com/blog/a-new-visual-identity/) blog post,
|
|
8
|
+
[nimiq-style](https://nimiq.github.io/nimiq-style/), and the
|
|
9
|
+
[Nimiq Design Kit](https://nimiq.dev/design-kit).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why — the identity
|
|
14
|
+
|
|
15
|
+
> *Nimiq is an Inuit word for an object or force that binds things together.*
|
|
16
|
+
|
|
17
|
+
Nimiq sees itself as **the lowest common denominator at the intersection of payment
|
|
18
|
+
technology and accessible interaction with a blockchain** — and aims for **radical
|
|
19
|
+
simplicity in both technology and design**.
|
|
20
|
+
|
|
21
|
+
The brand persona, derived from team and community (not theory):
|
|
22
|
+
|
|
23
|
+
**Helping & caring · Trustworthy & transparent · Conscientious & driven**
|
|
24
|
+
|
|
25
|
+
…which translates into four visual pillars:
|
|
26
|
+
|
|
27
|
+
1. **A base that follows common patterns.**
|
|
28
|
+
2. **Traditional and basic colors with a subtle spin.**
|
|
29
|
+
3. **Warm and round, but straight visual and form language.**
|
|
30
|
+
4. **Subtle complexity and calculated breaks in a clear structure.**
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## The prime directive — radical simplicity
|
|
35
|
+
|
|
36
|
+
> *"Stripping away everything that's not necessary will lead to less cognitive load and
|
|
37
|
+
> eventually to a better experience."*
|
|
38
|
+
|
|
39
|
+
The logo is the proof: every clever N-in-a-hexagon draft was discarded for the **bare
|
|
40
|
+
hexagon** — because two shapes, one inside the other, was already too complex. What
|
|
41
|
+
remained is *"a clean slate, ready for the community to fill."*
|
|
42
|
+
|
|
43
|
+
And the ecosystem framing that defines this CLI's job:
|
|
44
|
+
|
|
45
|
+
> *"We don't want to dictate the way the Nimiq ecosystem looks or feels, but rather
|
|
46
|
+
> provide a boilerplate of our vision, stripped down to the very core, so that there's
|
|
47
|
+
> enough room for others to fill it out with their own ideas."*
|
|
48
|
+
|
|
49
|
+
This tool **is** that boilerplate, made executable.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## The laws
|
|
54
|
+
|
|
55
|
+
### 1. Color — three rules, in order
|
|
56
|
+
1. **Traditional, basic colors with a subtle, unexpected spin.** The ultramarine
|
|
57
|
+
dark-blue (`#1F2348`) carries a touch of violet spreading from the corner; the
|
|
58
|
+
classic red tilts slightly toward magenta. Colors relate to *payments*, not to
|
|
59
|
+
crypto fashion.
|
|
60
|
+
2. **Build on a light stage.** Start from a clean slate (white / `#F8F8F8`); create
|
|
61
|
+
structure with very light, nuanced grays derived from the main colors — this
|
|
62
|
+
underlines the transparent, open, collaborative approach and leaves room for
|
|
63
|
+
others' ideas.
|
|
64
|
+
3. **Color is for accentuated highlights only** — to set focus, and as a *reaction to
|
|
65
|
+
interaction*. Never decorative. (Green = success only. Red = error only.
|
|
66
|
+
Orange = warning. Blue = action/info.)
|
|
67
|
+
|
|
68
|
+
### 2. The gradient is the spin
|
|
69
|
+
A **subtle radial gradient is THE key feature** of the visual identity. Apply it to
|
|
70
|
+
every element that qualifies as a color area — buttons, boxes, backgrounds — anchored
|
|
71
|
+
in the **bottom-right corner** (e.g. `radial-gradient(100% 100% at bottom right,
|
|
72
|
+
#260133, #1F2348)`). This is how "traditional colors" get their spin. On logo
|
|
73
|
+
substrates: white base with a faint color-aligned tint sweeping from the top-right edge.
|
|
74
|
+
|
|
75
|
+
### 3. Form — warm and round, but straight and tangible
|
|
76
|
+
Interfaces are **warm and round, but straight and tangible at the same time** (pill
|
|
77
|
+
buttons and 8px-radius cards, yet crisp edges and exact alignment). **Every element
|
|
78
|
+
has a clear anchor and a relation to the whole.** Pare down the number of visual
|
|
79
|
+
elements until **everything remaining is necessary for the layout to function**.
|
|
80
|
+
|
|
81
|
+
### 4. White space does the structural work
|
|
82
|
+
Achieve hierarchy, separation and cohesion with **white space instead of structural
|
|
83
|
+
elements** like separators or boxes. *"We avoid all forms of decorative elements if we
|
|
84
|
+
can't defend them on a content level."* When in doubt: more breathing room, fewer lines.
|
|
85
|
+
|
|
86
|
+
### 5. Typography — simple, unique, open
|
|
87
|
+
**Muli/Mulish**: a classic sans-serif molded by radical geometric simplicity, with
|
|
88
|
+
unique details that never compromise that simplicity — chosen over Roboto/Open Sans
|
|
89
|
+
because Nimiq could *own* it, and because it's open source (community must be able to
|
|
90
|
+
reproduce everything). **Fira Mono** for technical content — addresses, keys, numbers —
|
|
91
|
+
optimized for readability, light-footed in non-code environments.
|
|
92
|
+
|
|
93
|
+
### 6. Calculated breaks — the element of surprise
|
|
94
|
+
Radical simplicity is not sterility:
|
|
95
|
+
|
|
96
|
+
> *"For this reason we embrace subtle complexity — an element of surprise,
|
|
97
|
+
> well-defined breaks in a clear structure — something that makes the experience
|
|
98
|
+
> memorable."*
|
|
99
|
+
|
|
100
|
+
One surprise per experience: the gradient's diagonal, an identicon's character, the
|
|
101
|
+
honeycomb peeking through. **"Driven" translates into adding that bit of sophistication
|
|
102
|
+
and friction that makes the difference between convenience and fascination.** Friction
|
|
103
|
+
belongs only where it protects something critical (a passphrase, money).
|
|
104
|
+
|
|
105
|
+
### 7. Familiar in the basics, honest in the details
|
|
106
|
+
> *"We use common, learned patterns for critical interactions with Nimiq wherever
|
|
107
|
+
> possible. We're familiar in the basics, but surprising in the details. Nevertheless,
|
|
108
|
+
> if in doubt, everything we do follows intuitive patterns. If we don't follow best
|
|
109
|
+
> practices, we follow logic, nature or human behavior."*
|
|
110
|
+
|
|
111
|
+
### 8. Reproducibility is a brand value
|
|
112
|
+
> *"We want to encourage the community to use our visual language as a foundation for
|
|
113
|
+
> their own projects. … everything we create visually needs to be easily accessible and
|
|
114
|
+
> reproducible, being able to act as a boilerplate by virtue of its simplicity."*
|
|
115
|
+
|
|
116
|
+
This is why this CLI pixel-verifies every component against the real upstream and ships
|
|
117
|
+
the team's real assets instead of hand-made approximations. Reproduction *is* the brand
|
|
118
|
+
working as designed.
|
|
119
|
+
|
|
120
|
+
### 9. The logo is sacred ground
|
|
121
|
+
The hexagon signet is **universal** — anyone in the ecosystem may use it and build their
|
|
122
|
+
own mark on top of it. The **signet + wordmark combination is reserved** and must not be
|
|
123
|
+
altered. Clear space: the width of the letter N (horizontal) / half a hexagon (signet).
|
|
124
|
+
Monochrome versions for busy or colored backgrounds. Never reconstruct the geometry —
|
|
125
|
+
use the shipped files (`nq assets search logo`).
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## The generation checklist
|
|
130
|
+
|
|
131
|
+
Before any NEW component or design ships, it must answer **yes** to all of these:
|
|
132
|
+
|
|
133
|
+
- [ ] Could anything be removed without the layout failing? (If yes → remove it.)
|
|
134
|
+
- [ ] Does it sit on a light stage, structured by white space rather than boxes/lines?
|
|
135
|
+
- [ ] Is every color either a main color with the radial-gradient spin, a light nuanced
|
|
136
|
+
gray derived from main colors, or a semantic highlight reacting to focus/interaction?
|
|
137
|
+
- [ ] Are color areas gradient-built (bottom-right radial), not flat fills?
|
|
138
|
+
- [ ] Is it warm and round (pills, soft radii) yet straight and tangible (crisp anchors,
|
|
139
|
+
exact alignment, clear relation to the whole)?
|
|
140
|
+
- [ ] Is the typography Muli/Mulish (UI) and Fira Mono (technical values) only?
|
|
141
|
+
- [ ] Is there at most ONE calculated break / element of surprise, and can it be
|
|
142
|
+
defended on a content level?
|
|
143
|
+
- [ ] Are the basics a common, learned pattern? (Surprise lives in details, never in
|
|
144
|
+
critical interactions.)
|
|
145
|
+
- [ ] Does it use the team's real assets (`nq assets`) — never redrawn logos, icons or art?
|
|
146
|
+
- [ ] Is it reproducible — plain HTML/CSS or standard Vue, no exotic dependencies,
|
|
147
|
+
verifiable by `nq verify`?
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
*Run `nq principles` to print this document. `nq new <name>` scaffolds a component with
|
|
152
|
+
this checklist embedded — a component is not done until the checklist and the pixel
|
|
153
|
+
verification both pass.*
|
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ Nimiq apps before it ships, plus the team's real asset library (logos, icons, fl
|
|
|
8
8
|
> real shipped files or faithful ports of their open-source components, never hand-drawn
|
|
9
9
|
> approximations.
|
|
10
10
|
|
|
11
|
+
## The soul of the tool
|
|
12
|
+
|
|
13
|
+
Everything here flows from the **[Nimiq Design Principles](PRINCIPLES.md)** — distilled from
|
|
14
|
+
the NIMIQ Style Guide (October 2018) and the
|
|
15
|
+
[A New Visual Identity](https://www.nimiq.com/blog/a-new-visual-identity/) essay: radical
|
|
16
|
+
simplicity, a light stage structured by white space, traditional colors with the radial-gradient
|
|
17
|
+
spin, warm-and-round-yet-tangible form, and one calculated break per experience. `nq principles`
|
|
18
|
+
prints them; `nq new <name>` scaffolds a component with the 10-point principles checklist
|
|
19
|
+
embedded — a component isn't done until the checklist and the pixel verification both pass.
|
|
20
|
+
|
|
11
21
|
## Install
|
|
12
22
|
|
|
13
23
|
```bash
|
package/bin/nq.js
CHANGED
|
@@ -24,6 +24,9 @@ Usage:
|
|
|
24
24
|
nq assets search <term> Search assets incl. the 323 nimiq-icons + 422 hexagon flags
|
|
25
25
|
nq assets add <name...> Copy official asset(s) into ./nimiq/assets/
|
|
26
26
|
(icon:<name> extracts from nimiq-icons, flag:<cc> from nimiq-flags)
|
|
27
|
+
nq principles Print the Nimiq design principles — the soul of this tool
|
|
28
|
+
nq new <name> Scaffold a new registry component with the principles
|
|
29
|
+
checklist + verification contract embedded
|
|
27
30
|
nq verify <component|all> Render the html variant and diff against the reference PNG
|
|
28
31
|
nq help This message
|
|
29
32
|
|
|
@@ -239,6 +242,88 @@ async function cmdTokens() {
|
|
|
239
242
|
console.log(await readFile(join(ROOT, 'assets', 'tokens.md'), 'utf8'));
|
|
240
243
|
}
|
|
241
244
|
|
|
245
|
+
async function cmdPrinciples() {
|
|
246
|
+
console.log(await readFile(join(ROOT, 'PRINCIPLES.md'), 'utf8'));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const CHECKLIST = [
|
|
250
|
+
'remove-everything-unnecessary: could anything be removed without the layout failing?',
|
|
251
|
+
'light-stage: white/#F8F8F8 base, structure via white space + nuanced grays, not boxes/lines',
|
|
252
|
+
'color-rules: main colors w/ gradient spin | light grays | semantic highlights on interaction only',
|
|
253
|
+
'gradients: color areas use the bottom-right radial gradient, never flat fills',
|
|
254
|
+
'form: warm and round yet straight and tangible; every element anchored, related to the whole',
|
|
255
|
+
'type: Mulish for UI, Fira Mono for technical values, nothing else',
|
|
256
|
+
'one-break: at most ONE calculated surprise, defensible on a content level',
|
|
257
|
+
'learned-patterns: basics follow common patterns; surprise lives in details only',
|
|
258
|
+
'real-assets: team-shipped files via nq assets — no redrawn logos/icons/art',
|
|
259
|
+
'reproducible: plain HTML/CSS or standard Vue, passes nq verify',
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
async function cmdNew(name, flags) {
|
|
263
|
+
if (!name || !/^[a-z][a-z0-9-]*$/.test(name)) throw new Error('nq new <kebab-case-name>');
|
|
264
|
+
const dir = join(ROOT, 'registry', 'components', name);
|
|
265
|
+
if (existsSync(dir)) throw new Error(`component "${name}" already exists`);
|
|
266
|
+
const pascal = name.split('-').map(w => w[0].toUpperCase() + w.slice(1)).join('');
|
|
267
|
+
await mkdir(join(dir, 'html'), { recursive: true });
|
|
268
|
+
await mkdir(join(dir, 'truth'), { recursive: true });
|
|
269
|
+
await mkdir(join(dir, 'vue'), { recursive: true });
|
|
270
|
+
|
|
271
|
+
const meta = {
|
|
272
|
+
name,
|
|
273
|
+
purpose: 'TODO: one sentence — what it is and where Nimiq uses it',
|
|
274
|
+
category: 'misc',
|
|
275
|
+
variants: ['vue', 'html'],
|
|
276
|
+
props: [],
|
|
277
|
+
cssFiles: ['css/legacy/nimiq-style.min.css'],
|
|
278
|
+
assetFiles: [],
|
|
279
|
+
dependsOn: [],
|
|
280
|
+
npmDeps: [],
|
|
281
|
+
verified: false,
|
|
282
|
+
verify: { viewport: { width: 600, height: 400 }, selector: `.${name}`, maxDiffPct: 0.5, settleMs: 250 },
|
|
283
|
+
principles: Object.fromEntries(CHECKLIST.map(c => [c.split(':')[0], false])),
|
|
284
|
+
notes: 'Scaffolded by nq new. Cite the source (upstream file, real asset, or screenshot reference) here. A component is DONE when every principles flag is true AND nq verify passes.',
|
|
285
|
+
};
|
|
286
|
+
await writeFile(join(dir, 'meta.json'), JSON.stringify(meta, null, 2) + '\n');
|
|
287
|
+
|
|
288
|
+
const pageChrome = (title, cssHref) => `<!DOCTYPE html>
|
|
289
|
+
<html lang="en">
|
|
290
|
+
<head>
|
|
291
|
+
<meta charset="utf-8">
|
|
292
|
+
<title>${title}</title>
|
|
293
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
294
|
+
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;600;700;800&display=swap" rel="stylesheet">
|
|
295
|
+
<link rel="stylesheet" href="../../../../assets/css/legacy/nimiq-style.min.css">
|
|
296
|
+
<link rel="stylesheet" href="${cssHref}">
|
|
297
|
+
<style>
|
|
298
|
+
html, body { margin: 0; padding: 0; background: #fff; }
|
|
299
|
+
body { font-family: 'Mulish', 'Muli', system-ui, sans-serif; }
|
|
300
|
+
</style>
|
|
301
|
+
</head>
|
|
302
|
+
<body>
|
|
303
|
+
<div class="${name}">
|
|
304
|
+
<!-- TODO: same markup as html/${name}.html — keep truth/demo/snippet identical -->
|
|
305
|
+
</div>
|
|
306
|
+
</body>
|
|
307
|
+
</html>
|
|
308
|
+
`;
|
|
309
|
+
await writeFile(join(dir, 'truth', 'truth.html'), pageChrome(`${name} — truth (cite your source!)`, `../html/${name}.css`));
|
|
310
|
+
await writeFile(join(dir, 'html', 'demo.html'), pageChrome(name, `${name}.css`));
|
|
311
|
+
await writeFile(join(dir, 'html', `${name}.html`), `<!-- ${name} — TODO describe. Requires ${name}.css + nimiq-style.min.css.\n Source: TODO (upstream file / real asset / reference screenshot). -->\n<div class="${name}">\n <!-- TODO -->\n</div>\n`);
|
|
312
|
+
await writeFile(join(dir, 'html', `${name}.css`), `/* ${name} — all selectors namespaced under .${name}.\n Principles: light stage, white-space structure, bottom-right radial gradients,\n Mulish/Fira Mono, one calculated break max. Run: nq principles */\n.${name} {\n font-family: 'Mulish', sans-serif;\n}\n`);
|
|
313
|
+
await writeFile(join(dir, 'vue', `${pascal}.vue`), `<script setup lang="ts">\n// ${pascal} — port faithfully; inline small helpers; record npm deps in meta.json\n</script>\n\n<template>\n <div class="${name}">\n <!-- TODO -->\n </div>\n</template>\n\n<style scoped>\n.${name} {\n font-family: 'Mulish', sans-serif;\n}\n</style>\n`);
|
|
314
|
+
|
|
315
|
+
console.log(`+ registry/components/${name}/ scaffolded\n`);
|
|
316
|
+
console.log('The principles gate (all must become true in meta.json):');
|
|
317
|
+
for (const c of CHECKLIST) console.log(' [ ] ' + c);
|
|
318
|
+
console.log(`\nWorkflow:
|
|
319
|
+
1. Build truth/truth.html from a REAL source (upstream code, real asset, or live screenshot) — cite it in meta.notes.
|
|
320
|
+
2. node scripts/snap.mjs ${name} (truth -> reference.png; eyeball it)
|
|
321
|
+
3. Build html/${name}.html + demo.html + vue/${pascal}.vue with identical markup.
|
|
322
|
+
4. node scripts/verify.mjs ${name} (must pass at <= 0.5% diff)
|
|
323
|
+
5. Flip the principles flags + verified:true, then: node scripts/build-index.mjs
|
|
324
|
+
Read the soul of the tool first: nq principles`);
|
|
325
|
+
}
|
|
326
|
+
|
|
242
327
|
async function cmdVerify(target) {
|
|
243
328
|
const { verify } = await import(join(ROOT, 'scripts', 'verify.mjs'));
|
|
244
329
|
const names = target === 'all' || !target
|
|
@@ -264,6 +349,8 @@ try {
|
|
|
264
349
|
case 'add': await cmdAdd(rest, flags); break;
|
|
265
350
|
case 'init': await cmdInit(flags); break;
|
|
266
351
|
case 'tokens': await cmdTokens(); break;
|
|
352
|
+
case 'principles': await cmdPrinciples(); break;
|
|
353
|
+
case 'new': await cmdNew(rest[0], flags); break;
|
|
267
354
|
case 'assets': await cmdAssets(rest[0], rest.slice(1), flags); break;
|
|
268
355
|
case 'verify': await cmdVerify(rest[0]); break;
|
|
269
356
|
default: console.log(HELP);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nimiq-branding-cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "nq \u2014 pixel-verified Nimiq UI component registry + CLI. 39 components (Vue 3 + plain HTML) diffed against the real Nimiq apps, plus the team's real asset library. Unofficial community tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|