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
@@ -2,22 +2,57 @@
2
2
  "name": "page-header",
3
3
  "purpose": "Card header for Nimiq page flows: centered title, optional back arrow, optional step progress indicator. Used at the top of a SmallPage.",
4
4
  "category": "layout",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "backArrow", "type": "boolean", "default": false, "description": "Show a back arrow on the left; emits 'back' on click" },
8
- { "name": "progressIndicator", "type": "boolean", "default": false, "description": "Show the step progress bar across the top of the card" },
9
- { "name": "numberSteps", "type": "number", "default": 6, "description": "Total number of steps in the progress indicator" },
10
- { "name": "step", "type": "number", "default": 1, "description": "Current step (1-based); steps <= step are highlighted" }
10
+ {
11
+ "name": "backArrow",
12
+ "type": "boolean",
13
+ "default": false,
14
+ "description": "Show a back arrow on the left; emits 'back' on click"
15
+ },
16
+ {
17
+ "name": "progressIndicator",
18
+ "type": "boolean",
19
+ "default": false,
20
+ "description": "Show the step progress bar across the top of the card"
21
+ },
22
+ {
23
+ "name": "numberSteps",
24
+ "type": "number",
25
+ "default": 6,
26
+ "description": "Total number of steps in the progress indicator"
27
+ },
28
+ {
29
+ "name": "step",
30
+ "type": "number",
31
+ "default": 1,
32
+ "description": "Current step (1-based); steps <= step are highlighted"
33
+ }
34
+ ],
35
+ "cssFiles": [
36
+ "css/legacy/nimiq-style.min.css"
11
37
  ],
12
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
13
38
  "dependsOn": [],
14
39
  "npmDeps": [],
15
40
  "verified": true,
16
41
  "verify": {
17
- "viewport": { "width": 500, "height": 700 },
42
+ "viewport": {
43
+ "width": 500,
44
+ "height": 700
45
+ },
18
46
  "selector": ".small-page",
19
47
  "maxDiffPct": 0.5,
20
48
  "settleMs": 250
21
49
  },
22
- "notes": "Sample state: inside a SmallPage, title 'Send Transaction', backArrow=true, progressIndicator=true, 6 steps, step 3. ArrowLeftIcon (arrow-left.svg from @nimiq/style) is inlined. $t stubbed to identity ('Go back')."
50
+ "notes": "Sample state: inside a SmallPage, title 'Send Transaction', backArrow=true, progressIndicator=true, 6 steps, step 3. ArrowLeftIcon (arrow-left.svg from @nimiq/style) is inlined. $t stubbed to identity ('Go back').",
51
+ "source": {
52
+ "repo": "vue-components",
53
+ "ref": "3c5b972",
54
+ "files": [
55
+ "vue-components:src/components/PageHeader.vue"
56
+ ]
57
+ }
23
58
  }
@@ -93,5 +93,14 @@
93
93
  "notes": "Sample state: 199.6 NIM (19960000 luna) / 2.99 EUR to shop.nimiq.com (identicon for burn address NQ07 0000..., pinned @nimiq/iqons@1.6.0 CDN), timer frozen at 10 of 15 minutes left (progress 1/3, countdown '10', circle dash values hand-computed for radius 12, fullCircleLength 2π·12). Tooltips closed; arrow-runway arrow rests at opacity 0 (screenshots disable the infinite animation). The .payment-info-line wrapper is the 420px SmallPage width; upstream root class is .info-line. Cascade quirk reproduced from upstream: Account's '.row .label:not(.editable)' (specificity 0,4,0) beats PaymentInfoLine's deep '.account .label' padding-left .75rem, so the label padding is effectively 1rem; ties (font-weight/mask-image/identicon size) go to PaymentInfoLine via injection order — the snippet CSS preserves this rule order, don't reorder. Vue port ships PaymentInfoLine.vue + minimal inline Account.vue (no editable/cashlink) and Timer.vue (radius snaps instead of tweening, simplified update interval); Amount.vue, FiatAmount.vue, Tooltip.vue, Identicon.vue come from sibling registry components (see dependsOn). getExchangeRates (rate-deviation warning) uses the real @nimiq/utils npm package; failures just suppress the warning. $t stubbed as identity; upstream's :container=\"$parent\" became a container prop. 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).",
94
94
  "assetFiles": [
95
95
  "img/iqons.min.svg"
96
- ]
96
+ ],
97
+ "source": {
98
+ "repo": "vue-components",
99
+ "ref": "3c5b972",
100
+ "files": [
101
+ "vue-components:src/components/PaymentInfoLine.vue",
102
+ "vue-components:src/components/Timer.vue",
103
+ "vue-components:src/components/Identicon.vue"
104
+ ]
105
+ }
97
106
  }
@@ -2,20 +2,76 @@
2
2
  "name": "price-chart",
3
3
  "purpose": "The wallet sidebar's crypto price sparkline — '24H' badge over a smoothed white line chart, with the currency ticker, fiat price and 24h percentage change below, on the navy sidebar background.",
