nimiq-branding-cli 1.1.0 → 1.2.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.
Files changed (74) hide show
  1. package/.audit/report.json +71 -0
  2. package/.audit/report.md +33 -0
  3. package/AUDIT.md +60 -0
  4. package/LINT.md +158 -0
  5. package/README.md +47 -4
  6. package/align/canonical.json +44 -0
  7. package/audit/learnings.json +51 -0
  8. package/bin/nq.js +90 -5
  9. package/hooks/ci-additions.md +53 -0
  10. package/hooks/pre-commit +10 -0
  11. package/hooks/session-start.sh +3 -0
  12. package/hooks/stack-align.yml +75 -0
  13. package/package.json +4 -4
  14. package/registry/SKILL-BLOCK.md +20 -0
  15. package/registry/components/account-header/meta.json +11 -1
  16. package/registry/components/account-list/meta.json +9 -1
  17. package/registry/components/account-ring/meta.json +9 -1
  18. package/registry/components/address-display/html/address-display.css +17 -13
  19. package/registry/components/address-display/html/demo.html +8 -10
  20. package/registry/components/address-display/meta.json +37 -7
  21. package/registry/components/address-display/vue/AddressDisplay.vue +11 -9
  22. package/registry/components/address-input/meta.json +53 -10
  23. package/registry/components/amount/meta.json +60 -11
  24. package/registry/components/amount-input/meta.json +55 -10
  25. package/registry/components/amount-with-fee/meta.json +58 -11
  26. package/registry/components/app-showcase-card/meta.json +17 -4
  27. package/registry/components/backup-banner/meta.json +29 -5
  28. package/registry/components/balance-distribution/meta.json +25 -5
  29. package/registry/components/buttons/meta.json +38 -7
  30. package/registry/components/card/meta.json +20 -4
  31. package/registry/components/close-button/meta.json +19 -4
  32. package/registry/components/consensus-icon/meta.json +26 -5
  33. package/registry/components/copyable/meta.json +25 -5
  34. package/registry/components/fiat-amount/meta.json +49 -9
  35. package/registry/components/flag-hex/html/demo.html +55 -0
  36. package/registry/components/flag-hex/html/flag-hex.css +21 -0
  37. package/registry/components/flag-hex/html/flag-hex.html +38 -0
  38. package/registry/components/flag-hex/meta.json +43 -0
  39. package/registry/components/flag-hex/vue/FlagHex.vue +98 -0
  40. package/registry/components/hero-section/meta.json +9 -4
  41. package/registry/components/honeycomb-band/meta.json +47 -9
  42. package/registry/components/identicon/meta.json +8 -1
  43. package/registry/components/label-input/meta.json +49 -9
  44. package/registry/components/loading-spinner/meta.json +19 -4
  45. package/registry/components/page-body/meta.json +23 -5
  46. package/registry/components/page-footer/meta.json +22 -5
  47. package/registry/components/page-header/meta.json +43 -8
  48. package/registry/components/payment-info-line/meta.json +10 -1
  49. package/registry/components/price-chart/meta.json +67 -11
  50. package/registry/components/qr-code/meta.json +22 -5
  51. package/registry/components/search-bar/meta.json +25 -5
  52. package/registry/components/select-bar/meta.json +37 -7
  53. package/registry/components/slider-toggle/meta.json +45 -8
  54. package/registry/components/small-page/meta.json +19 -4
  55. package/registry/components/status-alert/meta.json +37 -7
  56. package/registry/components/status-screen/meta.json +70 -13
  57. package/registry/components/swap-balance-bar/meta.json +8 -1
  58. package/registry/components/timer/meta.json +54 -10
  59. package/registry/components/toast-notification/meta.json +49 -9
  60. package/registry/components/tooltip/meta.json +69 -13
  61. package/registry/components/transaction-list/meta.json +9 -1
  62. package/registry/index.json +10 -0
  63. package/schemas/nimiq-stack.v1.json +103 -0
  64. package/scripts/align.mjs +435 -0
  65. package/scripts/apply-safe-bump.mjs +47 -0
  66. package/scripts/audit.mjs +247 -0
  67. package/scripts/bootstrap-provenance.mjs +126 -0
  68. package/scripts/hooks.mjs +152 -0
  69. package/scripts/lint.mjs +333 -0
  70. package/scripts/scaffold.mjs +391 -0
  71. package/scripts/sync-skill.mjs +84 -0
  72. package/test/align.test.mjs +166 -0
  73. package/test/scaffold.test.mjs +103 -0
  74. package/upstream-pins.json +35 -0
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>flag-hex — demo</title>
6
+ <link rel="preconnect" href="https://fonts.googleapis.com">
7
+ <link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;600;700;800&display=swap" rel="stylesheet">
8
+ <link rel="stylesheet" href="flag-hex.css">
9
+ <style>
10
+ html,body{margin:0;background:#fff}
11
+ body{font-family:"Mulish","Muli",system-ui,sans-serif;display:flex;gap:14px;align-items:center;justify-content:center;min-height:200px}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <!-- flag-hex — a flag clipped into the Nimiq rounded hexagon. Requires flag-hex.css.
16
+
17
+ Real use: point the <image> at a flag-icons file (MIT; flags are public domain):
18
+ <image href="/flags/fr.svg" x=".." y=".." width=".." height=".."
19
+ preserveAspectRatio="xMidYMid slice" clip-path="url(#fh-clip)"/>
20
+
21
+ The x/y/width/height define the IMAGE BOX. Size the box to the flag's OWN
22
+ aspect ratio and to COVER the 20x18 hexagon, so it fills with no white
23
+ letterbox (browsers letterbox slice on referenced SVGs). For a square (1:1)
24
+ flag the box below is ~ -1,-1.8 21.6x21.6. For a 4:3 flag use ~ -3,-0.7
25
+ 25.9x19.4. Pan/zoom by shifting/scaling the box (keep scale >= (10+|pan|)/11
26
+ or the opposite edge uncovers).
27
+
28
+ Each example inlines a simple flag (rects) so the file is self-contained;
29
+ swap the <g> for an <image href> in real use. clipPath ids must be unique. -->
30
+
31
+ <!-- Germany (horizontal tricolor) — fills cleanly, gray edge defines the shape -->
32
+ <svg class="flag-hex" viewBox="0 0 20 18" role="img" aria-label="German">
33
+ <defs><clipPath id="fh-de"><path d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/></clipPath></defs>
34
+ <g clip-path="url(#fh-de)">
35
+ <rect x="-1" y="-1.8" width="21.6" height="7.2" fill="#000"/>
36
+ <rect x="-1" y="5.4" width="21.6" height="7.2" fill="#d00"/>
37
+ <rect x="-1" y="12.6" width="21.6" height="7.2" fill="#ffce00"/>
38
+ </g>
39
+ <path class="fh-edge" d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/>
40
+ </svg>
41
+
42
+ <!-- France (vertical tricolor) — the pointed L/R tips pinch the side stripes;
43
+ the overscan + edge keep it reading. A near-square 1:1 source is used. -->
44
+ <svg class="flag-hex" viewBox="0 0 20 18" role="img" aria-label="French">
45
+ <defs><clipPath id="fh-fr"><path d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/></clipPath></defs>
46
+ <g clip-path="url(#fh-fr)">
47
+ <rect x="-1" y="-1.8" width="7.2" height="21.6" fill="#002395"/>
48
+ <rect x="6.2" y="-1.8" width="7.2" height="21.6" fill="#fff"/>
49
+ <rect x="13.4" y="-1.8" width="7.2" height="21.6" fill="#ed2939"/>
50
+ </g>
51
+ <path class="fh-edge" d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/>
52
+ </svg>
53
+
54
+ </body>
55
+ </html>
@@ -0,0 +1,21 @@
1
+ /* flag-hex — a country flag clipped into the Nimiq rounded hexagon.
2
+ Requires nimiq-style.min.css only for the page font; the component is
3
+ self-contained SVG. Namespaced under .flag-hex. */
4
+
5
+ .flag-hex {
6
+ display: block;
7
+ width: 56px; /* size to taste; aspect stays 20:18 (height = width * 0.9) */
8
+ height: 50.4px;
9
+ /* let the gray edge stroke reach past the viewBox at the tips (not clipped) */
10
+ overflow: visible;
11
+ }
12
+
13
+ /* The flag image fills the box; the box is sized to the flag's aspect (see the
14
+ x/y/width/height on each <image> in the markup) so it covers with no
15
+ letterbox. The gray edge path defines the hexagon shape + the tips. */
16
+ .flag-hex .fh-edge {
17
+ fill: none;
18
+ stroke: rgba(31, 35, 72, 0.22);
19
+ stroke-width: 0.4;
20
+ stroke-linejoin: round;
21
+ }
@@ -0,0 +1,38 @@
1
+ <!-- flag-hex — a flag clipped into the Nimiq rounded hexagon. Requires flag-hex.css.
2
+
3
+ Real use: point the <image> at a flag-icons file (MIT; flags are public domain):
4
+ <image href="/flags/fr.svg" x=".." y=".." width=".." height=".."
5
+ preserveAspectRatio="xMidYMid slice" clip-path="url(#fh-clip)"/>
6
+
7
+ The x/y/width/height define the IMAGE BOX. Size the box to the flag's OWN
8
+ aspect ratio and to COVER the 20x18 hexagon, so it fills with no white
9
+ letterbox (browsers letterbox slice on referenced SVGs). For a square (1:1)
10
+ flag the box below is ~ -1,-1.8 21.6x21.6. For a 4:3 flag use ~ -3,-0.7
11
+ 25.9x19.4. Pan/zoom by shifting/scaling the box (keep scale >= (10+|pan|)/11
12
+ or the opposite edge uncovers).
13
+
14
+ Each example inlines a simple flag (rects) so the file is self-contained;
15
+ swap the <g> for an <image href> in real use. clipPath ids must be unique. -->
16
+
17
+ <!-- Germany (horizontal tricolor) — fills cleanly, gray edge defines the shape -->
18
+ <svg class="flag-hex" viewBox="0 0 20 18" role="img" aria-label="German">
19
+ <defs><clipPath id="fh-de"><path d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/></clipPath></defs>
20
+ <g clip-path="url(#fh-de)">
21
+ <rect x="-1" y="-1.8" width="21.6" height="7.2" fill="#000"/>
22
+ <rect x="-1" y="5.4" width="21.6" height="7.2" fill="#d00"/>
23
+ <rect x="-1" y="12.6" width="21.6" height="7.2" fill="#ffce00"/>
24
+ </g>
25
+ <path class="fh-edge" d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/>
26
+ </svg>
27
+
28
+ <!-- France (vertical tricolor) — the pointed L/R tips pinch the side stripes;
29
+ the overscan + edge keep it reading. A near-square 1:1 source is used. -->
30
+ <svg class="flag-hex" viewBox="0 0 20 18" role="img" aria-label="French">
31
+ <defs><clipPath id="fh-fr"><path d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/></clipPath></defs>
32
+ <g clip-path="url(#fh-fr)">
33
+ <rect x="-1" y="-1.8" width="7.2" height="21.6" fill="#002395"/>
34
+ <rect x="6.2" y="-1.8" width="7.2" height="21.6" fill="#fff"/>
35
+ <rect x="13.4" y="-1.8" width="7.2" height="21.6" fill="#ed2939"/>
36
+ </g>
37
+ <path class="fh-edge" d="M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688"/>
38
+ </svg>
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "flag-hex",
3
+ "purpose": "A country flag clipped into the Nimiq rounded hexagon (the same shape as the identicon) — for language / country pickers. Handles the overscan, per-flag pan/zoom, aspect-correct fill (no letterbox), and one consistent gray edge so every flag reads cleanly including the pointed left/right tips.",
4
+ "category": "data-display",
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
9
+ "props": [
10
+ {
11
+ "name": "url",
12
+ "type": "String",
13
+ "required": true,
14
+ "description": "Flag image URL. Vendor flags from `flag-icons` (MIT; flags are public domain). Use the 1x1 (square) file for most; the 4x3 file for wide/striped flags (see fit.aspect)."
15
+ },
16
+ {
17
+ "name": "fit",
18
+ "type": "{ scale?: number; dx?: number; dy?: number; aspect?: number }",
19
+ "required": false,
20
+ "description": "Pan/zoom the flag inside the hexagon. aspect = flag width/height (default 1 for 1x1 files, 4/3 for 4x3). scale zooms; dx/dy pan in SVG units (+x right, +y down). Keep scale >= (10 + max(|dx|,|dy|)) / 11 or the opposite edge uncovers (white gap)."
21
+ }
22
+ ],
23
+ "cssFiles": [],
24
+ "assetFiles": [],
25
+ "dependsOn": [],
26
+ "npmDeps": [],
27
+ "verified": false,
28
+ "verify": {
29
+ "viewport": {
30
+ "width": 300,
31
+ "height": 200
32
+ },
33
+ "selector": ".flag-hex",
34
+ "maxDiffPct": 0.5,
35
+ "settleMs": 200
36
+ },
37
+ "notes": "ORIGINAL Nimiq-brand composition (not an upstream port), so `verified:false` — it reuses the VERBATIM Nimiq hexagon path (M19.964 8.156...). Hard-won rules baked in: (1) The hexagon is POINTED left/right — a naive clip crops vertical-stripe flags (France, Mexico) into slivers and leaves white at the tips; the image box overscans past the viewBox and overflow:visible lets the edge stroke reach the points. (2) preserveAspectRatio='slice' on a REFERENCED SVG <image> is letterboxed by browsers -> white line above/below; the fix is to size the image box to the flag's OWN aspect ratio (fit.aspect) so it covers with no letterbox. (3) flag-icons 1x1 (square) files STRETCH wide flags — e.g. the US canton becomes ~76% width; use the 4x3 source + fit.aspect=4/3 for the US. (4) Panning needs a matching zoom: scale >= (10+|pan|)/11 or the opposite edge uncovers. (5) Per-flag fit examples that took real tuning: US {aspect:4/3}, China {scale:1.2,dx:2.2,dy:2.8} (nudge the 5 stars into view), Korea {scale:0.84} (white margin around the taeguk/trigrams). (6) Every flag gets ONE faint gray edge rgba(31,35,72,0.22) stroke-width 0.4 so the shape + tips always read against a white page — uniform reads cleaner than per-flag colors. Flags are public-domain artwork; this snippet inlines simple tricolors (drawn as rects) so it is self-contained — swap the <g> for an <image href> pointing at your vendored flag file in real use. Pairs with the `identicon` component (same hexagon).",
38
+ "source": {
39
+ "repo": "original",
40
+ "ref": null,
41
+ "files": []
42
+ }
43
+ }
@@ -0,0 +1,98 @@
1
+ <!--
2
+ FlagHex — a country flag clipped into the Nimiq rounded hexagon (the same
3
+ shape as the @nimiq/iqons identicon), for language / country pickers.
4
+
5
+ WHY THIS COMPONENT EXISTS (hard-won, see meta.json notes):
6
+ - The Nimiq hexagon is POINTED left/right, so a naive clip crops
7
+ vertical-stripe flags (France, Mexico) into slivers and leaves white at
8
+ the tips. This handles the overscan + edge so every flag reads cleanly.
9
+ - `preserveAspectRatio="slice"` on a *referenced SVG* <image> is
10
+ letterboxed by browsers -> white gaps. Fix: size the image box to the
11
+ flag's OWN aspect ratio (`fit.aspect`) so it covers with no letterbox.
12
+ - Wide/striped flags (US) must use the natural 4:3 source; the square (1x1)
13
+ version stretches the canton to ~76% width.
14
+ - Panning a flag uncovers the opposite edge unless you also zoom:
15
+ scale >= (10 + |pan|) / 11. `fit.scale` + `fit.dx/dy` repositions
16
+ off-center symbols (US stripes, China stars) while staying covered.
17
+ - Every flag gets ONE faint gray edge so the shape + tips always read; the
18
+ svg is overflow:visible so the stroke isn't clipped at the points.
19
+
20
+ Flags are PUBLIC DOMAIN; vendor them from `flag-icons` (MIT) into your app
21
+ and pass the resolved URL via `url`. Use the 1x1 file for most, the 4x3 file
22
+ (+ fit.aspect = 4/3) for wide flags.
23
+
24
+ props:
25
+ url string — flag SVG/PNG url (consumer-provided)
26
+ fit? { scale?, dx?, dy?, aspect? } — pan/zoom; aspect = flag w/h (default 1)
27
+ -->
28
+ <script lang="ts">
29
+ // Module-scope counter -> a unique clipPath id per instance (no prop needed).
30
+ let _flagHexCounter = 0
31
+ </script>
32
+
33
+ <script setup lang="ts">
34
+ import { computed } from 'vue'
35
+
36
+ const props = defineProps<{
37
+ url: string
38
+ fit?: { scale?: number; dx?: number; dy?: number; aspect?: number }
39
+ }>()
40
+
41
+ const uid = `flaghex-${(_flagHexCounter += 1)}`
42
+
43
+ // Verbatim Nimiq hexagon path (rounded corners), 20x18 viewBox — pointed L/R.
44
+ const HEX =
45
+ 'M19.964 8.156 15.758.844A1.69 1.69 0 0014.299 0H5.887c-.6 0-1.156.32-1.456.844L.225 8.156c-.3.523-.3 1.165 0 1.688l4.206 7.312c.3.523.856.844 1.456.844h8.412c.6 0 1.156-.32 1.456-.844l4.206-7.312a1.69 1.69 0 00.003-1.688'
46
+
47
+ // The image box MATCHES the flag's aspect ratio (a = w/h) and is sized to
48
+ // COVER the 20x18 hexagon (+ overscan). Box-aspect == flag-aspect means the
49
+ // flag fills with no letterbox regardless of how the browser resolves
50
+ // preserveAspectRatio on the referenced SVG. scale zooms; dx/dy pan (+x right).
51
+ const box = computed(() => {
52
+ const a = props.fit?.aspect ?? 1
53
+ const s = props.fit?.scale ?? 1
54
+ const dx = props.fit?.dx ?? 0
55
+ const dy = props.fit?.dy ?? 0
56
+ const OVER = 1.08
57
+ const w = Math.max(20, 18 * a) * OVER * s
58
+ const h = w / a
59
+ return { x: 10 + dx - w / 2, y: 9 + dy - h / 2, w, h }
60
+ })
61
+ </script>
62
+
63
+ <template>
64
+ <svg class="flag-hex" viewBox="0 0 20 18" aria-hidden="true">
65
+ <defs>
66
+ <clipPath :id="uid">
67
+ <path :d="HEX" />
68
+ </clipPath>
69
+ </defs>
70
+ <image
71
+ :href="url"
72
+ :x="box.x"
73
+ :y="box.y"
74
+ :width="box.w"
75
+ :height="box.h"
76
+ preserveAspectRatio="xMidYMid slice"
77
+ :clip-path="`url(#${uid})`"
78
+ />
79
+ <!-- One consistent gray edge on every flag (defines the shape + the tips). -->
80
+ <path
81
+ :d="HEX"
82
+ fill="none"
83
+ stroke="rgba(31,35,72,0.22)"
84
+ stroke-width="0.4"
85
+ stroke-linejoin="round"
86
+ />
87
+ </svg>
88
+ </template>
89
+
90
+ <style scoped>
91
+ .flag-hex {
92
+ display: block;
93
+ width: 100%;
94
+ height: 100%;
95
+ /* let the edge stroke reach past the viewBox at the tips (not clipped) */
96
+ overflow: visible;
97
+ }
98
+ </style>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hero-section",
3
- "purpose": "nimiq.com home hero: deep purple radial-gradient sky with floating translucent hexagons, white 800-weight 'Universal Money for Independent Individuals' headline, lavender subtitle, light-blue 'Create Wallet \u2197' pill, WORKS WITH partner row, and a dotted-globe white arc cresting at the bottom.",
3
+ "purpose": "nimiq.com home hero: deep purple radial-gradient sky with floating translucent hexagons, white 800-weight 'Universal Money for Independent Individuals' headline, lavender subtitle, light-blue 'Create Wallet ' pill, WORKS WITH partner row, and a dotted-globe white arc cresting at the bottom.",
4
4
  "category": "marketing",
