@wix/himalaya-cli 0.1065.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 +173 -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
@@ -1958,7 +1958,6 @@ const SCREENS = runtimeSetting({
1958
1958
  "verticals": "[\"feed\",\"events\",\"blog\",\"groups\",\"cms\"]",
1959
1959
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"communityPulse.section0\",\"emptyWhen\":[\"noMemberAvatars\"]},{\"id\":\"section_1\",\"label\":\"Announcement\",\"dataSource\":\"communityPulse.section1\",\"emptyWhen\":[\"noAnnouncement\",\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Groups\",\"dataSource\":\"communityPulse.section2\",\"emptyWhen\":[\"noGroupCards\"]},{\"id\":\"section_3\",\"label\":\"Latest activity\",\"dataSource\":\"communityPulse.section3\"}]",
1960
1960
  "ownerDefaultLayout": "communityPulse",
1961
- "brandAccent": "#6C3FC4",
1962
1961
  "copy": "{\"home.activityTitle\":\"Latest activity\",\"home.announcementCaption\":\"Announcement\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your people.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Our members\",\"home.onboardingBody\":\"Join groups, RSVP to events, and keep up with the community feed — all in one place.\"}"
1963
1962
  }
1964
1963
  }
@@ -4223,10 +4222,7 @@ const SCREENS = runtimeSetting({
4223
4222
  "bottom": 28
4224
4223
  },
4225
4224
  "onAppear": {
4226
- "actionId": "tier5.himiBrandedAppearance.sync",
4227
- "params": {
4228
- "brandAccent": "#6C3FC4"
4229
- }
4225
+ "actionId": "tier5.himiBrandedAppearance.sync"
4230
4226
  }
4231
4227
  }
4232
4228
  },
@@ -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
@@ -2142,7 +2142,6 @@ const SCREENS = runtimeSetting({
2142
2142
  "verticals": "[\"feed\",\"events\",\"blog\",\"groups\"]",
2143
2143
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Next up\",\"dataSource\":\"eventsFirst.section0\",\"emptyWhen\":[\"noNextEvent\"]},{\"id\":\"section_1\",\"label\":\"This season\",\"dataSource\":\"eventsFirst.section1\",\"emptyWhen\":[\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Discover 3\",\"dataSource\":\"eventsFirst.section2\"},{\"id\":\"section_3\",\"label\":\"Latest from the community\",\"dataSource\":\"eventsFirst.section3\"}]",
2144
2144
  "ownerDefaultLayout": "eventsFirst",
2145
- "brandAccent": "#5B3E96",
2146
2145
  "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.agendaTitle\":\"This season\",\"home.allEventsCta\":\"All events\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your crew.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Who's coming\",\"home.nextUpCaption\":\"Next up\",\"home.onboardingBody\":\"See what's next, RSVP in a couple of taps, and keep up with the community between events — all in one place.\",\"home.recapsCaption\":\"Event recaps\",\"home.rsvpCta\":\"View event & RSVP\"}"
2147
2146
  }
2148
2147
  }
@@ -4407,10 +4406,7 @@ const SCREENS = runtimeSetting({
4407
4406
  "bottom": 28
4408
4407
  },
4409
4408
  "onAppear": {
4410
- "actionId": "tier5.himiBrandedAppearance.sync",
4411
- "params": {
4412
- "brandAccent": "#5B3E96"
4413
- }
4409
+ "actionId": "tier5.himiBrandedAppearance.sync"
4414
4410
  }
4415
4411
  }
4416
4412
  },
@@ -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
@@ -2423,7 +2423,6 @@ const SCREENS = runtimeSetting({
2423
2423
  "verticals": "[\"store\",\"bookings\",\"plans\",\"events\",\"blog\"]",
2424
2424
  "homeModules": "[{\"id\":\"member_progress\",\"label\":\"This week's schedule\",\"dataSource\":\"member.bookings.schedule\",\"emptyWhen\":[\"hideMemberProgress\"]},{\"id\":\"up_next\",\"label\":\"Up next\",\"dataSource\":\"member.bookings\",\"emptyWhen\":[\"hideNextBooking\",\"hidePersonalError\"]},{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"trainingFirst.section0\",\"emptyWhen\":[\"hideHeroImage\"]},{\"id\":\"section_1\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"trainingFirst.section1\"}]",
2425
2425
  "ownerDefaultLayout": "trainingFirst",
2426
- "brandAccent": "#C1552F",
2427
2426
  "copy": "{\"home.storeFeaturedTitle\":\"Shop the gear\"}"
2428
2427
  }
2429
2428
  }
@@ -5129,10 +5128,7 @@ const SCREENS = runtimeSetting({
5129
5128
  "bottom": 28
5130
5129
  },
5131
5130
  "onAppear": {
5132
- "actionId": "tier5.himiBrandedAppearance.sync",
5133
- "params": {
5134
- "brandAccent": "#C1552F"
5135
- }
5131
+ "actionId": "tier5.himiBrandedAppearance.sync"
5136
5132
  }
5137
5133
  }
5138
5134
  },
@@ -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
@@ -2078,7 +2078,6 @@ const SCREENS = runtimeSetting({
2078
2078
  "verticals": "[\"plans\",\"feed\",\"events\",\"blog\",\"groups\"]",
2079
2079
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"communityPulse.section0\",\"emptyWhen\":[\"noMemberAvatars\"]},{\"id\":\"section_1\",\"label\":\"From the field\",\"dataSource\":\"communityPulse.section1\",\"emptyWhen\":[\"noAnnouncement\",\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Chapters & teams\",\"dataSource\":\"communityPulse.section2\",\"emptyWhen\":[\"noGroupCards\"]},{\"id\":\"section_3\",\"label\":\"Latest from the community\",\"dataSource\":\"communityPulse.section3\"}]",
2080
2080
  "ownerDefaultLayout": "communityPulse",
2081
- "brandAccent": "#0F6B5C",
2082
2081
  "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.announcementCaption\":\"From the field\",\"home.eventsTitle\":\"Volunteer opportunities\",\"home.groupsSubtitle\":\"Find your cause and pitch in.\",\"home.groupsTitle\":\"Chapters & teams\",\"home.joinConversationCta\":\"Share your story\",\"home.membersCaption\":\"Our volunteers\",\"home.onboardingBody\":\"Find a way to give, sign up to volunteer, and follow the mission's impact — all in one place.\"}"
2083
2082
  }
2084
2083
  }
@@ -4520,10 +4519,7 @@ const SCREENS = runtimeSetting({
4520
4519
  "bottom": 28
4521
4520
  },
4522
4521
  "onAppear": {
4523
- "actionId": "tier5.himiBrandedAppearance.sync",
4524
- "params": {
4525
- "brandAccent": "#0F6B5C"
4526
- }
4522
+ "actionId": "tier5.himiBrandedAppearance.sync"
4527
4523
  }
4528
4524
  }
4529
4525
  },
@@ -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.