4
4
  "category": "data-display",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "currency", "type": "string", "default": "'nim'", "description": "Crypto ticker shown bottom-left (rendered uppercase). Upstream: CryptoCurrency enum ('nim' | 'btc' | ...)." },
8
- { "name": "price", "type": "number", "default": "0.000501", "description": "Current fiat price. Formatted like @nimiq/vue-components FiatAmount with maxRelativeDeviation 0.001 (decimals grow until the rounded value is within 0.1%)." },
9
- { "name": "fiatCurrency", "type": "string", "default": "'usd'", "description": "Fiat currency code for price formatting." },
10
- { "name": "change", "type": "number", "default": "0.001", "description": "Relative price change over the time range (0.001 = +0.1%). Positive renders green with a CSS '+' prefix; upstream styles only .positive (negative stays 60% white)." },
11
- { "name": "points", "type": "{ x: number, y: number }[]", "default": "18-point sample NIM series", "description": "History points, normalized to the box by the upstream LineChart algorithm (cubic smoothing, factor 0.2). Upstream samples 18 points from the fiat history API." },
12
- { "name": "showTimespanLabel", "type": "boolean", "default": "true", "description": "Show the '24H' pill (upstream: true for the first sidebar chart, false for the BTC one below it). Click emits 'timespan'." },
13
- { "name": "timeRange", "type": "'24h' | '7d'", "default": "'24h'", "description": "Label text for the timespan pill." }
10
+ {
11
+ "name": "currency",
12
+ "type": "string",
13
+ "default": "'nim'",
14
+ "description": "Crypto ticker shown bottom-left (rendered uppercase). Upstream: CryptoCurrency enum ('nim' | 'btc' | ...)."
15
+ },
16
+ {
17
+ "name": "price",
18
+ "type": "number",
19
+ "default": "0.000501",
20
+ "description": "Current fiat price. Formatted like @nimiq/vue-components FiatAmount with maxRelativeDeviation 0.001 (decimals grow until the rounded value is within 0.1%)."
21
+ },
22
+ {
23
+ "name": "fiatCurrency",
24
+ "type": "string",
25
+ "default": "'usd'",
26
+ "description": "Fiat currency code for price formatting."
27
+ },
28
+ {
29
+ "name": "change",
30
+ "type": "number",
31
+ "default": "0.001",
32
+ "description": "Relative price change over the time range (0.001 = +0.1%). Positive renders green with a CSS '+' prefix; upstream styles only .positive (negative stays 60% white)."
33
+ },
34
+ {
35
+ "name": "points",
36
+ "type": "{ x: number, y: number }[]",
37
+ "default": "18-point sample NIM series",
38
+ "description": "History points, normalized to the box by the upstream LineChart algorithm (cubic smoothing, factor 0.2). Upstream samples 18 points from the fiat history API."
39
+ },
40
+ {
41
+ "name": "showTimespanLabel",
42
+ "type": "boolean",
43
+ "default": "true",
44
+ "description": "Show the '24H' pill (upstream: true for the first sidebar chart, false for the BTC one below it). Click emits 'timespan'."
45
+ },
46
+ {
47
+ "name": "timeRange",
48
+ "type": "'24h' | '7d'",
49
+ "default": "'24h'",
50
+ "description": "Label text for the timespan pill."
51
+ }
52
+ ],
53
+ "cssFiles": [
54
+ "css/legacy/nimiq-style.min.css"
14
55
  ],
15
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
16
56
  "dependsOn": [],
17
57
  "npmDeps": [],
18
58
  "verified": true,