5
5
  "variants": [
6
6
  "vue",
@@ -48,9 +48,14 @@
48
48
  "maxDiffPct": 0.5,
49
49
  "settleMs": 400
50
50
  },
51
- "notes": "REAL-ASSETS-ONLY (2026-06-12): sky = vendored img/gods-light.webp ('Nimiq Gods Rays Background', nimiq.com original) rendered absolute inset-0 with blur(60px) brightness(.8) over a #1F2348 base, exactly like the live site; globe continents = vendored img/world-map.svg (nimiq.com /_ipx/_/assets/images/home-hero/map.svg, 977x362) placed via <image> at x25 y545 1150x426 on the white dome (r1600 circle) with the blue atmosphere halo strokes. Partner row uses the real white CPL/BTC-Lightning/NAKA/Polygon logos (nimiq.com prismic originals, inlined SVG). Floating hexagons + dome halo remain CSS/SVG-built (no shipped asset exists \u2014 the live site draws them in code too). nq add copies assetFiles to ./nimiq/assets/img/; snippet hrefs assume ../../../../assets/ which page builders rewrite to assets/. Alternative generated hexagon-dot globe: scripts/gen-hex-map.mjs.",
51
+ "notes": "REAL-ASSETS-ONLY (2026-06-12): sky = vendored img/gods-light.webp ('Nimiq Gods Rays Background', nimiq.com original) rendered absolute inset-0 with blur(60px) brightness(.8) over a #1F2348 base, exactly like the live site; globe continents = vendored img/world-map.svg (nimiq.com /_ipx/_/assets/images/home-hero/map.svg, 977x362) placed via <image> at x25 y545 1150x426 on the white dome (r1600 circle) with the blue atmosphere halo strokes. Partner row uses the real white CPL/BTC-Lightning/NAKA/Polygon logos (nimiq.com prismic originals, inlined SVG). Floating hexagons + dome halo remain CSS/SVG-built (no shipped asset exists the live site draws them in code too). nq add copies assetFiles to ./nimiq/assets/img/; snippet hrefs assume ../../../../assets/ which page builders rewrite to assets/. Alternative generated hexagon-dot globe: scripts/gen-hex-map.mjs.",
52
52
  "assetFiles": [
53
53
  "img/world-map.svg",
54
54
  "img/gods-light.webp"
55
- ]
56
- }
55
+ ],
56
+ "source": {
57
+ "repo": "nimiq-com",
58
+ "ref": null,
59
+ "files": []
60
+ }
61
+ }
@@ -2,15 +2,45 @@
2
2
  "name": "honeycomb-band",
