@wix/himalaya-cli 0.1064.0 → 0.1066.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 (48) hide show
  1. package/dist/cli.mjs +175 -45
  2. package/dist/preview-assets.generated.bin +0 -0
  3. package/guides/wix-site-to-himi-app.md +2 -2
  4. package/package.json +2 -2
  5. package/templates/beauty-salon/dev/index.ts +1 -5
  6. package/templates/beauty-salon/tier5-src/_shared/himi-layouts.ts +8 -6
  7. package/templates/beauty-salon/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  8. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +5 -5
  9. package/templates/branded-lite/dev/index.ts +2 -6
  10. package/templates/branded-lite/tier5-src/_shared/himi-layouts.ts +8 -6
  11. package/templates/branded-lite/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  12. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +5 -5
  13. package/templates/community-club/dev/index.ts +1 -5
  14. package/templates/community-club/tier5-src/_shared/himi-layouts.ts +8 -6
  15. package/templates/community-club/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  16. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +5 -5
  17. package/templates/events-meetups/dev/index.ts +1 -5
  18. package/templates/events-meetups/tier5-src/_shared/himi-layouts.ts +8 -6
  19. package/templates/events-meetups/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  20. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +5 -5
  21. package/templates/gym-fitness/dev/index.ts +1 -5
  22. package/templates/gym-fitness/tier5-src/_shared/himi-layouts.ts +8 -6
  23. package/templates/gym-fitness/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  24. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +5 -5
  25. package/templates/nonprofit-cause/dev/index.ts +1 -5
  26. package/templates/nonprofit-cause/tier5-src/_shared/himi-layouts.ts +8 -6
  27. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  28. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +5 -5
  29. package/templates/podcast-creator/dev/index.ts +1 -5
  30. package/templates/podcast-creator/tier5-src/_shared/himi-layouts.ts +8 -6
  31. package/templates/podcast-creator/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  32. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +5 -5
  33. package/templates/portfolio-creative/dev/index.ts +1 -5
  34. package/templates/portfolio-creative/tier5-src/_shared/himi-layouts.ts +8 -6
  35. package/templates/portfolio-creative/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  36. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +5 -5
  37. package/templates/restaurant/dev/index.ts +1 -5
  38. package/templates/restaurant/tier5-src/_shared/himi-layouts.ts +8 -6
  39. package/templates/restaurant/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  40. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +5 -5
  41. package/templates/retail-store/dev/index.ts +2 -6
  42. package/templates/retail-store/tier5-src/_shared/himi-layouts.ts +8 -6
  43. package/templates/retail-store/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  44. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +5 -5
  45. package/templates/yoga-studio/dev/index.ts +1 -5
  46. package/templates/yoga-studio/tier5-src/_shared/himi-layouts.ts +8 -6
  47. package/templates/yoga-studio/tier5-src/himi-branded-lite-appearance/index.ts +13 -18
  48. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +5 -5