19
- "verify": { "viewport": { "width": 280, "height": 220 }, "selector": ".price-chart-widget", "maxDiffPct": 0.5, "settleMs": 250 },
20
- "notes": "Source: upstream/wallet/src/components/PriceChart.vue + LineChart.vue (scoped CSS verbatim, Vue scoping emulated with data-v attrs in truth), rendered in the wallet sidebar context: Sidebar.vue .price-chart-wrapper/.price-chart rules (height 15rem, padding 1.5rem, timespan moved to 1.5rem/1.5rem — Sidebar's ::v-deep override wins by injection order) on a 24rem-wide navy excerpt (--bg-secondary: var(--nimiq-blue), color white, --padding-sides 1.5rem from App.vue/Sidebar.vue). The .price-chart-widget root stands in for the sidebar; the wrapper's 3rem scroll-fade mask is omitted (it belongs to the full-height scrolling sidebar and would fade the meta row in this exact-height excerpt). Sample state: NIM, $0.000501, +0.1% (green), fixed deterministic 18-point 24h series; path precomputed with LineChart's exact calculatePath (smoothing 0.2, strokeWidth 2.5, viewBox padding 5) for the 144x52 svg box. White line is white at 0.5 stroke-opacity (currentColor), as upstream. Upstream pairs it with a second chart (currency='btc', showTimespanLabel=false) in the same wrapper. Wallet theme vars (--body-size, --small-size, --tiny-label-size) defined on the snippet root."
59
+ "verify": {
60
+ "viewport": {
61
+ "width": 280,
62
+ "height": 220
63
+ },
64
+ "selector": ".price-chart-widget",
65
+ "maxDiffPct": 0.5,
66
+ "settleMs": 250
67
+ },
68
+ "notes": "Source: upstream/wallet/src/components/PriceChart.vue + LineChart.vue (scoped CSS verbatim, Vue scoping emulated with data-v attrs in truth), rendered in the wallet sidebar context: Sidebar.vue .price-chart-wrapper/.price-chart rules (height 15rem, padding 1.5rem, timespan moved to 1.5rem/1.5rem — Sidebar's ::v-deep override wins by injection order) on a 24rem-wide navy excerpt (--bg-secondary: var(--nimiq-blue), color white, --padding-sides 1.5rem from App.vue/Sidebar.vue). The .price-chart-widget root stands in for the sidebar; the wrapper's 3rem scroll-fade mask is omitted (it belongs to the full-height scrolling sidebar and would fade the meta row in this exact-height excerpt). Sample state: NIM, $0.000501, +0.1% (green), fixed deterministic 18-point 24h series; path precomputed with LineChart's exact calculatePath (smoothing 0.2, strokeWidth 2.5, viewBox padding 5) for the 144x52 svg box. White line is white at 0.5 stroke-opacity (currentColor), as upstream. Upstream pairs it with a second chart (currency='btc', showTimespanLabel=false) in the same wrapper. Wallet theme vars (--body-size, --small-size, --tiny-label-size) defined on the snippet root.",
69
+ "source": {
70
+ "repo": "wallet",
71
+ "ref": "656ed56",
72
+ "files": [
73
+ "wallet:src/components/PriceChart.vue",
74
+ "wallet:src/components/LineChart.vue"
75
+ ]
76
+ }
21
77
  }
@@ -2,7 +2,10 @@
2
2
  "name": "qr-code",
3
3
  "purpose": "Nimiq QR code — renders arbitrary data (typically a nimiq: payment URI) as a rounded-module QR code on a canvas, filled by default with the Nimiq light-blue radial gradient (#265DD7 → #0582CA) on a transparent background.",
4
4
  "category": "data-display",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
10
  {
8
11
  "name": "data",
@@ -46,15 +49,29 @@
46
49
  "description": "Width and height of the QR code in pixels (canvas pixels; the canvas has no devicePixelRatio scaling)."
47
50
  }
48
51
  ],
49
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
52
+ "cssFiles": [
53
+ "css/legacy/nimiq-style.min.css"
54
+ ],
50
55
  "dependsOn": [],
51
- "npmDeps": ["qr-creator"],
56
+ "npmDeps": [
57
+ "qr-creator"
58
+ ],
52
59
  "verified": true,
53
60
  "verify": {
54
- "viewport": { "width": 600, "height": 400 },
61
+ "viewport": {
62
+ "width": 600,
63
+ "height": 400
64
+ },
55
65
  "selector": "canvas",
56
66
  "maxDiffPct": 0.5,
57
67
  "settleMs": 250
58
68
  },
59
- "notes": "Upstream QrCode.vue has no <style> block — the component is a bare <canvas class=\"qr-code\">, all rendering done by qr-creator (pinned qr-creator@1.0.0, the only published version). Sample state: data 'nimiq:NQ07000000000000000000000000000000000000', size 240, all other props at upstream defaults. qr-creator sets canvas.width/height = size with no devicePixelRatio handling, so the QR renders at CSS-pixel resolution. The Vue port keeps the upstream toDataUrl() helper as an exposed method."
69
+ "notes": "Upstream QrCode.vue has no <style> block — the component is a bare <canvas class=\"qr-code\">, all rendering done by qr-creator (pinned qr-creator@1.0.0, the only published version). Sample state: data 'nimiq:NQ07000000000000000000000000000000000000', size 240, all other props at upstream defaults. qr-creator sets canvas.width/height = size with no devicePixelRatio handling, so the QR renders at CSS-pixel resolution. The Vue port keeps the upstream toDataUrl() helper as an exposed method.",
70
+ "source": {
71
+ "repo": "vue-components",
72
+ "ref": "3c5b972",
73
+ "files": [
74
+ "vue-components:src/components/QrCode.vue"
75
+ ]
76
+ }
60
77
  }
@@ -2,19 +2,39 @@
2
2
  "name": "search-bar",
3
3
  "purpose": "The wallet's pill-shaped transaction search input: magnifier icon + borderless input inside a 500px-radius inset border, light-blue on focus, with a clear (cross) button while active.",
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 search query, bindable via v-model (modelValue/update:modelValue in the Vue 3 port)." }
10
+ {
11
+ "name": "value",
12
+ "type": "string",
13
+ "default": "",
14
+ "description": "The search query, bindable via v-model (modelValue/update:modelValue in the Vue 3 port)."
15
+ }
16
+ ],
17
+ "cssFiles": [
18
+ "css/legacy/nimiq-style.min.css"
8
19
  ],
9
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
10
20
  "dependsOn": [],
11
21
  "npmDeps": [],
12
22
  "verified": true,