3
3
  "purpose": "nimiq.com community honeycomb band: blue 'Community →' pill centered above the real nimiq.com fading honeycomb background (bg-hexagons.svg) with three brand-colored social hexagons overlaid (YouTube red, X black, Facebook blue).",
4
4
  "category": "marketing",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "ctaLabel", "type": "string", "default": "Community", "description": "Label of the blue pill above the mosaic." },
8
- { "name": "ctaHref", "type": "string", "default": "#", "description": "Link target of the pill." },
9
- { "name": "youtubeHref", "type": "string", "default": "#", "description": "Link target of the red YouTube hexagon." },
10
- { "name": "xHref", "type": "string", "default": "#", "description": "Link target of the black X hexagon." },
11
- { "name": "facebookHref", "type": "string", "default": "#", "description": "Link target of the blue Facebook hexagon." }
10
+ {
11
+ "name": "ctaLabel",
12
+ "type": "string",
13
+ "default": "Community",
14
+ "description": "Label of the blue pill above the mosaic."
15
+ },
16
+ {
17
+ "name": "ctaHref",
18
+ "type": "string",
19
+ "default": "#",
20
+ "description": "Link target of the pill."
21
+ },
22
+ {
23
+ "name": "youtubeHref",
24
+ "type": "string",
25
+ "default": "#",
26
+ "description": "Link target of the red YouTube hexagon."
27
+ },
28
+ {
29
+ "name": "xHref",
30
+ "type": "string",
31
+ "default": "#",
32
+ "description": "Link target of the black X hexagon."
33
+ },
34
+ {
35
+ "name": "facebookHref",
36
+ "type": "string",
37
+ "default": "#",
38
+ "description": "Link target of the blue Facebook hexagon."
39
+ }
40
+ ],
41
+ "cssFiles": [
42
+ "honeycomb-band.css"
12
43
  ],
