@designcrowd/library.brand-page 6.1.14 → 6.1.16

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 (93) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CNqGW-GB.js → CreatedOnBrandCrowd-de-de-CmNhDMIf.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BWZeor_R.js → CreatedOnBrandCrowd-en-us-DgplxxWa.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-D_-5Qdpd.js → CreatedOnBrandCrowd-es-es-DFdhFOa1.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BC6Xo2uA.js → CreatedOnBrandCrowd-fr-ca-DjLCWBLB.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BC6Xo2uA.js → CreatedOnBrandCrowd-fr-fr-DjLCWBLB.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-D10DwNv3.js → CreatedOnBrandCrowd-pt-br-CI2XD0OS.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-D10DwNv3.js → CreatedOnBrandCrowd-pt-pt-CI2XD0OS.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-BDHrHOOB.js → CreatedOnDcom-de-de-CkLkBztx.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-EbniAwLE.js → CreatedOnDcom-en-us-frnxBJJv.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-BpAIMuUH.js → CreatedOnDcom-es-es-CrqgiERj.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BDSu4efU.js → CreatedOnDcom-fr-ca-CPoEcusj.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-BR7f1SWB.js → CreatedOnDcom-fr-fr-BqrRH3dV.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DzY1XBcr.js → CreatedOnDcom-pt-br-CpbVi3Sw.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-1v5N9vgW.js → CreatedOnDcom-pt-pt-s0XZ3c7y.js} +1 -1
  15. package/lib/src/brand-page/models/index.d.ts +4 -1
  16. package/lib/src/brand-page/models/index.js +4 -1
  17. package/lib/src/brand-page/utils/view-model.util.js +18 -2
  18. package/lib/src/brand-page/view-models.d.ts +2 -1
  19. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +4 -1
  20. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +3 -0
  21. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +15 -0
  22. package/lib/src/brand-page-maker/composables/useEventTracker.js +36 -0
  23. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +18 -0
  24. package/lib/src/{bundled-translations-BiFAlQdC.js → bundled-translations-B2q5GWTQ.js} +1 -1
  25. package/lib/src/{bundled-translations.de-DE-DuRWnOP-.js → bundled-translations.de-DE-FhH2H_rd.js} +5 -5
  26. package/lib/src/{bundled-translations.es-ES-CokHdZtW.js → bundled-translations.es-ES-_ZExxMi7.js} +5 -5
  27. package/lib/src/{bundled-translations.fr-CA-CJcMmubA.js → bundled-translations.fr-CA-Tg-ODIiC.js} +1 -1
  28. package/lib/src/{bundled-translations.fr-FR-DYPup8fJ.js → bundled-translations.fr-FR-C0sU51Mz.js} +1 -1
  29. package/lib/src/{bundled-translations.pt-BR-GSsp1kuV.js → bundled-translations.pt-BR-MYFIhA4u.js} +9 -9
  30. package/lib/src/{bundled-translations.pt-PT-DbkY8UQ3.js → bundled-translations.pt-PT-DeUBSzCB.js} +9 -9
  31. package/lib/src/bundles/bundled-translations.de-DE.json +1 -0
  32. package/lib/src/bundles/bundled-translations.es-ES.json +1 -0
  33. package/lib/src/bundles/bundled-translations.fr-CA.json +1 -0
  34. package/lib/src/bundles/bundled-translations.fr-FR.json +1 -0
  35. package/lib/src/bundles/bundled-translations.json +1 -0
  36. package/lib/src/bundles/bundled-translations.pt-BR.json +1 -0
  37. package/lib/src/bundles/bundled-translations.pt-PT.json +1 -0
  38. package/lib/src/css/library.brand-page-brandCrowd.min.css +1 -1
  39. package/lib/src/css/library.brand-page-brandPage.min.css +1 -1
  40. package/lib/src/css/library.brand-page-designCom.min.css +1 -1
  41. package/lib/src/css/library.brand-page-preview.min.css +1 -1
  42. package/lib/src/index.mjs +2 -2
  43. package/lib/src/{lib-BMP5cseh.js → lib-O_eJgJHP.js} +16498 -16437
  44. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +3 -0
  45. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +3 -0
  46. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +3 -0
  47. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +3 -0
  48. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +5 -0
  49. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +5 -0
  50. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +25 -0
  51. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +3 -0
  52. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +3 -0
  53. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +3 -0
  54. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +3 -0
  55. package/lib/src/tracker.d.ts +1 -1
  56. package/lib/src/tracker.js +1 -1
  57. package/lib/src/tracker.mjs +9 -7
  58. package/lib/src/{useEventTracker-Dmc2jFdN.js → useEventTracker-C5Ql5p7u.js} +56 -22
  59. package/lib/src/utils/minify-brand-page-metadata.d.ts +1 -0
  60. package/lib/src/utils/minify-brand-page-metadata.js +13 -3
  61. package/package.json +1 -1
  62. package/src/brand-page/models/index.ts +9 -2
  63. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +18 -6
  64. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +6 -2
  65. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +12 -4
  66. package/src/brand-page/utils/view-model.util.test.ts +145 -1
  67. package/src/brand-page/utils/view-model.util.ts +27 -2
  68. package/src/brand-page/view-models.ts +2 -0
  69. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +9 -1
  70. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.preview-gate.test.ts +38 -1
  71. package/src/brand-page-maker/components/preview-modal/PreviewModalWithState.test.ts +123 -0
  72. package/src/brand-page-maker/components/preview-modal/PreviewModalWithState.vue +25 -0
  73. package/src/brand-page-maker/composables/useEventTracker.ts +41 -0
  74. package/src/bundles/bundled-translations.de-DE.json +1 -0
  75. package/src/bundles/bundled-translations.es-ES.json +1 -0
  76. package/src/bundles/bundled-translations.fr-CA.json +1 -0
  77. package/src/bundles/bundled-translations.fr-FR.json +1 -0
  78. package/src/bundles/bundled-translations.json +1 -0
  79. package/src/bundles/bundled-translations.pt-BR.json +1 -0
  80. package/src/bundles/bundled-translations.pt-PT.json +1 -0
  81. package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.test.ts +111 -0
  82. package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.vue +18 -0
  83. package/src/shared/page-settings/components/page-settings/i18n/page-settings.de-DE.json +1 -0
  84. package/src/shared/page-settings/components/page-settings/i18n/page-settings.es-ES.json +1 -0
  85. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-CA.json +1 -0
  86. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-FR.json +1 -0
  87. package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +1 -0
  88. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-BR.json +1 -0
  89. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-PT.json +1 -0
  90. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +33 -0
  91. package/src/tracker.ts +2 -0
  92. package/src/utils/minify-brand-page-metadata.test.ts +70 -1
  93. package/src/utils/minify-brand-page-metadata.ts +19 -4