13
23
  "verify": {
14
- "viewport": { "width": 480, "height": 120 },
24
+ "viewport": {
25
+ "width": 480,
26
+ "height": 120
27
+ },
15
28
  "selector": ".search-bar",
16
29
  "maxDiffPct": 0.5,
17
30
  "settleMs": 250
18
31
  },
19
- "notes": "Source: upstream/wallet/src/components/SearchBar.vue (+ CrossCloseButton.vue, inlined in the Vue port). Sample state: resting — empty value, unfocused, placeholder 'Search transactions' (the upstream ResizeObserver picks that string for container widths >210px and non-expanded max-width), magnifier icon at opacity 0.4, no cross button, max-width 'var(--default-sb-width)' = clamp(5.5rem, 100%, 30rem). Theme vars from wallet themes.scss defined on the root: --text-16/--text-22 (inset 1.5px pill border, hover), --light-blue-40 (focus border), --body-size: 2rem. --attr-duration/--nimiq-ease come from the legacy framework CSS. The upstream svg has no viewBox/size attributes; CSS sizes it to 1.75rem, exactly as in the wallet."
32
+ "notes": "Source: upstream/wallet/src/components/SearchBar.vue (+ CrossCloseButton.vue, inlined in the Vue port). Sample state: resting — empty value, unfocused, placeholder 'Search transactions' (the upstream ResizeObserver picks that string for container widths >210px and non-expanded max-width), magnifier icon at opacity 0.4, no cross button, max-width 'var(--default-sb-width)' = clamp(5.5rem, 100%, 30rem). Theme vars from wallet themes.scss defined on the root: --text-16/--text-22 (inset 1.5px pill border, hover), --light-blue-40 (focus border), --body-size: 2rem. --attr-duration/--nimiq-ease come from the legacy framework CSS. The upstream svg has no viewBox/size attributes; CSS sizes it to 1.75rem, exactly as in the wallet.",
33
+ "source": {
34
+ "repo": "wallet",
35
+ "ref": "656ed56",
36
+ "files": [
37
+ "wallet:src/components/SearchBar.vue"
38
+ ]
39
+ }
20
40
  }
@@ -2,21 +2,51 @@
2
2
  "name": "select-bar",
3
3
  "purpose": "Segmented radio bar (pill-shaped) for choosing one of several ordered options, e.g. a transaction fee level. All options up to and including the selected one are tinted with the selected option's color; later options stay on the neutral highlight background.",
4
4
  "category": "form",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "name", "type": "string", "required": false, "description": "The radio group's name attribute, shared by all option inputs." },
8
- { "name": "options", "type": "SelectBarOption[]", "required": false, "description": "Options as { color, value, text, index }. color is a nimiq-style bg class (e.g. 'nq-green-bg'), index defines the order, value is emitted on change." },
9
- { "name": "selectedValue", "type": "number", "required": false, "description": "value of the initially selected option. Defaults to the first option (lowest index)." }
10
+ {
11
+ "name": "name",
12
+ "type": "string",
13
+ "required": false,
14
+ "description": "The radio group's name attribute, shared by all option inputs."
15
+ },
16
+ {
17
+ "name": "options",
18
+ "type": "SelectBarOption[]",
19
+ "required": false,
20
+ "description": "Options as { color, value, text, index }. color is a nimiq-style bg class (e.g. 'nq-green-bg'), index defines the order, value is emitted on change."
21
+ },
22
+ {
23
+ "name": "selectedValue",
24
+ "type": "number",
25
+ "required": false,
26
+ "description": "value of the initially selected option. Defaults to the first option (lowest index)."
27
+ }
28
+ ],
29
+ "cssFiles": [
30
+ "css/legacy/nimiq-style.min.css"
10
31
  ],
11
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
12
32
  "dependsOn": [],
13
33
  "npmDeps": [],
14
34
  "verified": true,
15
35
  "verify": {
16
- "viewport": { "width": 600, "height": 200 },
36
+ "viewport": {
37
+ "width": 600,
38
+ "height": 200
39
+ },
17
40
  "selector": ".select-bar",
18
41
  "maxDiffPct": 0.5,
19
42
  "settleMs": 250
20
43
  },