13
- "cssFiles": ["honeycomb-band.css"],
14
44
  "assetFiles": [
15
45
  "img/bg-hexagons.svg",
16
46
  "logos/nimiq-icons-logos/logos-youtube-mono.svg",
@@ -21,10 +51,18 @@
21
51
  "npmDeps": [],
22
52
  "verified": true,
23
53
  "verify": {
24
- "viewport": { "width": 1200, "height": 700 },
54
+ "viewport": {
55
+ "width": 1200,
56
+ "height": 700
57
+ },
25
58
  "selector": ".nq-honeycomb-band",
26
59
  "maxDiffPct": 0.5,
27
60
  "settleMs": 300
28
61
  },
29
- "notes": "MARKETING component. Band background is the REAL nimiq.com asset assets/img/bg-hexagons.svg (2795x544 fading honeycomb, currentColor hexagons at .01-.06 opacity), swept from www.nimiq.com inline CSS as custom--bg-hexagons (references/assets/nimiq-com/css-inline/custom--bg-hexagons.svg). Rendered 1:1 as a CSS alpha mask filled with currentColor (site text navy #1F2348) — the same currentColor mechanism the live page uses — centered and clipped at the section edges. The live mosaic is lazy-loaded and did not render in references/screenshots/nimiq-com/home-desktop-full.png, so the foreground composition (three social hexagons + pill) follows references/supporting-elements-brief-2026-06-11.md item 9. Social hexagon shape = the hexagon path extracted verbatim from bg-hexagons.svg (one lattice cell, viewBox 45.5 248 124 110); the hexagons sit on exact lattice cells of the background (column step 107.5px, row step 124px, half-offset 62px; cells (967.5,303) YouTube, (1827.5,303) X, (1935,241) Facebook). Social glyphs are the REAL nimiq-icons mono logos from assets/logos/nimiq-icons-logos/ (logos-youtube-mono.svg, logos-twitter-mono.svg = the X glyph, logos-facebook-mono.svg — the same icon set nimiq.com inlines as nimiq--logos-*-mono), applied as white CSS masks. Pill uses the nimiq-style light-blue gradient radial-gradient(100% 100% at bottom right, #265DD7, #0582CA), calibrated against the 'Community →' pill in home-desktop-full.png. Asset url()s are ../../../../assets/… relative to the snippet; nq add copies them to ./nimiq/assets/… in consumer projects. Mulish via Google Fonts; deterministic, no JS."
62
+ "notes": "MARKETING component. Band background is the REAL nimiq.com asset assets/img/bg-hexagons.svg (2795x544 fading honeycomb, currentColor hexagons at .01-.06 opacity), swept from www.nimiq.com inline CSS as custom--bg-hexagons (references/assets/nimiq-com/css-inline/custom--bg-hexagons.svg). Rendered 1:1 as a CSS alpha mask filled with currentColor (site text navy #1F2348) — the same currentColor mechanism the live page uses — centered and clipped at the section edges. The live mosaic is lazy-loaded and did not render in references/screenshots/nimiq-com/home-desktop-full.png, so the foreground composition (three social hexagons + pill) follows references/supporting-elements-brief-2026-06-11.md item 9. Social hexagon shape = the hexagon path extracted verbatim from bg-hexagons.svg (one lattice cell, viewBox 45.5 248 124 110); the hexagons sit on exact lattice cells of the background (column step 107.5px, row step 124px, half-offset 62px; cells (967.5,303) YouTube, (1827.5,303) X, (1935,241) Facebook). Social glyphs are the REAL nimiq-icons mono logos from assets/logos/nimiq-icons-logos/ (logos-youtube-mono.svg, logos-twitter-mono.svg = the X glyph, logos-facebook-mono.svg — the same icon set nimiq.com inlines as nimiq--logos-*-mono), applied as white CSS masks. Pill uses the nimiq-style light-blue gradient radial-gradient(100% 100% at bottom right, #265DD7, #0582CA), calibrated against the 'Community →' pill in home-desktop-full.png. Asset url()s are ../../../../assets/… relative to the snippet; nq add copies them to ./nimiq/assets/… in consumer projects. Mulish via Google Fonts; deterministic, no JS.",
63
+ "source": {
64
+ "repo": "nimiq-com",
65
+ "ref": null,
66
+ "files": []
67
+ }
30
68
  }