@@ -0,0 +1,111 @@
1
+ import { describe, test, expect, beforeEach } from 'vitest';
2
+ import { createPinia, setActivePinia } from 'pinia';
3
+ import { shallowMount } from '@vue/test-utils';
4
+ import HeaderSettingsPanel from './HeaderSettingsPanel.vue';
5
+ import { useUiPageSettingsStore } from '../../store';
6
+ import { PAGE_SECTIONS } from '../../models';
7
+ import { VIEWPORTS } from '@brand-page';
8
+
9
+ // Coverage for BP-5851: the header background opacity slider.
10
+ describe('HeaderSettingsPanel — background opacity (BP-5851)', () => {
11
+ let pageSettingsStore: ReturnType<typeof useUiPageSettingsStore>;
12
+
13
+ beforeEach(() => {
14
+ setActivePinia(createPinia());
15
+ pageSettingsStore = useUiPageSettingsStore();
16
+ });
17
+
18
+ const mountPanel = () =>
19
+ shallowMount(HeaderSettingsPanel, {
20
+ props: {
21
+ isAdminMaker: false,
22
+ isBrandSite: true,
23
+ logoTheme: {},
24
+ sectionName: PAGE_SECTIONS.header,
25
+ styleOptions: [],
26
+ },
27
+ });
28
+
29
+ const header = (viewport: 'mobile' | 'desktop' = VIEWPORTS.mobile) =>
30
+ pageSettingsStore.sections[PAGE_SECTIONS.header][viewport];
31
+
32
+ test('defaults to opacity 100 and hides the slider when no background color is set', () => {
33
+ const wrapper = mountPanel();
34
+
35
+ expect(header().backgroundStyle.backgroundColor).toBeFalsy();
36
+ expect((wrapper.vm as any).backgroundOpacity).toBe(100);
37
+ expect((wrapper.vm as any).showBackgroundOpacity).toBe(false);
38
+ });
39
+
40
+ test('shows the slider once a non-transparent background color is set', async () => {
41
+ const wrapper = mountPanel();
42
+
43
+ header().backgroundStyle = { backgroundColor: '#336699' };
44
+ await wrapper.vm.$nextTick();
45
+
46
+ expect((wrapper.vm as any).showBackgroundOpacity).toBe(true);
47
+ });
48
+
49
+ test('an un-constructed header (backgroundStyle carrying no backgroundOpacity) reads as 100', async () => {
50
+ const wrapper = mountPanel();
51
+
52
+ header().backgroundStyle = { backgroundColor: '#336699' };
53
+ await wrapper.vm.$nextTick();
54
+
55
+ expect(header().backgroundStyle.backgroundOpacity).toBeUndefined();
56
+ expect((wrapper.vm as any).backgroundOpacity).toBe(100);
57
+ });
58
+
59
+ test('hides the slider again when the color is switched to transparent', async () => {
60
+ const wrapper = mountPanel();
61
+
62
+ header().backgroundStyle = { backgroundColor: '#336699' };
63
+ await wrapper.vm.$nextTick();
64
+ expect((wrapper.vm as any).showBackgroundOpacity).toBe(true);
65
+
66
+ header().backgroundStyle = { backgroundColor: 'transparent' };
67
+ await wrapper.vm.$nextTick();
68
+
69
+ expect((wrapper.vm as any).showBackgroundOpacity).toBe(false);
70
+ });
71
+
72
+ test('onBackgroundOpacityChange merges the value into backgroundStyle of the active viewport section', () => {
73
+ const wrapper = mountPanel();
74
+
75
+ header().backgroundStyle = {
76
+ backgroundColor: '#336699',
77
+ backgroundOpacity: 100,
78
+ };
79
+
80
+ (wrapper.vm as any).onBackgroundOpacityChange(45);
81
+
82
+ expect(header().backgroundStyle.backgroundOpacity).toBe(45);
83
+ // A merge, not a replace — the sibling backgroundColor must survive.
84
+ expect(header().backgroundStyle.backgroundColor).toBe('#336699');
85
+ // The other viewport is untouched — opacity is per-viewport, like backgroundColor.
86
+ expect(header(VIEWPORTS.desktop).backgroundStyle.backgroundOpacity).toBe(
87
+ 100,
88
+ );
89
+ });
90
+
91
+ test('onBackgroundOpacityChange coerces a string value from the slider input', () => {
92
+ const wrapper = mountPanel();
93
+
94
+ (wrapper.vm as any).onBackgroundOpacityChange('60');
95
+
96
+ expect(header().backgroundStyle.backgroundOpacity).toBe(60);
97
+ });
98
+
99
+ test('onBackgroundOpacityChange clamps an out-of-range value (mid-typing in the Slider text field)', () => {
100
+ const wrapper = mountPanel();
101
+
102
+ (wrapper.vm as any).onBackgroundOpacityChange('-5');
103
+ expect(header().backgroundStyle.backgroundOpacity).toBe(0);
104
+
105
+ (wrapper.vm as any).onBackgroundOpacityChange(500);
106
+ expect(header().backgroundStyle.backgroundOpacity).toBe(100);
107
+
108
+ (wrapper.vm as any).onBackgroundOpacityChange('not-a-number');
109
+ expect(header().backgroundStyle.backgroundOpacity).toBe(0);
110
+ });
111
+ });
@@ -95,6 +95,22 @@
95
95
  :toggle-unique-id="sectionName"