21
- "notes": "Sample state: fee selector name='fee' with options free (nq-light-blue-bg, value 0), standard (nq-green-bg, value 1), express (nq-gold-bg, value 2); selectedValue=1 so 'standard' (middle) is selected — free+standard render nq-green-bg, express renders nq-highlight-bg. $t stubbed as identity (texts come pre-translated through the options prop upstream). Emits 'changed' with the option's value. Color classes (nq-light-blue-bg/nq-green-bg/nq-gold-bg) and --nimiq-highlight-bg come from the legacy nimiq-style CSS; bg classes also flip the label text to white via the stylesheet's later .nq-*-bg{color:var(--nimiq-white)} rule. The HTML snippet is interactive: each label carries its option color in data-color and a scoped inline script (root resolved via document.currentScript.previousElementSibling — safe when embedded among other snippets) replicates upstream getColor() on the bubbling 'change' event, so clicking free/standard/express re-colors correctly; listen for 'change' on .select-bar as the plain-HTML equivalent of the Vue 'changed' emit. Resting state is unchanged and pixel-verified."
44
+ "notes": "Sample state: fee selector name='fee' with options free (nq-light-blue-bg, value 0), standard (nq-green-bg, value 1), express (nq-gold-bg, value 2); selectedValue=1 so 'standard' (middle) is selected — free+standard render nq-green-bg, express renders nq-highlight-bg. $t stubbed as identity (texts come pre-translated through the options prop upstream). Emits 'changed' with the option's value. Color classes (nq-light-blue-bg/nq-green-bg/nq-gold-bg) and --nimiq-highlight-bg come from the legacy nimiq-style CSS; bg classes also flip the label text to white via the stylesheet's later .nq-*-bg{color:var(--nimiq-white)} rule. The HTML snippet is interactive: each label carries its option color in data-color and a scoped inline script (root resolved via document.currentScript.previousElementSibling — safe when embedded among other snippets) replicates upstream getColor() on the bubbling 'change' event, so clicking free/standard/express re-colors correctly; listen for 'change' on .select-bar as the plain-HTML equivalent of the Vue 'changed' emit. Resting state is unchanged and pixel-verified.",
45
+ "source": {
46
+ "repo": "vue-components",
47
+ "ref": "3c5b972",
48
+ "files": [
49
+ "vue-components:src/components/SelectBar.vue"
50
+ ]
51
+ }
22
52
  }
@@ -2,22 +2,59 @@
2
2
  "name": "slider-toggle",
3
3
  "purpose": "Pill-shaped radio toggle with a white 'active box' that slides under the selected option. Used in the Nimiq Wallet as a currency switcher (NIM/BTC) and for filter/sort selection.",
4
4
  "category": "form",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "name", "type": "string", "required": true, "description": "The radio input group name." },
8
- { "name": "value", "type": "string", "required": true, "description": "The currently selected option (slot name). Upstream v-model: emits 'input' on change." },
9
- { "name": "type", "type": "'currency' | 'default'", "required": false, "default": "'default'", "description": "Visual style: 'currency' is compact (1.75rem, bold, wide letter-spacing), 'default' is large (2.5rem, semibold)." },
10
- { "name": "loading", "type": "boolean", "required": false, "default": "false", "description": "Replaces the options with a LoadingSpinner + 'Loading' label." }
10
+ {
11
+ "name": "name",
12
+ "type": "string",
13
+ "required": true,
14
+ "description": "The radio input group name."
15
+ },
16
+ {
17
+ "name": "value",
18
+ "type": "string",
19
+ "required": true,
20
+ "description": "The currently selected option (slot name). Upstream v-model: emits 'input' on change."
21
+ },
22
+ {
23
+ "name": "type",
24
+ "type": "'currency' | 'default'",
25
+ "required": false,
26
+ "default": "'default'",
27
+ "description": "Visual style: 'currency' is compact (1.75rem, bold, wide letter-spacing), 'default' is large (2.5rem, semibold)."
28
+ },
29
+ {
30
+ "name": "loading",
31
+ "type": "boolean",
32
+ "required": false,
33
+ "default": "false",
34
+ "description": "Replaces the options with a LoadingSpinner + 'Loading' label."
35
+ }
36
+ ],
37
+ "cssFiles": [
38
+ "css/legacy/nimiq-style.min.css"
11
39
  ],
12
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
13
40
  "dependsOn": [],
14
41
  "npmDeps": [],
15
42
  "verified": true,
16
43
  "verify": {
17
- "viewport": { "width": 500, "height": 200 },
44
+ "viewport": {
45
+ "width": 500,
46
+ "height": 200
47
+ },
18
48
  "selector": ".slider-toggle",
19
49
  "maxDiffPct": 0.5,
20
50
  "settleMs": 250
21
51
  },
22
- "notes": "Sample state: name='currency', type='currency', two slots nim/btc rendering 'NIM'/'BTC' with first active (matches the upstream storybook currency story). Upstream positions the .active-box via JS (getBoundingClientRect on the active label); truth.html replicates that with a small inline script, while html/demo.html is CSS-only — the active label itself gets the white pill via label:has(input:checked), which is geometrically identical (active-box width = label width, height = label height). Upstream options are dynamic named slots (one label per slot). The Vue port inlines LoadingSpinner.vue (hexagon SVG spinner) instead of importing it."
52
+ "notes": "Sample state: name='currency', type='currency', two slots nim/btc rendering 'NIM'/'BTC' with first active (matches the upstream storybook currency story). Upstream positions the .active-box via JS (getBoundingClientRect on the active label); truth.html replicates that with a small inline script, while html/demo.html is CSS-only — the active label itself gets the white pill via label:has(input:checked), which is geometrically identical (active-box width = label width, height = label height). Upstream options are dynamic named slots (one label per slot). The Vue port inlines LoadingSpinner.vue (hexagon SVG spinner) instead of importing it.",
53
+ "source": {
54
+ "repo": "vue-components",
55
+ "ref": "3c5b972",
56
+ "files": [
57
+ "vue-components:src/components/SliderToggle.vue"
58
+ ]
59
+ }
23
60
  }