@@ -34,5 +34,12 @@
34
34
  "notes": "Renders via @nimiq/iqons@1.6.0 pinned on jsdelivr in both truth and demo; Iqons.svgPath must point at iqons.min.svg (the component normally resolves it via webpack file-loader). The generated data URL embeds a random clip-path id but is pixel-identical across runs. Upstream fixes width:10rem; sample state overrides to 128px at page level. ValidationUtils.normalizeAddress/isValidAddress from @nimiq/utils@0.11.1 are inlined in the Vue port. Identicon sprite is the REAL team-shipped iqons.min.svg (84-symbol part catalog, 88KB) vendored at assets/img/iqons.min.svg — byte-identical to the Hub-deployed sprite captured in references/assets/hub/iqons.min.svg and to @nimiq/iqons@1.6.0 dist; the snippet probes the local copy and falls back to the same CDN sprite, while the iqons JS library itself stays pinned on jsdelivr (code, not artwork).",
35
35
  "assetFiles": [
36
36
  "img/iqons.min.svg"
37
- ]
37
+ ],
38
+ "source": {
39
+ "repo": "vue-components",
40
+ "ref": "3c5b972",
41
+ "files": [
42
+ "vue-components:src/components/Identicon.vue"
43
+ ]
44
+ }
38
45
  }