@@ -19,11 +19,10 @@ import {
19
19
  interface State {
20
20
  /** The pending accent id (persisted on Apply, re-read by the home worker). */
21
21
  pendingAccent: string;
22
- /** The app's brand color (from the onAppear param) — the "brand" accent swatch
23
- * and the accent fallback resolve to it. */
24
- brandAccent: string;
25
- // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but
26
- // NOT per-row `$item.` fields, so each static swatch binds its own flat field.
22
+ // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but NOT
23
+ // per-row `$item.` fields, so each static swatch binds its own flat field.
24
+ // `accentHex_brand` is "" on purpose: the swatch keeps its own accentPrimary token,
25
+ // which is the brand (see accentColor in _shared/himi-layouts.ts).
27
26
  accentHex_brand: string;
28
27
  accentHex_amber: string;
29
28
  accentHex_terracotta: string;
@@ -54,7 +53,7 @@ function paramValue(arg: unknown, key: string): string {
54
53
  return typeof result === "string" ? result : "";
55
54
  }
56
55
 
57
- function build(pendingAccent: string, brandAccent: string): State {
56
+ function build(pendingAccent: string): State {
58
57
  const flags: Record<string, boolean> = {};
59
58
  for (const id of ACCENT_IDS) {
60
59
  flags[`selected_${id}`] = id === pendingAccent;
@@ -63,8 +62,7 @@ function build(pendingAccent: string, brandAccent: string): State {
63
62
  return {
64
63
  ...flags,
65
64
  pendingAccent,
66
- brandAccent,
67
- accentHex_brand: brandAccent || ACCENT_META.amber.hex,
65
+ accentHex_brand: "",
68
66
  accentHex_amber: ACCENT_META.amber.hex,
69
67
  accentHex_terracotta: ACCENT_META.terracotta.hex,
70
68
  accentHex_sage: ACCENT_META.sage.hex,
@@ -84,24 +82,21 @@ const svc = defineService<State, {
84
82
  apply: (state: State) => State;
85
83
  reset: (state: State) => State;
86
84
  }>("himiBrandedAppearance", {
87
- state: build(initialAccent(), ""),
85
+ state: build(initialAccent()),
88
86
  actions: {
89
- // onAppear: re-read the persisted accent + the app's brand color so the swatches
90
- // reflect reality (the brand swatch tints to the app's own color).
91
- sync: (state, arg) => {
92
- const brandAccent = paramValue(arg, "brandAccent") || state.brandAccent;
93
- return build(initialAccent(), brandAccent);
94
- },
87
+ // onAppear: re-read the persisted accent so the swatches reflect reality. An older
88
+ // descriptor may still pass a `brandAccent`; it is not used (the Brand swatch is the token).
89
+ sync: () => build(initialAccent()),
95
90
  // Tapping an accent swatch marks it pending (persisted on Apply).
96
91
  selectAccent: (state, arg) => {
97
92
  const id = paramValue(arg, "id");
98
93
  if (!isAccentId(id)) return state;
99
- return build(id, state.brandAccent);
94
+ return build(id);
100
95
  },
101
96
  // Apply: persist the accent; the home worker re-reads it on launch.
102
97
  apply: (state) => {
103
98
  try { storage()?.setItem(ACCENT_KEY, state.pendingAccent); } catch { /* best-effort */ }
104
- return build(state.pendingAccent, state.brandAccent);
99
+ return build(state.pendingAccent);
105
100
  },
106
101
  // Reset: clear the override, reverting to the brand accent.
107
102
  reset: (state) => {
@@ -110,7 +105,7 @@ const svc = defineService<State, {
110
105
  if (store?.removeItem) store.removeItem(ACCENT_KEY);
111
106
  else store?.setItem(ACCENT_KEY, "");
112
107
  } catch { /* best-effort */ }
113
- return build(DEFAULT_ACCENT, state.brandAccent);
108
+ return build(DEFAULT_ACCENT);
114
109
  },
115
110
  },
116
111
  });
@@ -338,11 +338,11 @@ function storedLayout(): string | null {
338
338
  function storedAccent(): string {
339
339
  try { return storage()?.getItem(ACCENT_KEY) ?? ""; } catch { return ""; }
340
340
  }
341
- // user accent (storage) → brand color (onAppear param) fallback. Always a valid
342
- // hex so backgroundColorPath binds cleanly; "" brand param at first paint just
343
- // falls the surface back to its accentPrimary token.
344
- function resolveAccent(arg: unknown): string {
345
- return resolveAccentColor(storedAccent(), paramValue(arg, "brandAccent"));
341
+ // The member's chosen accent (storage), else "" — the surface keeps its accentPrimary token,
342
+ // which is the brand. An older descriptor's `brandAccent` param is ignored: it was a copy of
343
+ // the brand that a hand edit of tokens.json left behind.
344
+ function resolveAccent(_arg?: unknown): string {
345
+ return resolveAccentColor(storedAccent());
346
346
  }
347
347
 
348
348
  // onAppear param reader (the himi-branded-lite-cms pattern): the descriptor
@@ -2016,7 +2016,6 @@ const SCREENS = runtimeSetting({
2016
2016
  "verticals": "[\"plans\",\"feed\",\"events\",\"blog\",\"podcasts\"]",
2017
2017
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Latest episode\",\"dataSource\":\"episodeFirst.section0\",\"emptyWhen\":[\"noLatestPost\"]},{\"id\":\"section_1\",\"label\":\"All episodes\",\"dataSource\":\"episodeFirst.section1\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_2\",\"label\":\"Discover 3\",\"dataSource\":\"episodeFirst.section2\"},{\"id\":\"section_3\",\"label\":\"Latest activity\",\"dataSource\":\"episodeFirst.section3\"}]",
2018
2018
  "ownerDefaultLayout": "episodeFirst",
2019
- "brandAccent": "#6E4BFF",
2020
2019
  "copy": "{\"home.allEpisodesCta\":\"Browse all episodes\",\"home.episodesTitle\":\"All episodes\",\"home.latestEpisodeCaption\":\"Latest episode\",\"home.listenCta\":\"Listen now\",\"home.listenersCaption\":\"Our listeners\",\"home.notesCta\":\"Read the show notes\",\"home.onboardingBody\":\"Catch the latest episode, read the show notes, meet other listeners and support the show — all in one place.\",\"home.supportSubtitle\":\"Join a membership and keep new episodes coming.\",\"home.supportTitle\":\"Support the show\"}"
2021
2020
  }
2022
2021
  }
@@ -4458,10 +4457,7 @@ const SCREENS = runtimeSetting({
4458
4457
  "bottom": 28
4459
4458
  },
4460
4459
  "onAppear": {
4461
- "actionId": "tier5.himiBrandedAppearance.sync",
4462
- "params": {
4463
- "brandAccent": "#6E4BFF"
4464
- }
4460
+ "actionId": "tier5.himiBrandedAppearance.sync"
4465
4461
  }
4466
4462
  }
4467
4463
  },
@@ -134,12 +134,14 @@ export function isAccentId(x: unknown): x is AccentId {
134
134
  return typeof x === "string" && (ACCENT_IDS as readonly string[]).includes(x);
135
135
  }
136
136
 
137
- /** Resolve an accent id to a concrete hex, falling back to the app's brand color
138
- * for `brand` or any unknown value — so `backgroundColorPath` always binds to a
139
- * valid color. */
140
- export function accentColor(id: string, brandFallback: string): string {
141
- if (isAccentId(id) && ACCENT_META[id].hex) return ACCENT_META[id].hex;
142
- return brandFallback;
137
+ /** Resolve an accent id to its curated hex, or "" for `brand` and any unknown value.
138
+ * "" is deliberate: a bound `backgroundColorPath` that resolves empty keeps the surface's
139
+ * own `accentPrimary` token on every renderer, and that token IS the brand — after a
140
+ * `site analyze --apply` and after a hand edit of tokens.json alike. A brand colour copied
141
+ * into the descriptor instead (the old `brandAccent` param) went stale on the second path:
142
+ * the Home pill kept the template's blue after a rebrand. */
143
+ export function accentColor(id: string): string {
144
+ return isAccentId(id) ? ACCENT_META[id].hex : "";
143
145
  }
144
146
 
145
147
  /** Content-derived default: booking intent > deep catalog > editorial (blog-forward) > balanced.
@@ -19,11 +19,10 @@ import {
19
19
  interface State {
20
20
  /** The pending accent id (persisted on Apply, re-read by the home worker). */
21
21
  pendingAccent: string;
22
- /** The app's brand color (from the onAppear param) — the "brand" accent swatch
23
- * and the accent fallback resolve to it. */
24
- brandAccent: string;
25
- // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but
26
- // NOT per-row `$item.` fields, so each static swatch binds its own flat field.
22
+ // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but NOT
23
+ // per-row `$item.` fields, so each static swatch binds its own flat field.
24
+ // `accentHex_brand` is "" on purpose: the swatch keeps its own accentPrimary token,
25
+ // which is the brand (see accentColor in _shared/himi-layouts.ts).
27
26
  accentHex_brand: string;
28
27
  accentHex_amber: string;
29
28
  accentHex_terracotta: string;
@@ -54,7 +53,7 @@ function paramValue(arg: unknown, key: string): string {
54
53
  return typeof result === "string" ? result : "";
55
54
  }
56
55
 
57
- function build(pendingAccent: string, brandAccent: string): State {
56
+ function build(pendingAccent: string): State {
58
57
  const flags: Record<string, boolean> = {};
59
58
  for (const id of ACCENT_IDS) {
60
59
  flags[`selected_${id}`] = id === pendingAccent;
@@ -63,8 +62,7 @@ function build(pendingAccent: string, brandAccent: string): State {
63
62
  return {
64
63
  ...flags,
65
64
  pendingAccent,
66
- brandAccent,
67
- accentHex_brand: brandAccent || ACCENT_META.amber.hex,
65
+ accentHex_brand: "",
68
66
  accentHex_amber: ACCENT_META.amber.hex,
69
67
  accentHex_terracotta: ACCENT_META.terracotta.hex,
70
68
  accentHex_sage: ACCENT_META.sage.hex,
@@ -84,24 +82,21 @@ const svc = defineService<State, {
84
82
  apply: (state: State) => State;
85
83
  reset: (state: State) => State;
86
84
  }>("himiBrandedAppearance", {
87
- state: build(initialAccent(), ""),
85
+ state: build(initialAccent()),
88
86
  actions: {
89
- // onAppear: re-read the persisted accent + the app's brand color so the swatches
90
- // reflect reality (the brand swatch tints to the app's own color).
91
- sync: (state, arg) => {
92
- const brandAccent = paramValue(arg, "brandAccent") || state.brandAccent;
93
- return build(initialAccent(), brandAccent);
94
- },
87
+ // onAppear: re-read the persisted accent so the swatches reflect reality. An older
88
+ // descriptor may still pass a `brandAccent`; it is not used (the Brand swatch is the token).
89
+ sync: () => build(initialAccent()),
95
90
  // Tapping an accent swatch marks it pending (persisted on Apply).
96
91
  selectAccent: (state, arg) => {
97
92
  const id = paramValue(arg, "id");
98
93
  if (!isAccentId(id)) return state;
99
- return build(id, state.brandAccent);
94
+ return build(id);
100
95
  },
101
96
  // Apply: persist the accent; the home worker re-reads it on launch.
102
97
  apply: (state) => {
103
98
  try { storage()?.setItem(ACCENT_KEY, state.pendingAccent); } catch { /* best-effort */ }
104
- return build(state.pendingAccent, state.brandAccent);
99
+ return build(state.pendingAccent);
105
100
  },
106
101
  // Reset: clear the override, reverting to the brand accent.
107
102
  reset: (state) => {
@@ -110,7 +105,7 @@ const svc = defineService<State, {
110
105
  if (store?.removeItem) store.removeItem(ACCENT_KEY);
111
106
  else store?.setItem(ACCENT_KEY, "");
112
107
  } catch { /* best-effort */ }
113
- return build(DEFAULT_ACCENT, state.brandAccent);
108
+ return build(DEFAULT_ACCENT);
114
109
  },
115
110
  },
116
111
  });
@@ -338,11 +338,11 @@ function storedLayout(): string | null {
338
338
  function storedAccent(): string {
339
339
  try { return storage()?.getItem(ACCENT_KEY) ?? ""; } catch { return ""; }
340
340
  }
341
- // user accent (storage) → brand color (onAppear param) fallback. Always a valid
342
- // hex so backgroundColorPath binds cleanly; "" brand param at first paint just
343
- // falls the surface back to its accentPrimary token.
344
- function resolveAccent(arg: unknown): string {
345
- return resolveAccentColor(storedAccent(), paramValue(arg, "brandAccent"));
341
+ // The member's chosen accent (storage), else "" — the surface keeps its accentPrimary token,
342
+ // which is the brand. An older descriptor's `brandAccent` param is ignored: it was a copy of
343
+ // the brand that a hand edit of tokens.json left behind.
344
+ function resolveAccent(_arg?: unknown): string {
345
+ return resolveAccentColor(storedAccent());
346
346
  }
347
347
 
348
348
  // onAppear param reader (the himi-branded-lite-cms pattern): the descriptor
@@ -1886,7 +1886,6 @@ const SCREENS = runtimeSetting({
1886
1886
  "verticals": "[\"store\",\"portfolio\",\"events\",\"blog\"]",
1887
1887
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Selected work\",\"dataSource\":\"showcaseFirst.section0\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"showcaseFirst.section1\"},{\"id\":\"section_2\",\"label\":\"Prints & editions\",\"dataSource\":\"showcaseFirst.section2\",\"emptyWhen\":[\"noFeaturedProducts\"]},{\"id\":\"section_3\",\"label\":\"Exhibitions & fairs\",\"dataSource\":\"showcaseFirst.section3\"}]",
1888
1888
  "ownerDefaultLayout": "showcaseFirst",
1889
- "brandAccent": "#B0511F",
1890
1889
  "copy": "{\"home.aboutTitle\":\"About the studio\",\"home.contactCta\":\"Get in touch\",\"home.exhibitionsTitle\":\"Exhibitions & fairs\",\"home.onboardingBody\":\"Browse the latest work, pick up a print, and catch the next exhibition — all in one place.\",\"home.printsTitle\":\"Prints & editions\",\"home.viewWorkCta\":\"View all work\",\"home.workSubtitle\":\"The latest pieces, straight from the studio.\",\"home.workTitle\":\"Selected work\"}"
1891
1890
  }
1892
1891
  }
@@ -4327,10 +4326,7 @@ const SCREENS = runtimeSetting({
4327
4326
  "bottom": 28
4328
4327
  },
4329
4328
  "onAppear": {
4330
- "actionId": "tier5.himiBrandedAppearance.sync",
4331
- "params": {
4332
- "brandAccent": "#B0511F"
4333
- }
4329
+ "actionId": "tier5.himiBrandedAppearance.sync"
4334
4330
  }
4335
4331
  }
4336
4332
  },
@@ -134,12 +134,14 @@ export function isAccentId(x: unknown): x is AccentId {
134
134
  return typeof x === "string" && (ACCENT_IDS as readonly string[]).includes(x);
135
135
  }
136
136
 
137
- /** Resolve an accent id to a concrete hex, falling back to the app's brand color
138
- * for `brand` or any unknown value — so `backgroundColorPath` always binds to a
139
- * valid color. */
140
- export function accentColor(id: string, brandFallback: string): string {
141
- if (isAccentId(id) && ACCENT_META[id].hex) return ACCENT_META[id].hex;
142
- return brandFallback;
137
+ /** Resolve an accent id to its curated hex, or "" for `brand` and any unknown value.
138
+ * "" is deliberate: a bound `backgroundColorPath` that resolves empty keeps the surface's
139
+ * own `accentPrimary` token on every renderer, and that token IS the brand — after a
140
+ * `site analyze --apply` and after a hand edit of tokens.json alike. A brand colour copied
141
+ * into the descriptor instead (the old `brandAccent` param) went stale on the second path:
142
+ * the Home pill kept the template's blue after a rebrand. */
143
+ export function accentColor(id: string): string {
144
+ return isAccentId(id) ? ACCENT_META[id].hex : "";
143
145
  }
144
146
 
145
147
  /** Content-derived default: booking intent > deep catalog > editorial (blog-forward) > balanced.
@@ -19,11 +19,10 @@ import {
19
19
  interface State {
20
20
  /** The pending accent id (persisted on Apply, re-read by the home worker). */
21
21
  pendingAccent: string;
22
- /** The app's brand color (from the onAppear param) — the "brand" accent swatch
23
- * and the accent fallback resolve to it. */
24
- brandAccent: string;
25
- // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but
26
- // NOT per-row `$item.` fields, so each static swatch binds its own flat field.
22
+ // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but NOT
23
+ // per-row `$item.` fields, so each static swatch binds its own flat field.
24
+ // `accentHex_brand` is "" on purpose: the swatch keeps its own accentPrimary token,
25
+ // which is the brand (see accentColor in _shared/himi-layouts.ts).
27
26
  accentHex_brand: string;
28
27
  accentHex_amber: string;
29
28
  accentHex_terracotta: string;
@@ -54,7 +53,7 @@ function paramValue(arg: unknown, key: string): string {
54
53
  return typeof result === "string" ? result : "";
55
54
  }
56
55
 
57
- function build(pendingAccent: string, brandAccent: string): State {
56
+ function build(pendingAccent: string): State {
58
57
  const flags: Record<string, boolean> = {};
59
58
  for (const id of ACCENT_IDS) {
60
59
  flags[`selected_${id}`] = id === pendingAccent;
@@ -63,8 +62,7 @@ function build(pendingAccent: string, brandAccent: string): State {
63
62
  return {
64
63
  ...flags,
65
64
  pendingAccent,
66
- brandAccent,
67
- accentHex_brand: brandAccent || ACCENT_META.amber.hex,
65
+ accentHex_brand: "",
68
66
  accentHex_amber: ACCENT_META.amber.hex,
69
67
  accentHex_terracotta: ACCENT_META.terracotta.hex,
70
68
  accentHex_sage: ACCENT_META.sage.hex,
@@ -84,24 +82,21 @@ const svc = defineService<State, {
84
82
  apply: (state: State) => State;
85
83
  reset: (state: State) => State;
86
84
  }>("himiBrandedAppearance", {
87
- state: build(initialAccent(), ""),
85
+ state: build(initialAccent()),
88
86
  actions: {
89
- // onAppear: re-read the persisted accent + the app's brand color so the swatches
90
- // reflect reality (the brand swatch tints to the app's own color).
91
- sync: (state, arg) => {
92
- const brandAccent = paramValue(arg, "brandAccent") || state.brandAccent;
93
- return build(initialAccent(), brandAccent);
94
- },
87
+ // onAppear: re-read the persisted accent so the swatches reflect reality. An older
88
+ // descriptor may still pass a `brandAccent`; it is not used (the Brand swatch is the token).
89
+ sync: () => build(initialAccent()),
95
90
  // Tapping an accent swatch marks it pending (persisted on Apply).
96
91
  selectAccent: (state, arg) => {
97
92
  const id = paramValue(arg, "id");
98
93
  if (!isAccentId(id)) return state;
99
- return build(id, state.brandAccent);
94
+ return build(id);
100
95
  },
101
96
  // Apply: persist the accent; the home worker re-reads it on launch.
102
97
  apply: (state) => {
103
98
  try { storage()?.setItem(ACCENT_KEY, state.pendingAccent); } catch { /* best-effort */ }
104
- return build(state.pendingAccent, state.brandAccent);
99
+ return build(state.pendingAccent);
105
100
  },
106
101
  // Reset: clear the override, reverting to the brand accent.
107
102
  reset: (state) => {
@@ -110,7 +105,7 @@ const svc = defineService<State, {
110
105
  if (store?.removeItem) store.removeItem(ACCENT_KEY);
111
106
  else store?.setItem(ACCENT_KEY, "");
112
107
  } catch { /* best-effort */ }
113
- return build(DEFAULT_ACCENT, state.brandAccent);
108
+ return build(DEFAULT_ACCENT);
114
109
  },
115
110
  },
116
111
  });
@@ -338,11 +338,11 @@ function storedLayout(): string | null {
338
338
  function storedAccent(): string {
339
339
  try { return storage()?.getItem(ACCENT_KEY) ?? ""; } catch { return ""; }
340
340
  }
341
- // user accent (storage) → brand color (onAppear param) fallback. Always a valid
342
- // hex so backgroundColorPath binds cleanly; "" brand param at first paint just
343
- // falls the surface back to its accentPrimary token.
344
- function resolveAccent(arg: unknown): string {
345
- return resolveAccentColor(storedAccent(), paramValue(arg, "brandAccent"));
341
+ // The member's chosen accent (storage), else "" — the surface keeps its accentPrimary token,
342
+ // which is the brand. An older descriptor's `brandAccent` param is ignored: it was a copy of
343
+ // the brand that a hand edit of tokens.json left behind.
344
+ function resolveAccent(_arg?: unknown): string {
345
+ return resolveAccentColor(storedAccent());
346
346
  }
347
347
 
348
348
  // onAppear param reader (the himi-branded-lite-cms pattern): the descriptor
@@ -1941,7 +1941,6 @@ const SCREENS = runtimeSetting({
1941
1941
  "verticals": "[\"restaurants\",\"events\",\"blog\"]",
1942
1942
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"menuFirst.section0\",\"emptyWhen\":[\"hasDishHeroImage\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"menuFirst.section1\",\"emptyWhen\":[\"noMenuCategories\"]},{\"id\":\"section_2\",\"label\":\"Popular dishes\",\"dataSource\":\"menuFirst.section2\",\"emptyWhen\":[\"noPopularDishes\"]},{\"id\":\"section_3\",\"label\":\"Find us\",\"dataSource\":\"menuFirst.section3\"}]",
1943
1943
  "ownerDefaultLayout": "menuFirst",
1944
- "brandAccent": "#B8544A",
1945
1944
  "copy": "{\"home.eventsTitle\":\"Wine nights & events\",\"home.findUsTitle\":\"Find us\",\"home.hoursTitle\":\"Opening hours\",\"home.onboardingBody\":\"Browse the menu, reserve a table, and keep up with wine nights and kitchen news — all in one place.\",\"home.popularSubtitle\":\"The plates everyone keeps coming back for.\",\"home.popularTitle\":\"Popular dishes\",\"home.reserveCta\":\"Reserve a table\"}"
1946
1945
  }
1947
1946
  }
@@ -4294,10 +4293,7 @@ const SCREENS = runtimeSetting({
4294
4293
  "bottom": 28
4295
4294
  },
4296
4295
  "onAppear": {
4297
- "actionId": "tier5.himiBrandedAppearance.sync",
4298
- "params": {
4299
- "brandAccent": "#B8544A"
4300
- }
4296
+ "actionId": "tier5.himiBrandedAppearance.sync"
4301
4297
  }
4302
4298
  }
4303
4299
  },
@@ -134,12 +134,14 @@ export function isAccentId(x: unknown): x is AccentId {
134
134
  return typeof x === "string" && (ACCENT_IDS as readonly string[]).includes(x);
135
135
  }
136
136
 
137
- /** Resolve an accent id to a concrete hex, falling back to the app's brand color
138
- * for `brand` or any unknown value — so `backgroundColorPath` always binds to a
139
- * valid color. */
140
- export function accentColor(id: string, brandFallback: string): string {
141
- if (isAccentId(id) && ACCENT_META[id].hex) return ACCENT_META[id].hex;
142
- return brandFallback;
137
+ /** Resolve an accent id to its curated hex, or "" for `brand` and any unknown value.
138
+ * "" is deliberate: a bound `backgroundColorPath` that resolves empty keeps the surface's
139
+ * own `accentPrimary` token on every renderer, and that token IS the brand — after a
140
+ * `site analyze --apply` and after a hand edit of tokens.json alike. A brand colour copied
141
+ * into the descriptor instead (the old `brandAccent` param) went stale on the second path:
142
+ * the Home pill kept the template's blue after a rebrand. */
143
+ export function accentColor(id: string): string {
144
+ return isAccentId(id) ? ACCENT_META[id].hex : "";
143
145
  }
144
146
 
145
147
  /** Content-derived default: booking intent > deep catalog > editorial (blog-forward) > balanced.
@@ -19,11 +19,10 @@ import {
19
19
  interface State {
20
20
  /** The pending accent id (persisted on Apply, re-read by the home worker). */
21
21
  pendingAccent: string;
22
- /** The app's brand color (from the onAppear param) — the "brand" accent swatch
23
- * and the accent fallback resolve to it. */
24
- brandAccent: string;
25
- // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but
26
- // NOT per-row `$item.` fields, so each static swatch binds its own flat field.
22
+ // Per-accent swatch colors. backgroundColorPath resolves `$service:` paths but NOT
23
+ // per-row `$item.` fields, so each static swatch binds its own flat field.
24
+ // `accentHex_brand` is "" on purpose: the swatch keeps its own accentPrimary token,
25
+ // which is the brand (see accentColor in _shared/himi-layouts.ts).
27
26
  accentHex_brand: string;
28
27
  accentHex_amber: string;
29
28
  accentHex_terracotta: string;
@@ -54,7 +53,7 @@ function paramValue(arg: unknown, key: string): string {
54
53
  return typeof result === "string" ? result : "";
55
54
  }
56
55
 
57
- function build(pendingAccent: string, brandAccent: string): State {
56
+ function build(pendingAccent: string): State {
58
57
  const flags: Record<string, boolean> = {};
59
58
  for (const id of ACCENT_IDS) {
60
59
  flags[`selected_${id}`] = id === pendingAccent;
@@ -63,8 +62,7 @@ function build(pendingAccent: string, brandAccent: string): State {
63
62
  return {
64
63
  ...flags,
65
64
  pendingAccent,
66
- brandAccent,
67
- accentHex_brand: brandAccent || ACCENT_META.amber.hex,
65
+ accentHex_brand: "",
68
66
  accentHex_amber: ACCENT_META.amber.hex,
69
67
  accentHex_terracotta: ACCENT_META.terracotta.hex,
70
68
  accentHex_sage: ACCENT_META.sage.hex,
@@ -84,24 +82,21 @@ const svc = defineService<State, {
84
82
  apply: (state: State) => State;
85
83
  reset: (state: State) => State;
86
84
  }>("himiBrandedAppearance", {
87
- state: build(initialAccent(), ""),
85
+ state: build(initialAccent()),
88
86
  actions: {
89
- // onAppear: re-read the persisted accent + the app's brand color so the swatches
90
- // reflect reality (the brand swatch tints to the app's own color).
91
- sync: (state, arg) => {
92
- const brandAccent = paramValue(arg, "brandAccent") || state.brandAccent;
93
- return build(initialAccent(), brandAccent);
94
- },
87
+ // onAppear: re-read the persisted accent so the swatches reflect reality. An older
88
+ // descriptor may still pass a `brandAccent`; it is not used (the Brand swatch is the token).
89
+ sync: () => build(initialAccent()),
95
90
  // Tapping an accent swatch marks it pending (persisted on Apply).
96
91
  selectAccent: (state, arg) => {
97
92
  const id = paramValue(arg, "id");
98
93
  if (!isAccentId(id)) return state;
99
- return build(id, state.brandAccent);
94
+ return build(id);
100
95
  },
101
96
  // Apply: persist the accent; the home worker re-reads it on launch.
102
97
  apply: (state) => {
103
98
  try { storage()?.setItem(ACCENT_KEY, state.pendingAccent); } catch { /* best-effort */ }
104
- return build(state.pendingAccent, state.brandAccent);
99
+ return build(state.pendingAccent);
105
100
  },
106
101
  // Reset: clear the override, reverting to the brand accent.
107
102
  reset: (state) => {
@@ -110,7 +105,7 @@ const svc = defineService<State, {
110
105
  if (store?.removeItem) store.removeItem(ACCENT_KEY);
111
106
  else store?.setItem(ACCENT_KEY, "");
112
107
  } catch { /* best-effort */ }
113
- return build(DEFAULT_ACCENT, state.brandAccent);
108
+ return build(DEFAULT_ACCENT);
114
109
  },
115
110
  },
116
111
  });
@@ -338,11 +338,11 @@ function storedLayout(): string | null {
338
338
  function storedAccent(): string {
339
339
  try { return storage()?.getItem(ACCENT_KEY) ?? ""; } catch { return ""; }
340
340
  }
341
- // user accent (storage) → brand color (onAppear param) fallback. Always a valid
342
- // hex so backgroundColorPath binds cleanly; "" brand param at first paint just
343
- // falls the surface back to its accentPrimary token.
344
- function resolveAccent(arg: unknown): string {
345
- return resolveAccentColor(storedAccent(), paramValue(arg, "brandAccent"));
341
+ // The member's chosen accent (storage), else "" — the surface keeps its accentPrimary token,
342
+ // which is the brand. An older descriptor's `brandAccent` param is ignored: it was a copy of
343
+ // the brand that a hand edit of tokens.json left behind.
344
+ function resolveAccent(_arg?: unknown): string {
345
+ return resolveAccentColor(storedAccent());
346
346
  }
347
347
 
348
348
  // onAppear param reader (the himi-branded-lite-cms pattern): the descriptor
@@ -1419,8 +1419,7 @@ const SCREENS = runtimeSetting({
1419
1419
  "params": {
1420
1420
  "verticals": "[\"store\",\"blog\"]",
1421
1421
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Shop the collection\",\"dataSource\":\"storeFirst.section0\"},{\"id\":\"section_1\",\"label\":\"Explore\",\"dataSource\":\"storeFirst.section1\"},{\"id\":\"section_2\",\"label\":\"Shop\",\"dataSource\":\"storeFirst.section2\"}]",
1422
- "ownerDefaultLayout": "storeFirst",
1423
- "brandAccent": "#B4552D"
1422
+ "ownerDefaultLayout": "storeFirst"
1424
1423
  }
1425
1424
  }
1426
1425
  }
@@ -3772,10 +3771,7 @@ const SCREENS = runtimeSetting({
3772
3771
  "bottom": 28
3773
3772
  },
3774
3773
  "onAppear": {
3775
- "actionId": "tier5.himiBrandedAppearance.sync",
3776
- "params": {
3777
- "brandAccent": "#B4552D"
3778
- }
3774
+ "actionId": "tier5.himiBrandedAppearance.sync"
3779
3775
  }
3780
3776
  }
3781
3777
  },
@@ -134,12 +134,14 @@ export function isAccentId(x: unknown): x is AccentId {
134
134
  return typeof x === "string" && (ACCENT_IDS as readonly string[]).includes(x);
135
135
  }
136
136
 
137
- /** Resolve an accent id to a concrete hex, falling back to the app's brand color
138
- * for `brand` or any unknown value — so `backgroundColorPath` always binds to a
139
- * valid color. */
140
- export function accentColor(id: string, brandFallback: string): string {
141
- if (isAccentId(id) && ACCENT_META[id].hex) return ACCENT_META[id].hex;
142
- return brandFallback;
137
+ /** Resolve an accent id to its curated hex, or "" for `brand` and any unknown value.
138
+ * "" is deliberate: a bound `backgroundColorPath` that resolves empty keeps the surface's
139
+ * own `accentPrimary` token on every renderer, and that token IS the brand — after a
140
+ * `site analyze --apply` and after a hand edit of tokens.json alike. A brand colour copied
141
+ * into the descriptor instead (the old `brandAccent` param) went stale on the second path:
142
+ * the Home pill kept the template's blue after a rebrand. */
143
+ export function accentColor(id: string): string {
144
+ return isAccentId(id) ? ACCENT_META[id].hex : "";
143
145
  }
144
146
 
145
147
  /** Content-derived default: booking intent > deep catalog > editorial (blog-forward) > balanced.