@@ -2,17 +2,32 @@
2
2
  "name": "small-page",
3
3
  "purpose": "Standard 420x564px white card container (SmallPage) that frames a page of content in Nimiq apps; pairs with PageHeader/PageBody/PageFooter.",
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"],
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": 500, "height": 700 },
17
+ "viewport": {
18
+ "width": 500,
19
+ "height": 700
20
+ },
13
21
  "selector": ".small-page",
14
22
  "maxDiffPct": 0.5,
15
23
  "settleMs": 250
16
24
  },
17
- "notes": "SmallPage itself is just <div class=\"small-page nq-card\"> + slot; all visual styling (background, radius, shadow, text color) comes from .nq-card in legacy nimiq-style. Scoped CSS fixes size to 52.5rem x 70.5rem (420x564px at the 8px html root font-size set by nimiq-style) and overrides .nq-card margin with 0 1.25rem. Sample state fills the slot with .nq-card-header (.nq-h1) and .nq-card-body (.nq-text) placeholder content."
25
+ "notes": "SmallPage itself is just <div class=\"small-page nq-card\"> + slot; all visual styling (background, radius, shadow, text color) comes from .nq-card in legacy nimiq-style. Scoped CSS fixes size to 52.5rem x 70.5rem (420x564px at the 8px html root font-size set by nimiq-style) and overrides .nq-card margin with 0 1.25rem. Sample state fills the slot with .nq-card-header (.nq-h1) and .nq-card-body (.nq-text) placeholder content.",
26
+ "source": {
27
+ "repo": "vue-components",
28
+ "ref": "3c5b972",
29
+ "files": [
30
+ "vue-components:src/components/SmallPage.vue"
31
+ ]
32
+ }
18
33
  }
@@ -2,21 +2,51 @@
2
2
  "name": "status-alert",
3
3
  "purpose": "Status alert / callout block (note-info, tip-success, warning, caution-error, important) in the real Nimiq pattern: tinted background, 1.5px tonal outline, uppercase icon title and colored text. Used for status notices, warnings and error feedback.",
4
4
  "category": "feedback",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "type", "type": "'info' | 'success' | 'warning' | 'error' | 'important'", "default": "'info'", "description": "Alert severity; maps to the upstream callout classes note/tip/warning/caution/important (blue/green/orange/red/purple)." },
8
- { "name": "title", "type": "string", "default": "''", "description": "Title line text. Defaults to the upstream label for the type (Note/Tip/Warning/Caution/Important). Rendered uppercase with the type icon." },
9
- { "name": "message", "type": "string", "default": "''", "description": "Alert body text. The default slot overrides it when provided." }
10
+ {
11
+ "name": "type",
12
+ "type": "'info' | 'success' | 'warning' | 'error' | 'important'",
13
+ "default": "'info'",
14
+ "description": "Alert severity; maps to the upstream callout classes note/tip/warning/caution/important (blue/green/orange/red/purple)."
15
+ },
16
+ {
17
+ "name": "title",
18
+ "type": "string",
19
+ "default": "''",
20
+ "description": "Title line text. Defaults to the upstream label for the type (Note/Tip/Warning/Caution/Important). Rendered uppercase with the type icon."
21
+ },
22
+ {
23
+ "name": "message",
24
+ "type": "string",
25
+ "default": "''",
26
+ "description": "Alert body text. The default slot overrides it when provided."
27
+ }
28
+ ],
29
+ "cssFiles": [
30
+ "css/legacy/nimiq-style.min.css"
10
31
  ],
11
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
12
32
  "dependsOn": [],
13
33
  "npmDeps": [],
14
34
  "verified": true,
15
35
  "verify": {
16
- "viewport": { "width": 600, "height": 640 },
36
+ "viewport": {
37
+ "width": 600,
38
+ "height": 640
39
+ },
17
40
  "selector": "body",
18
41
  "maxDiffPct": 0.5,
19
42
  "settleMs": 250
20
43
  },