@@ -2,23 +2,63 @@
2
2
  "name": "label-input",
3
3
  "purpose": "A self-sizing text input for naming things (addresses, accounts), styled as an nq-input whose width grows and shrinks with its content.",
4
4
  "category": "form",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "value", "type": "string", "default": "", "description": "The label text, bindable via v-model (emits 'input' on typing, 'changed' on blur/submit)." },
8
- { "name": "maxBytes", "type": "number", "default": null, "description": "Maximum value length in UTF-8 bytes; input exceeding it is rejected (previous value is kept)." },
9
- { "name": "placeholder", "type": "string", "default": "Name your address", "description": "Placeholder text shown when empty (also defines the empty-state width)." },
10
- { "name": "vanishing", "type": "boolean", "default": false, "description": "Removes the input's inset border (nq-input 'vanishing' style)." },
11
- { "name": "disabled", "type": "boolean", "default": false, "description": "Disables the input and its hover effects." }
10
+ {
11
+ "name": "value",
12
+ "type": "string",
13
+ "default": "",
14
+ "description": "The label text, bindable via v-model (emits 'input' on typing, 'changed' on blur/submit)."
15
+ },
16
+ {
17
+ "name": "maxBytes",
18
+ "type": "number",
19
+ "default": null,
20
+ "description": "Maximum value length in UTF-8 bytes; input exceeding it is rejected (previous value is kept)."
21
+ },
22
+ {
23
+ "name": "placeholder",
24
+ "type": "string",
25
+ "default": "Name your address",
26
+ "description": "Placeholder text shown when empty (also defines the empty-state width)."
27
+ },
28
+ {
29
+ "name": "vanishing",
30
+ "type": "boolean",
31
+ "default": false,
32
+ "description": "Removes the input's inset border (nq-input 'vanishing' style)."
33
+ },
34
+ {
35
+ "name": "disabled",
36
+ "type": "boolean",
37
+ "default": false,
38
+ "description": "Disables the input and its hover effects."
39
+ }
40
+ ],
41
+ "cssFiles": [
42
+ "css/legacy/nimiq-style.min.css"
12
43
  ],
