@wix/himalaya-cli 0.930.0 → 0.932.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.
@@ -31,6 +31,12 @@ himi icon generate # a gradient tile + glyph, for a customer
31
31
  himi icon glyphs # what `generate --glyph` accepts
32
32
  ```
33
33
 
34
+ **No logo on the site?** `himi site analyze` reports `identity.logoUrl: null` for plenty of real
35
+ sites, and `from-site` then has nothing to take. That is the `generate` case, not a dead end:
36
+ `himi icon generate --glyph <name> --accent <hex>` draws a mark from the brand's own accent
37
+ colour — see `himi icon glyphs` for the vocabulary. A QA run hit exactly this, inferred
38
+ `generate` unaided, and both resulting binaries shipped their own mark (#2823 item 6).
39
+
34
40
  **`generate` with no `--glyph` draws `emblem`** — a mark that means nothing, on your app's
35
41
  accent colour. That is deliberate: every other glyph in the vocabulary asserts a business
36
42
  (`envelope` says mail, `cutlery` says restaurant), and a fallback fires precisely when
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.930.0",
3
+ "version": "0.932.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -64,5 +64,5 @@
64
64
  "artifactId": "himalaya-cli"
65
65
  }
66
66
  },
67
- "falconPackageHash": "4aa36a3ff0999cd423ab13c46f38df173f2468df2b52718520fafcb5"
67
+ "falconPackageHash": "5594dcf1852eb9fcdca5e28365f47573679ebb1fdaab2421b0cdd6ee"
68
68
  }
@@ -0,0 +1,147 @@
1
+ # Design Kits Catalog
2
+
3
+ Shared, OTA-deliverable look-and-feel **kits** (RFC 0021). Any Himalaya app can adopt a kit; the token *names* stay canonical (compile-time-validated), a kit supplies *values* + primitive *selections*.
4
+
5
+ ## Kit JSON shape
6
+
7
+ ```jsonc
8
+ {
9
+ "id": "liquid-glass", // kebab-case, == filename
10
+ "name": "Liquid Glass", // human label (admin gallery)
11
+ "tags": ["surface"], // "surface" | "construction"
12
+ "kit": { "material": "glass", // app-wide default surface material
13
+ "variant": "default" }, // app-wide default construction language
14
+ "manifest": { "materials": ["glass"], // primitives this kit RELIES on — gated by kit-coverage.test.ts
15
+ "variants": [] },
16
+ "tokens": { /* full tokens.json-shaped preset: colors, typography, spacing,
17
+ radius, shadows, gradients, materials … */ }
18
+ }
19
+ ```
20
+
21
+ ## The OTA-vs-native boundary
22
+
23
+ - **Content is OTA** — token values, which `material`/`variant`, their params (blur/tint/opacity). Adding a kit that reuses existing primitives is a JSON file, no rebuild.
24
+ - **The engine is native** — the code that *draws* a material (`flat`/`glass`/`frosted`) or a construction `variant` ships in the app binary (iOS SwiftUI, Android Compose, admin web preview).
25
+
26
+ A kit may only name primitives that all renderers implement. `manifest.materials` / `manifest.variants` are the contract; **`core/dev-server/tests/kit-coverage.test.ts`** fails CI if a kit references a primitive the renderers don't yet support. Pushing a kit to an older binary degrades gracefully (unknown material/variant → flat/default).
27
+
28
+ ## Phase 1 kits (surface)
29
+
30
+ | id | name | material |
31
+ |----|------|----------|
32
+ | `liquid-glass` | Liquid Glass | glass |
33
+ | `material` | Flat / Material 3 | flat |
34
+ | `minimal-editorial` | Minimal / Editorial | flat |
35
+ | `dark-luxe` | Dark Luxe | flat |
36
+
37
+ Phase 2 adds construction kits (neumorphic, brutalist, skeuomorphic, playful) that introduce `variant` languages. See `docs/superpowers/specs/2026-06-08-design-kits-design.md`.
38
+
39
+ ## Kit typography (WS-B)
40
+
41
+ A kit may carry `tokens.typography` to give itself a typographic identity —
42
+ per-style entries (`title1`, `body`, `heroTitle`, …) with an optional `family`.
43
+ Three hard rules:
44
+
45
+ 1. **Entries must be COMPLETE — `size` + `weight` + `line`, always.** Native
46
+ token decode is strict (iOS `Tokens.swift` / Android `Tokens.kt`): one
47
+ family-only entry (`{"family": "Georgia"}`) makes the whole `DesignTokens`
48
+ decode throw. Kits are registered STANDALONE in places (KitSnapshotTests,
49
+ branded-lite), so you cannot rely on the serve-time deep-merge to fill the
50
+ gaps. Gated in CI by `tools/release-build/app-tokens-typography.test.ts`.
51
+ 2. **A `family` must be shippable, or it degrades — and is now reported.**
52
+ An unregistered family still renders as the system font (iOS/Android/macOS)
53
+ or falls down the web stack; what changed is that this is no longer silent.
54
+ Name a family outside the catalog and `himi fonts check`, `himi validate`,
55
+ `himi push` and `POST /v1/validate` all fail with an error, CI fails on
56
+ `app-tokens-family.test.ts`, and a family that slips all of those emits
57
+ `font_family_unresolved` once at runtime.
58
+ Registration is no longer a per-app boot concern either: apps declare faces
59
+ and `FontInstaller` registers them from that declaration. Pick a family with
60
+ `himi fonts list` — see [docs/fonts.md](../../docs/fonts.md) and
61
+ [RFC 0056](../../core/schema/RFC/0056-ota-custom-fonts.md).
62
+ 3. **Kit family beats the brand fill.** `branding.fontFamily`
63
+ (himi-branded-lite) only fills entries that ship no family of their own — a
64
+ kit's per-entry family always survives.
65
+
66
+ Cross-platform-safe family values today:
67
+
68
+ | family | iOS | Android | macOS | web |
69
+ |--------|-----|---------|-------|-----|
70
+ | *(none)* | system (SF) | system (Roboto) | system (SF) | Madefor Text stack |
71
+ | `Wix Madefor Text` / `Wix Madefor Display` | ✅ registered | ✅ registered | ✅ himi-player | ✅ @font-face (web host + admin) |
72
+ | `Georgia` | ✅ OS font | ≈ `serif` fallback | ✅ OS font | ✅ OS font; web stacks tail with `serif` |
73
+
74
+ The web renderer resolves every family through `fontStack()`
75
+ (`core/web-renderer/src/tokens.ts`): serif-shaped families get a `serif`
76
+ generic tail (so a missing Georgia degrades to *a* serif, never into Madefor),
77
+ sans families fall back to the Madefor+system chain. On Android, `Tokens.kt`
78
+ carries a builtin degrade for the catalog's safe serif: an unregistered
79
+ `Georgia` resolves to the platform generic `serif` (never the default sans).
80
+
81
+ ### Per-kit typographic personalities (WS-B phase 2)
82
+
83
+ Every kit ships a full scale (`display`, `heroTitle`, `title1`–`title3`,
84
+ `body`, `bodyEmphasis`, `footnote`, `caption`) — complete entries only. One
85
+ line each (titles / body / the idea):
86
+
87
+ | kit | titles | body | personality |
88
+ |-----|--------|------|-------------|
89
+ | `minimal-editorial` | Georgia 400–500, −0.2…−0.5 tracking, line 1.25–1.4, title1 30 | Madefor Text 400/1.5 | literary magazine — light serif headlines, airy |
90
+ | `dark-luxe` | Madefor Display 400–500, **+0.8…+1.5 tracking**, title1 26 | Madefor Text 400/1.5/+0.2 | five-star hotel — smaller-but-spaced luxury caps feel |
91
+ | `brutalist` | Madefor Display 700–800, −0.3…−1.0 tracking, line 1.1, display 44 → title3 18 | Madefor Text 400/1.4 | poster-loud — heavy, tight, big size jumps; caption goes Display 700 |
92
+ | `playful` | Madefor Display 700, line 1.3–1.35, title1 31 (bigger than base) | Madefor Text 400/1.5 | friendly app — bouncy, roomy, bold |
93
+ | `neumorphic` | Madefor Text 500–600, line 1.35–1.45, title1 26 | Madefor Text 400/1.5/+0.1 | calm — soft mid weights, muted level contrast |
94
+ | `skeuomorphic` | *(system)* 600–700, classic iOS scale (title1 28) | *(system)* 400/1.45 | classic iOS — semibold titles; footnote/caption in Georgia (“printed label”) |
95
+ | `material` | *(system)* 400 large titles / 500 mid (title1 28) | *(system)* 400/1.5/+0.15 | Material — Roboto IS the identity on Android; positive small-end tracking |
96
+ | `liquid-glass` | *(system)* 600, −0.2…−0.6 tracking, display 40 / title1 32 | *(system)* 400/1.45/−0.1 | Apple-adjacent — SF is the glass identity, larger display/title1 |
97
+ | `shadcn` | Madefor Text 600, line 1.25, compact (title1 24) | Madefor Text 400/1.45, body 15 | dashboard — tight professional scale, neutral tracking |
98
+
99
+ Two CI gates keep this honest (`core/dev-server/tests/kit-coverage.test.ts`):
100
+ every kit must ship a typography block covering at least `title1` + `body`,
101
+ and no `title1` (family, weight, size, letterSpacing) tuple may be shared by
102
+ more than 2 kits — the anti-regression against drifting back to uniform type.
103
+ Keep `line` in the **1.1–1.5** range: iOS converts the multiple to additive
104
+ line spacing, Android consumes the multiple, web treats it unitless — values
105
+ outside that band diverge visibly across platforms.
106
+
107
+ ## Color coherence (the complete ladder)
108
+
109
+ Every kit ships a **complete** color ladder — the union of tokens the built
110
+ screens of all 16 CLI templates reference, plus the semantic completions:
111
+
112
+ ```
113
+ surfacePrimary surfaceSecondary surfaceElevated surfaceSubtle
114
+ textPrimary textSecondary textTertiary textOnAccent
115
+ accentPrimary accentSecondary accentSurface border
116
+ success successSurface warning warningSurface danger dangerSurface
117
+ ```
118
+
119
+ An incomplete ladder is a **bug**, not a shorthand: kit colors merge arm-by-arm
120
+ over the app's base tokens, so any missing token leaks the base value through —
121
+ the original dark-luxe kit had no `surfaceElevated`, and every card glared
122
+ white (`#FFFFFF`) on its near-black page. **`core/dev-server/tests/kit-coherence.test.ts`**
123
+ gates this in CI: full ladder present; per arm textPrimary ≥ 7:1 / textSecondary
124
+ ≥ 4.5:1 / textTertiary ≥ 3:1 on both `surfacePrimary` and `surfaceElevated`;
125
+ elevation subtle (≤ 2:1 vs the page, dark arms stepping *lighter* as elevation
126
+ rises); accents ≥ 3:1 on the page with textOnAccent ≥ 4.5:1; semantic colors
127
+ ≥ 3:1 on the page and on their own surfaces.
128
+
129
+ Deliberate judgment calls (the 2026-07 color reconsideration):
130
+
131
+ - **playful** — the hot-pink `textPrimary` failed 7:1 everywhere; legibility
132
+ wins. Body ink is now a deep berry (`#821B47`) that keeps the personality;
133
+ the original pink lives on as `accentSecondary` for headings-scale accents,
134
+ and `accentPrimary` deepened one notch (`#DE1673`) so white button text passes.
135
+ - **brutalist** — white cards ON yellow stay: stark contrast IS the language
136
+ (text on the yellow is black at ~18:1, verified). Radius stays 0; semantics
137
+ are primary-color loud on purpose.
138
+ - **dark-luxe** — inherently dark: light arm == dark arm by design. The gold
139
+ `glow` shadow stays gold in the kit but is **retinted to the brand accent at
140
+ merge time** (`branding.ts`) so it never clashes with a template's accent.
141
+ - **skeuomorphic / neumorphic** — dark-mode gloss/emboss pairs re-tuned so the
142
+ bevel highlight sits just above the surface and the shadow just below
143
+ (`glossTop/glossBottom`, `neumoLight/neumoDark`); elevation in dark mode comes
144
+ from those pairs plus a gentle lighter step, never from a bright card.
145
+ - **shadcn** — dark `danger` was a *background* red (`#7F1D1D`, 1.6:1 on the
146
+ page); it is now a legible foreground red (`#F87171`) with `dangerSurface`
147
+ carrying the fill role.
@@ -0,0 +1,202 @@
1
+ {
2
+ "id": "brutalist",
3
+ "name": "Brutalist",
4
+ "tags": [
5
+ "construction"
6
+ ],
7
+ "kit": {
8
+ "material": "flat",
9
+ "variant": "brutalist"
10
+ },
11
+ "manifest": {
12
+ "materials": [
13
+ "flat"
14
+ ],
15
+ "variants": [
16
+ "brutalist"
17
+ ]
18
+ },
19
+ "tokens": {
20
+ "colors": {
21
+ "surfacePrimary": {
22
+ "light": "#FFE600",
23
+ "dark": "#0D0D0D"
24
+ },
25
+ "surfaceSecondary": {
26
+ "light": "#FFFFFF",
27
+ "dark": "#1A1A1A"
28
+ },
29
+ "surfaceElevated": {
30
+ "light": "#FFFFFF",
31
+ "dark": "#232323"
32
+ },
33
+ "surfaceSubtle": {
34
+ "light": "#FFEE4D",
35
+ "dark": "#131313"
36
+ },
37
+ "textPrimary": {
38
+ "light": "#000000",
39
+ "dark": "#FFFFFF"
40
+ },
41
+ "textSecondary": {
42
+ "light": "#333333",
43
+ "dark": "#BBBBBB"
44
+ },
45
+ "textTertiary": {
46
+ "light": "#575757",
47
+ "dark": "#8C8C8C"
48
+ },
49
+ "textOnAccent": {
50
+ "light": "#000000",
51
+ "dark": "#000000"
52
+ },
53
+ "accentPrimary": {
54
+ "light": "#E63900",
55
+ "dark": "#FF6A2B"
56
+ },
57
+ "accentSecondary": {
58
+ "light": "#0000E6",
59
+ "dark": "#5C5CFF"
60
+ },
61
+ "accentSurface": {
62
+ "light": "#FFF6B3",
63
+ "dark": "#1F1F1F"
64
+ },
65
+ "success": {
66
+ "light": "#008A2E",
67
+ "dark": "#33E633"
68
+ },
69
+ "successSurface": {
70
+ "light": "#D6FFD6",
71
+ "dark": "#112911"
72
+ },
73
+ "warning": {
74
+ "light": "#7A4D00",
75
+ "dark": "#FFB300"
76
+ },
77
+ "warningSurface": {
78
+ "light": "#FFF3C2",
79
+ "dark": "#292109"
80
+ },
81
+ "danger": {
82
+ "light": "#D60000",
83
+ "dark": "#FF3333"
84
+ },
85
+ "dangerSurface": {
86
+ "light": "#FFD6D6",
87
+ "dark": "#291111"
88
+ },
89
+ "border": {
90
+ "light": "#000000",
91
+ "dark": "#FFFFFF"
92
+ }
93
+ },
94
+ "materials": {
95
+ "flat": {
96
+ "style": "flat",
97
+ "tint": "surfaceSecondary",
98
+ "opacity": 1
99
+ }
100
+ },
101
+ "typography": {
102
+ "display": {
103
+ "size": 44,
104
+ "weight": 800,
105
+ "line": 1.1,
106
+ "letterSpacing": -1.0,
107
+ "family": "Wix Madefor Display"
108
+ },
109
+ "heroTitle": {
110
+ "size": 38,
111
+ "weight": 800,
112
+ "line": 1.1,
113
+ "letterSpacing": -0.8,
114
+ "family": "Wix Madefor Display"
115
+ },
116
+ "title1": {
117
+ "size": 34,
118
+ "weight": 800,
119
+ "line": 1.1,
120
+ "letterSpacing": -0.8,
121
+ "family": "Wix Madefor Display"
122
+ },
123
+ "title2": {
124
+ "size": 24,
125
+ "weight": 800,
126
+ "line": 1.12,
127
+ "letterSpacing": -0.5,
128
+ "family": "Wix Madefor Display"
129
+ },
130
+ "title3": {
131
+ "size": 18,
132
+ "weight": 700,
133
+ "line": 1.15,
134
+ "letterSpacing": -0.3,
135
+ "family": "Wix Madefor Display"
136
+ },
137
+ "body": {
138
+ "size": 16,
139
+ "weight": 400,
140
+ "line": 1.4,
141
+ "letterSpacing": 0,
142
+ "family": "Wix Madefor Text"
143
+ },
144
+ "bodyEmphasis": {
145
+ "size": 16,
146
+ "weight": 700,
147
+ "line": 1.3,
148
+ "letterSpacing": 0,
149
+ "family": "Wix Madefor Text"
150
+ },
151
+ "footnote": {
152
+ "size": 13,
153
+ "weight": 500,
154
+ "line": 1.3,
155
+ "letterSpacing": 0,
156
+ "family": "Wix Madefor Text"
157
+ },
158
+ "caption": {
159
+ "size": 12,
160
+ "weight": 700,
161
+ "line": 1.25,
162
+ "letterSpacing": 0.5,
163
+ "family": "Wix Madefor Display"
164
+ }
165
+ },
166
+ "variantParams": {
167
+ "brutalist": {
168
+ "offset": 4,
169
+ "borderWidth": 3,
170
+ "shadowToken": "border"
171
+ }
172
+ },
173
+ "radius": {
174
+ "sm": 0,
175
+ "md": 0,
176
+ "pill": 0
177
+ },
178
+ "tabBar": {
179
+ "background": {
180
+ "color": "surfacePrimary"
181
+ },
182
+ "shape": {
183
+ "kind": "full"
184
+ },
185
+ "border": {
186
+ "topHairline": true,
187
+ "color": "border",
188
+ "width": 3
189
+ },
190
+ "selectedIndicator": {
191
+ "kind": "colorOnly",
192
+ "color": "accentPrimary"
193
+ },
194
+ "activeTint": "accentPrimary",
195
+ "inactiveTint": "textPrimary",
196
+ "labels": {
197
+ "show": true
198
+ },
199
+ "motion": "crisp"
200
+ }
201
+ }
202
+ }
@@ -0,0 +1,197 @@
1
+ {
2
+ "id": "dark-luxe",
3
+ "name": "Dark Luxe",
4
+ "tags": [
5
+ "surface"
6
+ ],
7
+ "kit": {
8
+ "material": "flat",
9
+ "variant": "default"
10
+ },
11
+ "manifest": {
12
+ "materials": [
13
+ "flat"
14
+ ],
15
+ "variants": []
16
+ },
17
+ "tokens": {
18
+ "colors": {
19
+ "surfacePrimary": {
20
+ "light": "#0B0D12",
21
+ "dark": "#0B0D12"
22
+ },
23
+ "surfaceSecondary": {
24
+ "light": "#14171F",
25
+ "dark": "#14171F"
26
+ },
27
+ "surfaceElevated": {
28
+ "light": "#1B1F29",
29
+ "dark": "#1B1F29"
30
+ },
31
+ "surfaceSubtle": {
32
+ "light": "#101319",
33
+ "dark": "#101319"
34
+ },
35
+ "textPrimary": {
36
+ "light": "#F1E9D2",
37
+ "dark": "#F1E9D2"
38
+ },
39
+ "textSecondary": {
40
+ "light": "#9AA0AC",
41
+ "dark": "#9AA0AC"
42
+ },
43
+ "textTertiary": {
44
+ "light": "#777E8C",
45
+ "dark": "#777E8C"
46
+ },
47
+ "textOnAccent": {
48
+ "light": "#1A1303",
49
+ "dark": "#1A1303"
50
+ },
51
+ "accentPrimary": {
52
+ "light": "#E9C46A",
53
+ "dark": "#E9C46A"
54
+ },
55
+ "accentSecondary": {
56
+ "light": "#C9A86A",
57
+ "dark": "#C9A86A"
58
+ },
59
+ "accentSurface": {
60
+ "light": "#221D11",
61
+ "dark": "#221D11"
62
+ },
63
+ "success": {
64
+ "light": "#95C9A2",
65
+ "dark": "#95C9A2"
66
+ },
67
+ "successSurface": {
68
+ "light": "#131F17",
69
+ "dark": "#131F17"
70
+ },
71
+ "warning": {
72
+ "light": "#E0B354",
73
+ "dark": "#E0B354"
74
+ },
75
+ "warningSurface": {
76
+ "light": "#211B0E",
77
+ "dark": "#211B0E"
78
+ },
79
+ "danger": {
80
+ "light": "#E08983",
81
+ "dark": "#E08983"
82
+ },
83
+ "dangerSurface": {
84
+ "light": "#221214",
85
+ "dark": "#221214"
86
+ },
87
+ "border": {
88
+ "light": "#262B36",
89
+ "dark": "#262B36"
90
+ }
91
+ },
92
+ "materials": {
93
+ "flat": {
94
+ "style": "flat",
95
+ "tint": "surfaceSecondary",
96
+ "opacity": 1
97
+ }
98
+ },
99
+ "typography": {
100
+ "display": {
101
+ "size": 32,
102
+ "weight": 400,
103
+ "line": 1.25,
104
+ "letterSpacing": 1.5,
105
+ "family": "Wix Madefor Display"
106
+ },
107
+ "heroTitle": {
108
+ "size": 28,
109
+ "weight": 400,
110
+ "line": 1.3,
111
+ "letterSpacing": 1.2,
112
+ "family": "Wix Madefor Display"
113
+ },
114
+ "title1": {
115
+ "size": 26,
116
+ "weight": 400,
117
+ "line": 1.3,
118
+ "letterSpacing": 1.2,
119
+ "family": "Wix Madefor Display"
120
+ },
121
+ "title2": {
122
+ "size": 21,
123
+ "weight": 500,
124
+ "line": 1.32,
125
+ "letterSpacing": 1.0,
126
+ "family": "Wix Madefor Display"
127
+ },
128
+ "title3": {
129
+ "size": 17,
130
+ "weight": 500,
131
+ "line": 1.35,
132
+ "letterSpacing": 0.8,
133
+ "family": "Wix Madefor Display"
134
+ },
135
+ "body": {
136
+ "size": 16,
137
+ "weight": 400,
138
+ "line": 1.5,
139
+ "letterSpacing": 0.2,
140
+ "family": "Wix Madefor Text"
141
+ },
142
+ "bodyEmphasis": {
143
+ "size": 16,
144
+ "weight": 500,
145
+ "line": 1.5,
146
+ "letterSpacing": 0.3,
147
+ "family": "Wix Madefor Text"
148
+ },
149
+ "footnote": {
150
+ "size": 13,
151
+ "weight": 400,
152
+ "line": 1.45,
153
+ "letterSpacing": 0.6,
154
+ "family": "Wix Madefor Text"
155
+ },
156
+ "caption": {
157
+ "size": 12,
158
+ "weight": 400,
159
+ "line": 1.4,
160
+ "letterSpacing": 1.0,
161
+ "family": "Wix Madefor Text"
162
+ }
163
+ },
164
+ "shadows": {
165
+ "glow": {
166
+ "color": "#E9C46A",
167
+ "opacity": 0.12,
168
+ "radius": 24,
169
+ "x": 0,
170
+ "y": 0
171
+ }
172
+ },
173
+ "radius": {
174
+ "sm": 10,
175
+ "md": 14,
176
+ "pill": 22
177
+ },
178
+ "tabBar": {
179
+ "background": {
180
+ "color": "surfacePrimary"
181
+ },
182
+ "shape": {
183
+ "kind": "full"
184
+ },
185
+ "selectedIndicator": {
186
+ "kind": "underline",
187
+ "color": "accentPrimary"
188
+ },
189
+ "activeTint": "accentPrimary",
190
+ "inactiveTint": "textSecondary",
191
+ "labels": {
192
+ "show": true
193
+ },
194
+ "motion": "smooth"
195
+ }
196
+ }
197
+ }