21
- "notes": "REBUILT 2026-06-11: previous port came from a local example file (icon-chip + navy text + 1px border) which is NOT a real Nimiq pattern. Source of truth now = upstream/nimiq-ui/packages/nimiq-vitepress-theme/src/assets/github-callouts.css (.custom-block.github-alert), the alert pattern nimiq-branding-skill/references/status-alerts.md cites; color tokens inlined as the light-mode halves of light-dark() from upstream/nimiq-ui/packages/nimiq-css/src/css/colors.css. Truth keeps the component CSS verbatim and restores the upstream docs context (html/body 16px; @nimiq/style's 8px rem base would halve the rem padding). Snippet/Vue porting decisions: rem converted to px at the upstream 16px root (padding 1rem 1.25rem -> 16px 20px) and font-size:16px pinned on the root class so the snippet renders identically under @nimiq/style; selectors renamed/namespaced under .nq-status-alert; the upstream docs full-bleed quirk (transform translateX(-8px) + width calc(100%+16px)) is kept for pixel parity — consumers can delete that pair for a non-bleeding alert. The legacy framework's own inline pattern is .nq-notice (colored text only), already covered by upstream apps; this component is the block-level alert. No upstream Vue component exists (callouts are markdown-rendered), so the Vue port is a faithful wrapper (type/title/message props + slot)."
44
+ "notes": "REBUILT 2026-06-11: previous port came from a local example file (icon-chip + navy text + 1px border) which is NOT a real Nimiq pattern. Source of truth now = upstream/nimiq-ui/packages/nimiq-vitepress-theme/src/assets/github-callouts.css (.custom-block.github-alert), the alert pattern nimiq-branding-skill/references/status-alerts.md cites; color tokens inlined as the light-mode halves of light-dark() from upstream/nimiq-ui/packages/nimiq-css/src/css/colors.css. Truth keeps the component CSS verbatim and restores the upstream docs context (html/body 16px; @nimiq/style's 8px rem base would halve the rem padding). Snippet/Vue porting decisions: rem converted to px at the upstream 16px root (padding 1rem 1.25rem -> 16px 20px) and font-size:16px pinned on the root class so the snippet renders identically under @nimiq/style; selectors renamed/namespaced under .nq-status-alert; the upstream docs full-bleed quirk (transform translateX(-8px) + width calc(100%+16px)) is kept for pixel parity — consumers can delete that pair for a non-bleeding alert. The legacy framework's own inline pattern is .nq-notice (colored text only), already covered by upstream apps; this component is the block-level alert. No upstream Vue component exists (callouts are markdown-rendered), so the Vue port is a faithful wrapper (type/title/message props + slot).",
45
+ "source": {
46
+ "repo": "nimiq-ui",
47
+ "ref": "68e96cd",
48
+ "files": [
49
+ "nimiq-ui:packages/nimiq-vitepress-theme/src/assets/github-callouts.css"
50
+ ]
51
+ }
22
52
  }
@@ -2,26 +2,83 @@
2
2
  "name": "status-screen",
3
3
  "purpose": "Full-card overlay status screen from the Nimiq Hub: loading (navy/light-blue gradient + hexagon spinner + status line), success (green gradient + animated checkmark), warning (orange + neutral face) and error (red + sad face) states, with optional action button/link. Covers the whole SmallPage while a request is processed and resolves into the iconic green checkmark end screen.",
4
4
  "category": "feedback",
5
- "variants": ["vue", "html"],
5
+ "variants": [
6
+ "vue",
7
+ "html"
8
+ ],
6
9
  "props": [
7
- { "name": "title", "type": "string", "default": "''", "description": "Headline; shown under the icon for result states and at the top for the loading state." },
8
- { "name": "state", "type": "'loading' | 'success' | 'warning' | 'error'", "default": "'loading'", "description": "Which screen to show; success adds the exit-transition slide-up animation." },
9
- { "name": "status", "type": "string", "default": "''", "description": "Loading state only: current step text, animates upward on change." },
10
- { "name": "lightBlue", "type": "boolean", "default": false, "description": "Use the light-blue gradient instead of navy for the loading background." },
11
- { "name": "message", "type": "string", "default": "''", "description": "Body text for warning and error states." },
12
- { "name": "mainAction", "type": "string", "default": "''", "description": "Label of the inverse action button on warning/error screens; emits 'main-action'." },
13
- { "name": "alternativeAction", "type": "string", "default": "''", "description": "Label of the secondary text link on warning/error screens; emits 'alternative-action'." },
14
- { "name": "small", "type": "boolean", "default": false, "description": "Compact layout (smaller title, tighter status spacing)." }
10
+ {
11
+ "name": "title",
12
+ "type": "string",
13
+ "default": "''",
14
+ "description": "Headline; shown under the icon for result states and at the top for the loading state."
15
+ },
16
+ {
17
+ "name": "state",
18
+ "type": "'loading' | 'success' | 'warning' | 'error'",
19
+ "default": "'loading'",
20
+ "description": "Which screen to show; success adds the exit-transition slide-up animation."
21
+ },
22
+ {
23
+ "name": "status",
24
+ "type": "string",
25
+ "default": "''",
26
+ "description": "Loading state only: current step text, animates upward on change."
27
+ },
28
+ {
29
+ "name": "lightBlue",
30
+ "type": "boolean",
31
+ "default": false,
32
+ "description": "Use the light-blue gradient instead of navy for the loading background."
33
+ },
34
+ {
35
+ "name": "message",
36
+ "type": "string",
37
+ "default": "''",
38
+ "description": "Body text for warning and error states."
39
+ },
40
+ {
41
+ "name": "mainAction",
42
+ "type": "string",
43
+ "default": "''",
44
+ "description": "Label of the inverse action button on warning/error screens; emits 'main-action'."
45
+ },
46
+ {
47
+ "name": "alternativeAction",
48
+ "type": "string",
49
+ "default": "''",
50
+ "description": "Label of the secondary text link on warning/error screens; emits 'alternative-action'."
51
+ },
52
+ {
53
+ "name": "small",
54
+ "type": "boolean",
55
+ "default": false,
56
+ "description": "Compact layout (smaller title, tighter status spacing)."
57
+ }
58
+ ],
59
+ "cssFiles": [
60
+ "css/legacy/nimiq-style.min.css"
61
+ ],
62
+ "dependsOn": [
63
+ "small-page"
15
64
  ],