96
96
  @update:model-value="onSelectColor"
97
97
  />
98
+ <div
99
+ v-if="showBackgroundOpacity"
100
+ data-test-header-background-opacity
101
+ class="tw-mt-4"
102
+ >
103
+ <span class="header-settings-panel_option-title">
104
+ {{ pageSettingsTr('backgroundOpacity') }}
105
+ </span>
106
+ <Slider
107
+ :model-value="backgroundOpacity"
108
+ :min="0"
109
+ :max="100"
110
+ suffix="%"
111
+ @update:model-value="onBackgroundOpacityChange"
112
+ />
113
+ </div>
98
114
  </CollapsiblePanel>
99
115
 
100
116
  <CollapsiblePanel
@@ -144,6 +160,7 @@
144
160
  import {
145
161
  CollapsiblePanel,
146
162
  HelloBar,
163
+ Slider,
147
164
  TabMenu,
148
165
  Toggle,
149
166
  } from '@designcrowd/fe-shared-lib';
@@ -163,6 +180,7 @@ export default {
163
180
  CollapsiblePanel,
164
181
  ColorField,
165
182
  HelloBar,
183
+ Slider,
166
184
  TabMenu,
167
185
  Toggle,
168
186
  HeaderDisplayStyle,
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Kopfzeilen-Einstellungen",
4
4
  "navigationSectionTitle": "Navigation",
5
5
  "headerSectionTitle": "Header",
6
+ "backgroundOpacity": "Hintergrunddeckkraft",
6
7
  "buttonSectionTitle": "Schaltfläche",
7
8
  "footerSettings": "Fußzeilen-Einstellungen",
8
9
  "managePages": "Seiten verwalten",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Configuración del encabezado",
4
4
  "navigationSectionTitle": "Navegación",
5
5
  "headerSectionTitle": "Encabezado",
6
+ "backgroundOpacity": "Opacidad del fondo",
6
7
  "buttonSectionTitle": "Botón",
7
8
  "footerSettings": "Configuración del pie de página",
8
9
  "managePages": "Gestionar páginas",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Paramètres d'en-tête",
4
4
  "navigationSectionTitle": "Navigation",
5
5
  "headerSectionTitle": "En-tête",
6
+ "backgroundOpacity": "Opacité de l'arrière-plan",
6
7
  "buttonSectionTitle": "Bouton",
7
8
  "footerSettings": "Paramètres de pied de page",
8
9
  "managePages": "Gérer les pages",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Paramètres d'en-tête",
4
4
  "navigationSectionTitle": "Navigation",
5
5
  "headerSectionTitle": "En-tête",
6
+ "backgroundOpacity": "Opacité de l'arrière-plan",
6
7
  "buttonSectionTitle": "Bouton",
7
8
  "footerSettings": "Paramètres de pied de page",
8
9
  "managePages": "Gérer les pages",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Header Settings",
4
4
  "navigationSectionTitle": "Navigation",
5
5
  "headerSectionTitle": "Header",
6
+ "backgroundOpacity": "Background opacity",
6
7
  "buttonSectionTitle": "Button",
7
8
  "footerSettings": "Footer Settings",
8
9
  "managePages": "Manage Pages",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Configurações do cabeçalho",
4
4
  "navigationSectionTitle": "Navegação",
5
5
  "headerSectionTitle": "Cabeçalho",
6
+ "backgroundOpacity": "Opacidade do plano de fundo",
6
7
  "buttonSectionTitle": "Botão",
7
8
  "footerSettings": "Definições de rodapé",
8
9
  "managePages": "Gerenciar páginas",
@@ -3,6 +3,7 @@
3
3
  "headerSettings": "Definições de cabeçalho",
4
4
  "navigationSectionTitle": "Navegação",
5
5
  "headerSectionTitle": "Cabeçalho",
6
+ "backgroundOpacity": "Opacidade do fundo",
6
7
  "buttonSectionTitle": "Botão",
7
8
  "footerSettings": "Definições de rodapé",
8
9
  "managePages": "Gerir páginas",
@@ -24,6 +24,7 @@ import type {
24
24
  PageFooterMobile,
25
25
  LogoImageModel,
26
26
  LogoViewModel,
27
+ BackgroundColorStyle,
27
28
  } from '@brand-page';
28
29
  import {
29
30
  LOGO_SLIDER_DESKTOP_MAX,
@@ -39,6 +40,7 @@ import type {
39
40
  UpdateItemValuePayload,
40
41
  } from '../../../content-settings/models';
41
42
  import { UI_DEVICE_MODES, UI_EVENT_SOURCES } from '../../../models';
43
+ import { isTransparentColour } from '@brand-page/utils/color.util';
42
44
 
43
45
  import type { PageSettingsStateSectionStyles } from '../../store';
44
46
  import { useUiPageSettingsStore } from '../../store';
@@ -216,6 +218,19 @@ export default defineComponent({
216
218
  return this.sections[this.sectionName][this.viewport]
217
219
  .backgroundColorEnabled;
218
220
  },
221
+ backgroundOpacity(): number {
222
+ // Real default: an un-constructed (JSON.parse'd) header never saw the constructor's default.
223
+ return (
224
+ this.sections[this.sectionName][this.viewport].backgroundStyle
225
+ ?.backgroundOpacity ?? 100
226
+ );
227
+ },
228
+ showBackgroundOpacity(): boolean {
229
+ // isTransparentColour('') is false, so an unset color needs its own check.
230
+ return (
231
+ !!this.backgroundColor && !isTransparentColour(this.backgroundColor)
232
+ );
233
+ },
219
234
  copyrightPlaceholder(): string {
220
235
  return commonTr(`copyrightTextPlaceholder`);
221
236
  },
@@ -544,6 +559,24 @@ export default defineComponent({
544
559
  });
545
560
  },
546
561
 
562
+ onBackgroundOpacityChange(value: number | string) {
563
+ // Clamped here: the Slider's text field can emit an out-of-range value mid-typing.
564
+ const clamped = Math.min(100, Math.max(0, Number(value) || 0));
565
+
566
+ // A variable, not an inline literal — backgroundOpacity isn't on BrandPageCssStyle.
567
+ const backgroundStyle: BackgroundColorStyle = {
568
+ backgroundOpacity: clamped,
569
+ };
570
+
571
+ this.pageSettingsStore.setPageSettingsSectionValue({
572
+ sectionName: this.sectionName,
573
+ propName: 'backgroundStyle',
574
+ value: backgroundStyle,
575
+ merge: true,
576
+ viewport: this.viewport,
577
+ });
578
+ },
579
+
547
580
  onBackgroundEnable(enabled: UpdateItemValuePayload<boolean>) {
548
581
  this.pageSettingsStore.setPageSettingsSectionValue({
549
582
  sectionName: this.sectionName,
package/src/tracker.ts CHANGED
@@ -19,6 +19,8 @@ export {
19
19
  PUBLISH_MODAL_CONTINUATION_EVENTS,
20
20
  GENERATE_TEXT_CONTINUATION_EVENTS,
21
21
  PUBLISH_OPTION_MODAL_CONTINUATION_EVENTS,
22
+ PREVIEW_CTA_LOCATIONS,
23
+ PREVIEW_MODAL_CONTINUATION_EVENTS,
22
24
  } from './brand-page-maker/composables/useEventTracker';
23
25
  export type {
24
26
  EventTracker,
@@ -1,7 +1,76 @@
1
1
  import { describe, test, expect } from 'vitest';
2
- import type { BrandPageModel } from '../brand-page/models';
2
+ import { BRAND_SITE } from '@brand-page-maker/components/brand-page-maker/__fixtures__';
3
+ import { BrandPageModel, PageHeaderDesktop } from '@brand-page';
3
4
  import { minifyBrandPageMetadata } from './minify-brand-page-metadata';
4
5
 
6
+ // Coverage for BP-5851: the AI website builder reads the minified metadata to
7
+ // understand the current site, so backgroundOpacity must be visible there too
8
+ // (read-only — see docs/plans for BP-5851) whenever the header isn't fully opaque.
9
+ describe('minifyBrandPageMetadata — header backgroundOpacity (BP-5851)', () => {
10
+ const buildModel = (pageHeaderDesktop: PageHeaderDesktop) =>
11
+ new BrandPageModel({
12
+ ...BRAND_SITE,
13
+ pages: [],
14
+ pageHeaderDesktop,
15
+ });
16
+
17
+ test('opacity below 100 is included in the minified header backgroundStyle', () => {
18
+ const model = buildModel(
19
+ new PageHeaderDesktop({
20
+ backgroundStyle: { backgroundColor: '#112233', backgroundOpacity: 65 },
21
+ }),
22
+ );
23
+
24
+ const minified = minifyBrandPageMetadata(model);
25
+
26
+ expect(minified.pageHeaderDesktop?.backgroundStyle).toEqual({
27
+ backgroundColor: '#112233',
28
+ backgroundOpacity: 65,
29
+ });
30
+ });
31
+
32
+ test('opacity 100 (default/fully opaque) is omitted to keep the payload small', () => {
33
+ const model = buildModel(
34
+ new PageHeaderDesktop({
35
+ backgroundStyle: { backgroundColor: '#112233', backgroundOpacity: 100 },
36
+ }),
37
+ );
38
+
39
+ const minified = minifyBrandPageMetadata(model);
40
+
41
+ expect(minified.pageHeaderDesktop?.backgroundStyle).toEqual({
42
+ backgroundColor: '#112233',
43
+ });
44
+ });
45
+
46
+ test('un-constructed legacy header with backgroundOpacity absent omits it (defaults to opaque)', () => {
47
+ // Forced after buildModel, since BrandPageModel's constructor would re-inject the default.
48
+ const model = buildModel(
49
+ new PageHeaderDesktop({
50
+ backgroundStyle: { backgroundColor: '#112233' },
51
+ }),
52
+ );
53
+
54
+ delete model.pageHeaderDesktop.backgroundStyle.backgroundOpacity;
55
+
56
+ const minified = minifyBrandPageMetadata(model);
57
+
58
+ expect(minified.pageHeaderDesktop?.backgroundStyle).toEqual({
59
+ backgroundColor: '#112233',
60
+ });
61
+ });
62
+
63
+ test('header with no backgroundColor has no minified backgroundStyle at all', () => {
64
+ const model = buildModel(
65
+ new PageHeaderDesktop({ backgroundStyle: { backgroundOpacity: 40 } }),
66
+ );
67
+
68
+ const minified = minifyBrandPageMetadata(model);
69
+
70
+ expect(minified.pageHeaderDesktop?.backgroundStyle).toBeUndefined();
71
+ });
72
+ });
73
+
5
74
  /**
6
75
  * BP-5886 — the outbound half of the AI colour bridge (review blocker B8).
7
76
  *
@@ -43,7 +43,7 @@ type MinifiedHeader = {
43
43
  logoImageHeight?: string;
44
44
  logoImageWidth?: string;
45
45
  logoAssetKey?: string;
46
- backgroundStyle?: { backgroundColor?: string };
46
+ backgroundStyle?: { backgroundColor?: string; backgroundOpacity?: number };
47
47
  navigation?: {
48
48
  displayType?: string;
49
49
  defaultButtonStyle?: { color?: string; fontFamily?: string };
@@ -552,6 +552,23 @@ const minifyFooter = (
552
552
  };
553
553
  };
554
554
 
555
+ // Fully opaque (100) is the default, so it's omitted to keep the AI payload small.
556
+ const minifyHeaderBackgroundStyle = (
557
+ backgroundStyle: AbstractPageHeaderStyles['backgroundStyle'] | undefined,
558
+ ): MinifiedHeader['backgroundStyle'] => {
559
+ const backgroundColor = backgroundStyle?.backgroundColor;
560
+
561
+ if (!backgroundColor) return undefined;
562
+
563
+ const backgroundOpacity = backgroundStyle.backgroundOpacity;
564
+
565
+ return {
566
+ backgroundColor,
567
+ ...(backgroundOpacity !== undefined &&
568
+ backgroundOpacity < 100 && { backgroundOpacity }),
569
+ };
570
+ };
571
+
555
572
  const minifyHeader = (
556
573
  header: AbstractPageHeaderStyles | undefined | null,
557
574
  logoAssetKey?: string,
@@ -566,9 +583,7 @@ const minifyHeader = (
566
583
  logoImageHeight: header.logoImageHeight || undefined,
567
584
  logoImageWidth: header.logoImageWidth || undefined,
568
585
  ...(logoAssetKey && { logoAssetKey }),
569
- backgroundStyle: header.backgroundStyle?.backgroundColor
570
- ? { backgroundColor: header.backgroundStyle.backgroundColor as string }
571
- : undefined,
586
+ backgroundStyle: minifyHeaderBackgroundStyle(header.backgroundStyle),
572
587
  navigation: nav
573
588
  ? {
574
589
  displayType: nav.displayType || undefined,