13
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
14
44
  "dependsOn": [],
15
45
  "npmDeps": [],
16
46
  "verified": true,
17
47
  "verify": {
18
- "viewport": { "width": 600, "height": 200 },
48
+ "viewport": {
49
+ "width": 600,
50
+ "height": 200
51
+ },
19
52
  "selector": ".label-input",
20
53
  "maxDiffPct": 0.5,
21
54
  "settleMs": 250
22
55
  },
23
- "notes": "Sample state: value 'Savings Account', unfocused, default props. The input width is set by script (replicating updateWidth(): measured value width + fontSize / 3), since upstream computes it in JS from hidden width-finder spans. Upstream's i18n default placeholder $t('Name your address') is hardcoded to its English string. Utf8Tools (stringToUtf8ByteArray, truncateToUtf8ByteLength) from @nimiq/utils are inlined in the Vue port via TextEncoder/TextDecoder."
56
+ "notes": "Sample state: value 'Savings Account', unfocused, default props. The input width is set by script (replicating updateWidth(): measured value width + fontSize / 3), since upstream computes it in JS from hidden width-finder spans. Upstream's i18n default placeholder $t('Name your address') is hardcoded to its English string. Utf8Tools (stringToUtf8ByteArray, truncateToUtf8ByteLength) from @nimiq/utils are inlined in the Vue port via TextEncoder/TextDecoder.",
57
+ "source": {
58
+ "repo": "vue-components",
59
+ "ref": "3c5b972",
60
+ "files": [
61
+ "vue-components:src/components/LabelInput.vue"
62
+ ]
63
+ }
24
64
  }
@@ -2,17 +2,32 @@
2
2
  "name": "loading-spinner",
3
3
  "purpose": "Animated hexagon loading indicator: two dashed hexagon outlines (one at 40% opacity) whose dash offsets rotate around the hexagon in a 4s stepped ease cycle. Inherits its color via currentColor.",
4
4
  "category": "feedback",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [],
7
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
10
+ "cssFiles": [
11
+ "css/legacy/nimiq-style.min.css"
12
+ ],
8
13
  "dependsOn": [],
9
14
  "npmDeps": [],
10
15
  "verified": true,
11
16
  "verify": {
12
- "viewport": { "width": 200, "height": 160 },
17
+ "viewport": {
18
+ "width": 200,
19
+ "height": 160
20
+ },
13
21
  "selector": ".loading-spinner",
14
22
  "maxDiffPct": 0.5,
15
23
  "settleMs": 250
16
24
  },