16
- "cssFiles": ["css/legacy/nimiq-style.min.css"],
17
- "dependsOn": ["small-page"],
18
65
  "npmDeps": [],
19
66
  "verified": true,
20
67
  "verify": {
21
- "viewport": { "width": 500, "height": 700 },
68
+ "viewport": {
69
+ "width": 500,
70
+ "height": 700
71
+ },
22
72
  "selector": ".small-page",
23
73
  "maxDiffPct": 0.5,
24
74
  "settleMs": 250
25
75
  },
26
- "notes": "Source: upstream/hub/src/components/StatusScreen.vue (Hub-internal component, not in @nimiq/vue-components — but it imports LoadingSpinner/CheckmarkIcon/FaceNeutralIcon/FaceSadIcon from there). Sample state: SUCCESS inside a SmallPage, mirroring hub CheckoutTransmission.vue ('Payment successful.'), static end-frame of the success-title-slide animation (Playwright animations:'disabled' fast-forwards it; root has .exit-transition but no .nq-blue-bg since showLoadingBackground is false in result states). Visuals come from .nq-green-bg gradient + white text rules in legacy nimiq-style; checkmark is checkmark.svg from @nimiq/style at font-size 9rem via .nq-icon{width:1em}. Snippet CSS namespaces every scoped selector under .status-screen; keyframe names kept upstream-verbatim (success-title-slide, exit-transition, status-screen-grow-from-bottom-button). Vue port: full Vue 3 script-setup rewrite of the class component — all 4 states, status-line crossfade timers, showLoadingBackground corner-pixel fix, Vue 3 transition class names (-enter-from), icons inlined."
76
+ "notes": "Source: upstream/hub/src/components/StatusScreen.vue (Hub-internal component, not in @nimiq/vue-components — but it imports LoadingSpinner/CheckmarkIcon/FaceNeutralIcon/FaceSadIcon from there). Sample state: SUCCESS inside a SmallPage, mirroring hub CheckoutTransmission.vue ('Payment successful.'), static end-frame of the success-title-slide animation (Playwright animations:'disabled' fast-forwards it; root has .exit-transition but no .nq-blue-bg since showLoadingBackground is false in result states). Visuals come from .nq-green-bg gradient + white text rules in legacy nimiq-style; checkmark is checkmark.svg from @nimiq/style at font-size 9rem via .nq-icon{width:1em}. Snippet CSS namespaces every scoped selector under .status-screen; keyframe names kept upstream-verbatim (success-title-slide, exit-transition, status-screen-grow-from-bottom-button). Vue port: full Vue 3 script-setup rewrite of the class component — all 4 states, status-line crossfade timers, showLoadingBackground corner-pixel fix, Vue 3 transition class names (-enter-from), icons inlined.",
77
+ "source": {
78
+ "repo": "hub",
79
+ "ref": "cf2ea41",
80
+ "files": [
81
+ "hub:src/components/StatusScreen.vue"
82
+ ]
83
+ }
27
84
  }
@@ -93,5 +93,12 @@
93
93
  "notes": "Source: upstream/wallet/src/components/swap/SwapBalanceBar.vue (+ icons/SwapBalanceBar/CurvedLine.vue, assets/swap-change-background.svg, assets/horizontal-double-arrow.svg; container width from swap/SwapModal.vue .small-page 63.5rem - 2*4rem page padding = 444px). Sample state per supporting-elements brief item 4: NIM 'Indigo Address' (NQ87 JY9X...) 519 NIM solid navy (.nq-blue via getColorClass: indigo identicon -> nq-blue) at 46%, BTC 0.00 -> +0.00000476 BTC at 54% rendered as full-width .change diagonal hatch over bitcoin orange, white round drag handle with double-arrow on the light-blue separator hairline, 46%/54% tick scale, CurvedLine connectors (widths 82/98px hand-computed from the upstream rAF formulas for a 444px root). Static frozen render; drag/touch logic lives in the Vue port only. Identicon via pinned @nimiq/iqons@1.6.0 jsdelivr recipe (module script iterates .swap-balance-bar .identicon[data-address], namespaced under the component root like the account-ring snippet). Vue port translates the upstream Pinia stores into props (nimAddresses/btcBalance/exchangeRates). 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).",
94
94
  "assetFiles": [
95
95
  "img/iqons.min.svg"
96
- ]
96
+ ],
97
+ "source": {
98
+ "repo": "wallet",
99
+ "ref": "656ed56",
100
+ "files": [
101
+ "wallet:src/components/swap/SwapBalanceBar.vue"
102
+ ]
103
+ }
97
104
  }