17
- "notes": "Sample state: default size (54x48 viewBox '0 0 54 48') on white, color inherited from body (nimiq-blue). Animation is infinite; the verify harness disables animations, which freezes both paths at their CSS-specified stroke-dashoffset (-40.5 big / 13 small — identical to the 0% keyframes). Upstream uses #big-hex/#small-hex ids; the HTML snippet uses classes and prefixed keyframe names (loading-spinner-big-hex/-small-hex) so multiple instances and showcase embedding stay valid. Size via the height/width attributes or CSS; color via the color property."
25
+ "notes": "Sample state: default size (54x48 viewBox '0 0 54 48') on white, color inherited from body (nimiq-blue). Animation is infinite; the verify harness disables animations, which freezes both paths at their CSS-specified stroke-dashoffset (-40.5 big / 13 small — identical to the 0% keyframes). Upstream uses #big-hex/#small-hex ids; the HTML snippet uses classes and prefixed keyframe names (loading-spinner-big-hex/-small-hex) so multiple instances and showcase embedding stay valid. Size via the height/width attributes or CSS; color via the color property.",
26
+ "source": {
27
+ "repo": "vue-components",
28
+ "ref": "3c5b972",
29
+ "files": [
30
+ "vue-components:src/components/LoadingSpinner.vue"
31
+ ]
32
+ }
18
33
  }
@@ -2,17 +2,35 @@
2
2
  "name": "page-body",
3
3
  "purpose": "Scrollable content area of a card-style page. Pairs .page-body with the legacy .nq-card-body padding rules and flex-grows to fill the parent SmallPage, scrolling vertically when content overflows.",
4
4
  "category": "layout",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [],
7
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
8
- "dependsOn": ["small-page", "page-header"],
10
+ "cssFiles": [
11
+ "css/legacy/nimiq-style.min.css"
12
+ ],
13
+ "dependsOn": [
14
+ "small-page",
15
+ "page-header"
16
+ ],
9
17
  "npmDeps": [],
10
18
  "verified": true,
11
19
  "verify": {
12
- "viewport": { "width": 500, "height": 700 },
20
+ "viewport": {
21
+ "width": 500,
22
+ "height": 700
23
+ },
13
24
  "selector": ".small-page",
14
25
  "maxDiffPct": 0.5,
15
26
  "settleMs": 250
16
27
  },
17
- "notes": "PageBody is a pure slot container (no props, no logic). Demo renders it in its natural habitat: a SmallPage with a PageHeader 'Settings' and two nq-text paragraphs. Styling comes almost entirely from the legacy framework's .nq-card-body rules (padding 4rem, padding-top 1rem after .nq-card-header, first/last child margin trimming); the component itself only adds flex-grow:1 and overflow-y:auto."
28
+ "notes": "PageBody is a pure slot container (no props, no logic). Demo renders it in its natural habitat: a SmallPage with a PageHeader 'Settings' and two nq-text paragraphs. Styling comes almost entirely from the legacy framework's .nq-card-body rules (padding 4rem, padding-top 1rem after .nq-card-header, first/last child margin trimming); the component itself only adds flex-grow:1 and overflow-y:auto.",
29
+ "source": {
30
+ "repo": "vue-components",
31
+ "ref": "3c5b972",
32
+ "files": [
33
+ "vue-components:src/components/PageBody.vue"
34
+ ]
35
+ }
18
36
  }
@@ -2,17 +2,34 @@
2
2
  "name": "page-footer",
3
3
  "purpose": "Footer area of a Nimiq page/card (nq-card-footer). Flex column that pins action buttons at the bottom of a SmallPage and applies the standard nq-button margins.",
4
4
  "category": "layout",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [],
7
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
8
- "dependsOn": ["small-page"],
10
+ "cssFiles": [
11
+ "css/legacy/nimiq-style.min.css"
12
+ ],
13
+ "dependsOn": [
14
+ "small-page"
15
+ ],
9
16
  "npmDeps": [],
10
17
  "verified": true,
11
18
  "verify": {
12
- "viewport": { "width": 500, "height": 700 },
19
+ "viewport": {
20
+ "width": 500,
21
+ "height": 700
22
+ },
13
23
  "selector": ".small-page",
14
24
  "maxDiffPct": 0.5,
15
25
  "settleMs": 250
16
26
  },
17
- "notes": "PageFooter is a pure slot container: .page-footer.nq-card-footer. The upstream scoped '>>>' deep selector (.page-footer >>> .nq-button) is flattened to a plain descendant selector in the HTML variant and :deep() in the Vue 3 port. Sample state renders it inside a SmallPage with one light-blue 'Continue' nq-button. With only a footer as card child (no PageBody), the footer sits at the top of the card — in real layouts PageBody flex-grows to push it down."
27
+ "notes": "PageFooter is a pure slot container: .page-footer.nq-card-footer. The upstream scoped '>>>' deep selector (.page-footer >>> .nq-button) is flattened to a plain descendant selector in the HTML variant and :deep() in the Vue 3 port. Sample state renders it inside a SmallPage with one light-blue 'Continue' nq-button. With only a footer as card child (no PageBody), the footer sits at the top of the card — in real layouts PageBody flex-grows to push it down.",
28
+ "source": {
29
+ "repo": "vue-components",
30
+ "ref": "3c5b972",
31
+ "files": [
32
+ "vue-components:src/components/PageFooter.vue"
33
+ ]
34
+ }
18
35
  }