@salesforce/ui-theme-manager 11.63.0 → 11.64.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 (45) hide show
  1. package/dist/authoring/core/ThemeManagerPanelRoot.d.cts +7 -0
  2. package/dist/authoring/core/ThemeManagerPanelRoot.d.ts +7 -0
  3. package/dist/authoring/core/ThemeManagerPanelRoot.d.ts.map +1 -1
  4. package/dist/authoring/core/components/BordersSection.d.ts.map +1 -1
  5. package/dist/authoring/core/components/ButtonSection.d.cts +7 -0
  6. package/dist/authoring/core/components/ButtonSection.d.ts +7 -0
  7. package/dist/authoring/core/components/ButtonSection.d.ts.map +1 -1
  8. package/dist/authoring/core/components/ScaleValueRow.d.cts +43 -0
  9. package/dist/authoring/core/components/ScaleValueRow.d.ts +43 -0
  10. package/dist/authoring/core/components/ScaleValueRow.d.ts.map +1 -0
  11. package/dist/authoring/core/components/SectionTabs.d.cts +23 -0
  12. package/dist/authoring/core/components/SectionTabs.d.ts +23 -0
  13. package/dist/authoring/core/components/SectionTabs.d.ts.map +1 -0
  14. package/dist/authoring/core/components/TypographySection.d.ts.map +1 -1
  15. package/dist/authoring/core/labels.d.cts +22 -5
  16. package/dist/authoring/core/labels.d.ts +22 -5
  17. package/dist/authoring/core/labels.d.ts.map +1 -1
  18. package/dist/authoring/react/index.js +1 -1
  19. package/dist/authoring/react/styles.css +1 -1
  20. package/dist/authoring/web-component/index.d.cts +8 -0
  21. package/dist/authoring/web-component/index.d.ts +8 -0
  22. package/dist/authoring/web-component/index.d.ts.map +1 -1
  23. package/dist/authoring/web-component/index.js +180 -19
  24. package/dist/protocol/brandCss.cjs +38 -1
  25. package/dist/protocol/brandCss.d.cts +19 -0
  26. package/dist/protocol/brandCss.d.ts +19 -0
  27. package/dist/protocol/brandCss.d.ts.map +1 -1
  28. package/dist/protocol/brandCss.js +39 -2
  29. package/dist/protocol/brandSearch.cjs +24 -10
  30. package/dist/protocol/brandSearch.d.cts +24 -0
  31. package/dist/protocol/brandSearch.d.ts +24 -0
  32. package/dist/protocol/brandSearch.d.ts.map +1 -1
  33. package/dist/protocol/brandSearch.js +24 -10
  34. package/dist/protocol/index.cjs +6 -0
  35. package/dist/protocol/index.js +8 -2
  36. package/dist/runtime/previewContent.cjs +496 -217
  37. package/dist/runtime/previewContent.d.cts +31 -12
  38. package/dist/runtime/previewContent.d.ts +31 -12
  39. package/dist/runtime/previewContent.d.ts.map +1 -1
  40. package/dist/runtime/previewContent.js +498 -219
  41. package/dist/runtime/theme-preview-runtime.js +2526 -0
  42. package/package.json +1 -1
  43. package/dist/authoring/core/components/AssetsSection.d.cts +0 -12
  44. package/dist/authoring/core/components/AssetsSection.d.ts +0 -12
  45. package/dist/authoring/core/components/AssetsSection.d.ts.map +0 -1
@@ -5,32 +5,28 @@ const brandCss = require("../protocol/brandCss.cjs");
5
5
  const brandResolver = require("../protocol/brandResolver.cjs");
6
6
  const brandTypes = require("../protocol/brandTypes.cjs");
7
7
  const themeCss = require("../protocol/themeCss.cjs");
8
- function escapeHtmlAttr(value) {
8
+ function escapeHtml(value) {
9
9
  return String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10
10
  }
11
11
  function getPreviewHtml(theme, options) {
12
12
  const variables = themeCss.themeToVariables(theme);
13
- return generatePreviewHtml(variables, options);
13
+ return generatePreviewHtml(variables, options, void 0, theme.metadata?.label);
14
14
  }
15
15
  function getBrandPreviewHtml(brand, options) {
16
16
  const resolved = brandResolver.resolveBrandTokens(brand);
17
17
  const variables = brandCss.brandToVariables(resolved);
18
- return generatePreviewHtml(variables, options, resolved);
18
+ return generatePreviewHtml(variables, options, resolved, brandTypes.getBrandTitle(resolved));
19
19
  }
20
- function generatePreviewHtml(variables, options, brand) {
21
- const cssVariables = Object.entries(variables).map(([key, value]) => ` ${key}: ${value};`).join("\n");
20
+ function generatePreviewHtml(variables, options, brand, title) {
21
+ const allVariables = { ...variables, ...brandCss.oklchDisplayVars(variables) };
22
+ const cssVariables = Object.entries(allVariables).map(([key, value]) => ` ${key}: ${value};`).join("\n");
22
23
  const fontFaceRules = brand ? fontFace.generateFontFaceRules(brand) : "";
23
24
  const scriptSrc = options?.runtimeScriptUri || "/_sfdc/theme-preview-runtime.js";
24
25
  const nonce = options?.nonce || "";
25
26
  const cspSource = options?.cspSource || "";
27
+ const scriptTag = options?.omitRuntimeScript ? "" : `<script${nonce ? ` nonce="${nonce}"` : ""} src="${scriptSrc}"><\/script>`;
26
28
  const cspMeta = cspSource ? ` <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}' ${cspSource}; img-src https: data: ${cspSource}; font-src https: data:;">` : "";
27
- const heroImage = options?.imageUris?.hero;
28
- const section3Image = options?.imageUris?.section3Left;
29
- const section4Image = options?.imageUris?.section4Right;
30
- const logoUrl = brand ? brandTypes.getBrandLogoUrl(brand) : void 0;
31
- const logoAlt = brand ? brandTypes.getBrandLogoAltText(brand) : "";
32
- const heroImg = heroImage ? `<img data-brand-hero src="${heroImage}" alt=""${logoUrl ? " hidden" : ""} />` : "";
33
- const logoImg = logoUrl ? `<img class="brand-logo" data-brand-logo src="${escapeHtmlAttr(logoUrl)}" alt="${escapeHtmlAttr(logoAlt)}" />` : "";
29
+ const brandName = escapeHtml(title && title.trim().length > 0 ? title : "Your Brand");
34
30
  return `<!DOCTYPE html>
35
31
  <html lang="en">
36
32
  <head>
@@ -52,293 +48,576 @@ ${cssVariables}
52
48
  body {
53
49
  font-family: var(--tm-font-body);
54
50
  font-size: var(--tm-font-size-base);
51
+ line-height: var(--tm-line-height, 1.5);
55
52
  color: var(--tm-color-text);
56
- background-color: var(--tm-color-background);
57
- line-height: var(--tm-line-height);
53
+ /* Neutral page behind the card; the card itself carries the brand surface. */
54
+ background: #0f1115;
58
55
  -webkit-font-smoothing: antialiased;
59
56
  -moz-osx-font-smoothing: grayscale;
60
- margin: 0;
61
- padding: 0;
57
+ padding: 1rem;
58
+ display: flex;
59
+ justify-content: center;
62
60
  }
63
61
 
64
- /* ===== BRAND PREVIEW SECTIONS ===== */
65
- .brand-section {
66
- padding: 1.5rem;
67
- background-color: var(--tm-color-background);
68
- color: var(--tm-color-text);
69
- border-radius: var(--tm-border-radius-square, 0);
70
- border-width: var(--tm-border-weight-none, 0);
71
- margin: 0;
72
- transition: all 0.25s ease;
62
+ /* ===== CARD SHELL ===== */
63
+ .snapshot-card {
64
+ width: 100%;
65
+ max-width: 340px;
66
+ background: #16181d;
67
+ border-radius: 18px;
68
+ padding: 8px;
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 8px;
72
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
73
73
  }
74
74
 
75
- .brand-section img {
76
- max-width: 100%;
77
- height: auto;
78
- display: block;
79
- border-radius: var(--tm-border-radius-square, 0);
75
+ /* ===== HEADER: "Live snapshot" + "See full preview" ===== */
76
+ .snapshot-header {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: space-between;
80
+ padding: 8px 10px;
81
+ color: #b7bcc7;
82
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
83
+ font-size: 0.78rem;
80
84
  }
81
85
 
82
- /* Brand logo replaces the top hero image: half the container width, capped
83
- at 200px tall, centered. object-fit keeps aspect ratio when the height cap
84
- applies to a tall logo. */
85
- .brand-logo {
86
- width: 50%;
87
- max-width: 50%;
88
- height: auto;
89
- max-height: 200px;
90
- object-fit: contain;
91
- margin: 0 auto;
92
- display: block;
86
+ .snapshot-header .label {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ gap: 6px;
93
90
  }
94
91
 
95
- [hidden] {
96
- display: none !important;
92
+ .snapshot-header .eye {
93
+ opacity: 0.7;
97
94
  }
98
95
 
99
- .brand-two-column {
100
- display: grid;
101
- grid-template-columns: 1fr 1fr;
102
- gap: 0.75rem;
96
+ .snapshot-header .full-preview {
97
+ color: #d7dae1;
98
+ opacity: 0.85;
99
+ white-space: nowrap;
100
+ cursor: pointer;
101
+ background: none;
102
+ border: none;
103
+ font: inherit;
104
+ padding: 0;
103
105
  }
104
106
 
105
- @media (max-width: 768px) {
106
- .brand-two-column {
107
- grid-template-columns: 1fr;
108
- }
107
+ .snapshot-header .full-preview:hover {
108
+ opacity: 1;
109
+ text-decoration: underline;
109
110
  }
110
111
 
111
- /* ===== TYPOGRAPHY ===== */
112
- .brand-h1 {
113
- font-family: var(--tm-heading1-font, var(--tm-font-heading));
114
- font-size: var(--tm-heading1-size, 2rem);
115
- font-weight: var(--tm-heading1-weight, var(--tm-heading-weight));
116
- line-height: var(--tm-heading1-line-height, 1.2);
112
+ /* ===== BRAND SURFACE (the white inner area) ===== */
113
+ .brand-surface {
114
+ background: var(--tm-color-background);
117
115
  color: var(--tm-color-text);
118
- margin: 0 0 0.75rem 0;
119
- text-align: left;
120
- transition: all 0.25s ease;
116
+ border-radius: 12px;
117
+ padding: 18px 16px 20px;
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 16px;
121
+ transition: background-color 0.25s ease, color 0.25s ease;
121
122
  }
122
123
 
123
- .brand-h2 {
124
- font-family: var(--tm-heading2-font, var(--tm-font-heading));
125
- font-size: var(--tm-heading2-size, 1.75rem);
126
- font-weight: var(--tm-heading2-weight, var(--tm-heading-weight));
127
- line-height: var(--tm-heading2-line-height, 1.2);
124
+ /* Brand title with accent dot */
125
+ .brand-title {
126
+ display: flex;
127
+ align-items: center;
128
+ gap: 9px;
129
+ /* Drive the specimen off --tm-font-body: it's the variable the panel's
130
+ single "Base font family" control actually edits. --tm-font-heading is
131
+ derived from heading1.fontFamily, which many brands hardcode (not a ref
132
+ to baseFontFamily), so keying headings off it makes the font dropdown
133
+ look broken. When a dedicated heading-font control exists, restore
134
+ var(--tm-font-heading, var(--tm-font-body)) here and below. */
135
+ font-family: var(--tm-font-body);
136
+ font-weight: var(--tm-heading-weight, 700);
137
+ font-size: 1.15rem;
128
138
  color: var(--tm-color-text);
129
- margin: 0.75rem 0 0 0;
130
- text-align: left;
139
+ }
140
+
141
+ .brand-title .dot {
142
+ width: 10px;
143
+ height: 10px;
144
+ border-radius: 50%;
145
+ background: var(--tm-color-accent, var(--tm-color-primary));
146
+ flex: 0 0 auto;
147
+ transition: background-color 0.25s ease;
148
+ }
149
+
150
+ /* Action row: primary pill + accent chip */
151
+ .action-row {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 12px;
155
+ }
156
+
157
+ .btn-primary {
158
+ padding: var(--tm-button-padding-y, 0.6rem) var(--tm-button-padding-x, 1.1rem);
159
+ background: var(--tm-button-primary-bg, var(--tm-color-primary));
160
+ color: var(--tm-button-primary-text, #ffffff);
161
+ border: var(--tm-button-primary-border-width, 0) solid var(--tm-button-primary-border, transparent);
162
+ border-radius: var(--tm-button-radius, var(--tm-border-radius, 999px));
163
+ font-family: var(--tm-font-body);
164
+ font-weight: var(--tm-body-weight, 600);
165
+ font-size: 0.9rem;
166
+ cursor: pointer;
131
167
  transition: all 0.25s ease;
132
168
  }
133
169
 
134
- .brand-h3 {
135
- font-family: var(--tm-heading3-font, var(--tm-font-heading));
136
- font-size: var(--tm-heading3-size, 1.5rem);
137
- font-weight: var(--tm-heading3-weight, var(--tm-heading-weight));
138
- line-height: var(--tm-heading3-line-height, 1.2);
139
- color: var(--tm-color-text);
140
- margin: 0 0 0.75rem 0;
141
- text-align: left;
170
+ .chip-accent {
171
+ padding: 0.4rem 0.75rem;
172
+ background: var(--tm-color-accent, var(--tm-color-primary));
173
+ color: var(--tm-color-background);
174
+ border-radius: 999px;
175
+ font-family: var(--tm-font-body);
176
+ font-weight: 700;
177
+ font-size: 0.72rem;
178
+ letter-spacing: 0.06em;
179
+ text-transform: uppercase;
142
180
  transition: all 0.25s ease;
143
181
  }
144
182
 
145
- .brand-h4 {
146
- font-family: var(--tm-heading4-font, var(--tm-font-heading));
147
- font-size: var(--tm-heading4-size, 1.25rem);
148
- font-weight: var(--tm-heading4-weight, var(--tm-heading-weight));
149
- line-height: var(--tm-heading4-line-height, 1.2);
183
+ /* Typography specimen card */
184
+ .type-specimen {
185
+ /* Plain fallback first; color-mix wins where supported (Chrome 111+). */
186
+ border: 1px solid rgba(0, 0, 0, 0.12);
187
+ border: 1px solid color-mix(in srgb, var(--tm-color-text) 12%, transparent);
188
+ border-radius: var(--tm-border-radius-round, 12px);
189
+ padding: 16px;
190
+ display: flex;
191
+ flex-direction: column;
192
+ gap: 6px;
193
+ }
194
+
195
+ /* The specimen font-size reads the SAME scale vars the panel's size sliders
196
+ edit (--tm-heading*-size / --tm-paragraph*-size), so dragging a slider is
197
+ visible here. Fallbacks keep the original showcase sizing when a var is
198
+ absent (e.g. the Theme path, which doesn't emit the scale). */
199
+ .type-specimen .aa {
200
+ font-family: var(--tm-font-body);
201
+ font-weight: var(--tm-heading-weight, 700);
202
+ font-size: var(--tm-heading1-size, 2.4rem);
203
+ line-height: 1;
150
204
  color: var(--tm-color-text);
151
- margin: 0 0 0.75rem 0;
152
- text-align: left;
153
- transition: all 0.25s ease;
154
205
  }
155
206
 
156
- .brand-h5 {
157
- font-family: var(--tm-heading5-font, var(--tm-font-heading));
158
- font-size: var(--tm-heading5-size, 1.1rem);
159
- font-weight: var(--tm-heading5-weight, var(--tm-heading-weight));
160
- line-height: var(--tm-heading5-line-height, 1.2);
207
+ .type-specimen .headline {
208
+ font-family: var(--tm-font-body);
209
+ font-weight: var(--tm-heading-weight, 700);
210
+ font-size: var(--tm-heading2-size, 1.35rem);
161
211
  color: var(--tm-color-text);
162
- margin: 0.75rem 0;
163
- text-align: center;
164
- padding: 0.75rem;
165
- transition: all 0.25s ease;
166
212
  }
167
213
 
168
- .brand-h6 {
169
- font-family: var(--tm-heading6-font, var(--tm-font-heading));
170
- font-size: var(--tm-heading6-size, 1rem);
171
- font-weight: var(--tm-heading6-weight, var(--tm-heading-weight));
172
- line-height: var(--tm-heading6-line-height, 1.2);
214
+ .type-specimen .body {
215
+ font-family: var(--tm-font-body);
216
+ font-weight: var(--tm-body-weight, 400);
217
+ font-size: var(--tm-paragraph1-size, 0.9rem);
218
+ line-height: var(--tm-line-height, 1.5);
219
+ /* Plain fallback first; color-mix (softer muted text) wins where supported. */
173
220
  color: var(--tm-color-text);
174
- margin: 0 0 0.75rem 0;
175
- text-align: center;
176
- transition: all 0.25s ease;
221
+ color: color-mix(in srgb, var(--tm-color-text) 65%, transparent);
222
+ }
223
+
224
+ /* ===== PALETTE ===== */
225
+ .palette {
226
+ display: flex;
227
+ flex-direction: column;
228
+ gap: 9px;
229
+ padding: 0 4px;
230
+ }
231
+
232
+ .palette-title {
233
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
234
+ font-size: 0.68rem;
235
+ font-weight: 600;
236
+ letter-spacing: 0.14em;
237
+ text-transform: uppercase;
238
+ color: #8b909b;
239
+ margin-bottom: 2px;
177
240
  }
178
241
 
179
- .brand-p1 {
180
- font-family: var(--tm-paragraph1-font, var(--tm-font-body));
181
- font-size: var(--tm-paragraph1-size, 1rem);
182
- font-weight: var(--tm-paragraph1-weight, var(--tm-body-weight));
183
- line-height: var(--tm-paragraph1-line-height, var(--tm-line-height));
242
+ .palette-row {
243
+ display: grid;
244
+ grid-template-columns: 18px 90px 1fr;
245
+ align-items: center;
246
+ gap: 10px;
247
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
248
+ font-size: 0.8rem;
249
+ color: #c7ccd6;
250
+ }
251
+
252
+ .palette-row .swatch {
253
+ width: 18px;
254
+ height: 18px;
255
+ border-radius: 5px;
256
+ border: 1px solid rgba(255, 255, 255, 0.12);
257
+ transition: background-color 0.25s ease;
258
+ }
259
+
260
+ .palette-row .oklch {
261
+ font-family: "SFMono-Regular", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
262
+ font-size: 0.76rem;
263
+ color: #9aa0ac;
264
+ }
265
+ /* Live-updating OKLCH label, sourced from a --tm-*-oklch CSS variable so a
266
+ color edit re-renders the text without any script. */
267
+ .palette-row .oklch::before {
268
+ content: var(--oklch-label, "");
269
+ }
270
+
271
+ /* ===== FORM ===== */
272
+ .form-field {
273
+ display: flex;
274
+ flex-direction: column;
275
+ gap: 6px;
276
+ padding: 0 4px 4px;
277
+ }
278
+
279
+ .form-field label {
280
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
281
+ font-size: 0.8rem;
282
+ font-weight: 600;
283
+ color: #d7dae1;
284
+ }
285
+
286
+ .form-field input {
287
+ width: 100%;
288
+ padding: 10px 12px;
289
+ background: transparent;
290
+ border: 1px solid rgba(255, 255, 255, 0.14);
291
+ border-radius: var(--tm-border-radius, 8px);
292
+ color: #d7dae1;
293
+ font-family: var(--tm-font-body);
294
+ font-size: 0.85rem;
295
+ }
296
+
297
+ .form-field input::placeholder {
298
+ color: #6a6f7a;
299
+ }
300
+
301
+ *, *::before, *::after {
302
+ transition-property: color, background-color, border-color;
303
+ transition-duration: 0.25s;
304
+ transition-timing-function: ease;
305
+ }
306
+ </style>
307
+ </head>
308
+ <body>
309
+ <div class="snapshot-card">
310
+ <div class="snapshot-header">
311
+ <span class="label"><span class="eye" aria-hidden="true">&#128065;</span> Live snapshot</span>
312
+ <button type="button" class="full-preview">See full preview <span aria-hidden="true">&rarr;</span></button>
313
+ </div>
314
+
315
+ <div class="brand-surface">
316
+ <div class="brand-title"><span class="dot"></span>${brandName}</div>
317
+
318
+ <div class="action-row">
319
+ <button class="btn-primary">Primary action</button>
320
+ <span class="chip-accent">Accent</span>
321
+ </div>
322
+
323
+ <div class="type-specimen">
324
+ <div class="aa">Aa</div>
325
+ <div class="headline">Built on trust</div>
326
+ <div class="body">Surfaces stay calm, decisions stay legible. Every token here is the live draft.</div>
327
+ </div>
328
+ </div>
329
+
330
+ <div class="palette">
331
+ <div class="palette-title">Palette</div>
332
+ <div class="palette-row"><span class="swatch" style="background: var(--tm-color-primary);"></span><span>Primary</span><span class="oklch" style="--oklch-label: var(--tm-color-primary-oklch, '');"></span></div>
333
+ <div class="palette-row"><span class="swatch" style="background: var(--tm-color-secondary);"></span><span>Secondary</span><span class="oklch" style="--oklch-label: var(--tm-color-secondary-oklch, '');"></span></div>
334
+ <div class="palette-row"><span class="swatch" style="background: var(--tm-color-accent);"></span><span>Accent</span><span class="oklch" style="--oklch-label: var(--tm-color-accent-oklch, '');"></span></div>
335
+ <div class="palette-row"><span class="swatch" style="background: var(--tm-color-background);"></span><span>Background</span><span class="oklch" style="--oklch-label: var(--tm-color-background-oklch, '');"></span></div>
336
+ <div class="palette-row"><span class="swatch" style="background: var(--tm-color-surface);"></span><span>Muted</span><span class="oklch" style="--oklch-label: var(--tm-color-surface-oklch, '');"></span></div>
337
+ </div>
338
+
339
+ <div class="form-field">
340
+ <label for="work-email">Work email</label>
341
+ <input id="work-email" type="email" placeholder="name@company.com" readonly />
342
+ </div>
343
+ </div>
344
+
345
+ ${scriptTag}
346
+ </body>
347
+ </html>`;
348
+ }
349
+ function getBrandFullPreviewHtml(brand, options) {
350
+ const resolved = brandResolver.resolveBrandTokens(brand);
351
+ const variables = brandCss.brandToVariables(resolved);
352
+ return generateFullPreviewHtml(variables, options, resolved, brandTypes.getBrandTitle(resolved));
353
+ }
354
+ function generateFullPreviewHtml(variables, options, brand, title) {
355
+ const allVariables = { ...variables, ...brandCss.oklchDisplayVars(variables) };
356
+ const cssVariables = Object.entries(allVariables).map(([key, value]) => ` ${key}: ${value};`).join("\n");
357
+ const fontFaceRules = brand ? fontFace.generateFontFaceRules(brand) : "";
358
+ const scriptSrc = options?.runtimeScriptUri || "/_sfdc/theme-preview-runtime.js";
359
+ const nonce = options?.nonce || "";
360
+ const cspSource = options?.cspSource || "";
361
+ const cspMeta = cspSource ? ` <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}' ${cspSource}; img-src https: data: ${cspSource}; font-src https: data:;">` : "";
362
+ const brandName = escapeHtml(title && title.trim().length > 0 ? title : "Your Brand");
363
+ return `<!DOCTYPE html>
364
+ <html lang="en">
365
+ <head>
366
+ <meta charset="UTF-8">
367
+ ${cspMeta}
368
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
369
+ <title>Brand Preview</title>
370
+ <style>${fontFaceRules}
371
+ :root {
372
+ ${cssVariables}
373
+ }
374
+
375
+ * {
376
+ margin: 0;
377
+ padding: 0;
378
+ box-sizing: border-box;
379
+ }
380
+
381
+ body {
382
+ font-family: var(--tm-font-body);
383
+ font-size: var(--tm-font-size-base);
384
+ line-height: var(--tm-line-height, 1.5);
184
385
  color: var(--tm-color-text);
185
- margin: 0 0 0.75rem 0;
186
- text-align: left;
187
- transition: all 0.25s ease;
386
+ background: var(--tm-color-background);
387
+ -webkit-font-smoothing: antialiased;
388
+ -moz-osx-font-smoothing: grayscale;
389
+ min-height: 100vh;
188
390
  }
189
391
 
190
- .brand-p1.center {
191
- text-align: center;
392
+ .fp-page {
393
+ max-width: 1120px;
394
+ margin: 0 auto;
395
+ padding: 40px 56px 64px;
192
396
  }
193
397
 
194
- .brand-p2 {
195
- font-family: var(--tm-paragraph2-font, var(--tm-font-body));
196
- font-size: var(--tm-paragraph2-size, 0.875rem);
197
- font-weight: var(--tm-paragraph2-weight, var(--tm-body-weight));
198
- line-height: var(--tm-paragraph2-line-height, var(--tm-line-height));
398
+ /* Monospace chrome: the "PREVIEW · MARKETING/INDEX.TSX · OKLCH(...)" line. */
399
+ .fp-eyebrow {
400
+ display: flex;
401
+ align-items: center;
402
+ gap: 12px;
403
+ font-family: "SFMono-Regular", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
404
+ font-size: 0.72rem;
405
+ letter-spacing: 0.12em;
406
+ text-transform: uppercase;
407
+ color: color-mix(in srgb, var(--tm-color-text) 55%, transparent);
199
408
  color: var(--tm-color-text);
200
- margin: 0;
201
- text-align: center;
202
- transition: all 0.25s ease;
409
+ opacity: 0.55;
410
+ margin-bottom: 28px;
411
+ }
412
+
413
+ .fp-eyebrow .dot {
414
+ width: 8px;
415
+ height: 8px;
416
+ border-radius: 50%;
417
+ background: var(--tm-color-accent, var(--tm-color-primary));
418
+ opacity: 1;
419
+ transition: background-color 0.25s ease;
420
+ }
421
+
422
+ /* The trailing OKLCH(...) value is a live token label. */
423
+ .fp-eyebrow .token::before {
424
+ content: var(--tm-color-primary-oklch, "");
203
425
  }
204
426
 
205
- /* ===== BUTTONS ===== */
206
- .brand-btn-primary {
427
+ .fp-chip {
207
428
  display: inline-block;
208
- padding: var(--tm-button-padding-y, 0.75rem) var(--tm-button-padding-x, 1.5rem);
209
- background-color: var(--tm-button-primary-bg, var(--tm-color-primary));
429
+ padding: 0.5rem 0.9rem;
430
+ background: var(--tm-color-accent, var(--tm-color-primary));
431
+ color: var(--tm-color-background);
432
+ border-radius: 999px;
433
+ font-family: var(--tm-font-body);
434
+ font-weight: 700;
435
+ font-size: 0.72rem;
436
+ letter-spacing: 0.08em;
437
+ text-transform: uppercase;
438
+ margin-bottom: 24px;
439
+ transition: all 0.25s ease;
440
+ }
441
+
442
+ .fp-hero-title {
443
+ /* Base font family drives all specimen text — see the card note. */
444
+ font-family: var(--tm-font-body);
445
+ font-weight: var(--tm-heading-weight, 800);
446
+ font-size: clamp(2.6rem, 5.2vw, 4.4rem);
447
+ line-height: 1.02;
448
+ letter-spacing: -0.02em;
449
+ color: var(--tm-color-text);
450
+ max-width: 14ch;
451
+ margin-bottom: 28px;
452
+ }
453
+
454
+ .fp-hero-body {
455
+ font-family: var(--tm-font-body);
456
+ font-weight: var(--tm-body-weight, 400);
457
+ font-size: 1.15rem;
458
+ line-height: var(--tm-line-height, 1.6);
459
+ color: var(--tm-color-text);
460
+ opacity: 0.62;
461
+ max-width: 46ch;
462
+ margin-bottom: 32px;
463
+ }
464
+
465
+ .fp-actions {
466
+ display: flex;
467
+ flex-wrap: wrap;
468
+ gap: 14px;
469
+ margin-bottom: 64px;
470
+ }
471
+
472
+ .fp-btn-primary {
473
+ padding: var(--tm-button-padding-y, 0.85rem) var(--tm-button-padding-x, 1.4rem);
474
+ background: var(--tm-button-primary-bg, var(--tm-color-primary));
210
475
  color: var(--tm-button-primary-text, #ffffff);
211
476
  border: var(--tm-button-primary-border-width, 0) solid var(--tm-button-primary-border, transparent);
212
- border-radius: var(--tm-button-radius, var(--tm-border-radius));
477
+ border-radius: var(--tm-button-radius, var(--tm-border-radius, 10px));
213
478
  font-family: var(--tm-font-body);
214
- font-size: 1rem;
215
- font-weight: var(--tm-body-weight);
216
- text-decoration: none;
479
+ font-weight: var(--tm-body-weight, 600);
480
+ font-size: 0.95rem;
217
481
  cursor: pointer;
218
482
  transition: all 0.25s ease;
219
- margin: 0 0 0.75rem 0;
220
483
  }
221
484
 
222
- .brand-btn-primary:hover {
223
- opacity: 0.9;
224
- transform: translateY(-1px);
225
- }
226
-
227
- .brand-btn-secondary {
228
- display: inline-block;
229
- padding: var(--tm-button-secondary-padding-y, var(--tm-button-padding-y, 0.75rem)) var(--tm-button-secondary-padding-x, var(--tm-button-padding-x, 1.5rem));
230
- background-color: var(--tm-button-secondary-bg, transparent);
231
- color: var(--tm-button-secondary-text, var(--tm-color-primary));
232
- border: var(--tm-button-secondary-border-width, 1px) solid var(--tm-button-secondary-border, var(--tm-color-primary));
233
- border-radius: var(--tm-button-secondary-radius, var(--tm-button-radius, var(--tm-border-radius)));
485
+ .fp-btn-secondary {
486
+ padding: var(--tm-button-secondary-padding-y, var(--tm-button-padding-y, 0.85rem)) var(--tm-button-secondary-padding-x, var(--tm-button-padding-x, 1.4rem));
487
+ background: var(--tm-button-secondary-bg, var(--tm-color-accent, var(--tm-color-primary)));
488
+ color: var(--tm-button-secondary-text, var(--tm-color-background));
489
+ border: var(--tm-button-secondary-border-width, 0) solid var(--tm-button-secondary-border, transparent);
490
+ border-radius: var(--tm-button-secondary-radius, var(--tm-button-radius, var(--tm-border-radius, 10px)));
234
491
  font-family: var(--tm-font-body);
235
- font-size: 1rem;
236
- font-weight: var(--tm-body-weight);
237
- text-decoration: none;
492
+ font-weight: var(--tm-body-weight, 600);
493
+ font-size: 0.95rem;
238
494
  cursor: pointer;
239
495
  transition: all 0.25s ease;
240
- margin: 0 0 0.75rem 0;
241
496
  }
242
497
 
243
- .brand-btn-secondary:hover {
244
- opacity: 0.9;
245
- transform: translateY(-1px);
498
+ /* Feature grid */
499
+ .fp-grid {
500
+ display: grid;
501
+ grid-template-columns: repeat(3, 1fr);
502
+ gap: 20px;
503
+ margin-bottom: 56px;
246
504
  }
247
505
 
248
- .brand-btn-tertiary {
249
- display: inline-block;
250
- padding: var(--tm-button-tertiary-padding-y, var(--tm-button-padding-y, 0.75rem)) var(--tm-button-tertiary-padding-x, var(--tm-button-padding-x, 1.5rem));
251
- background-color: var(--tm-button-tertiary-bg, transparent);
252
- color: var(--tm-button-tertiary-text, var(--tm-color-primary));
253
- border: var(--tm-button-tertiary-border-width, 0) solid var(--tm-button-tertiary-border, transparent);
254
- border-radius: var(--tm-button-tertiary-radius, var(--tm-button-radius, var(--tm-border-radius)));
506
+ @media (max-width: 900px) {
507
+ .fp-page { padding: 32px 24px 48px; }
508
+ .fp-grid { grid-template-columns: 1fr; }
509
+ }
510
+
511
+ .fp-card {
512
+ background: color-mix(in srgb, var(--tm-color-text) 4%, var(--tm-color-background));
513
+ border: 1px solid color-mix(in srgb, var(--tm-color-text) 8%, transparent);
514
+ border-radius: var(--tm-border-radius-round, var(--tm-border-radius, 16px));
515
+ padding: 24px;
516
+ transition: background-color 0.25s ease, border-color 0.25s ease;
517
+ }
518
+
519
+ .fp-card .icon {
520
+ display: inline-flex;
521
+ align-items: center;
522
+ justify-content: center;
523
+ width: 40px;
524
+ height: 40px;
525
+ border-radius: 10px;
526
+ background: var(--tm-color-text);
527
+ color: var(--tm-color-background);
528
+ font-size: 1.1rem;
529
+ margin-bottom: 20px;
530
+ transition: background-color 0.25s ease, color 0.25s ease;
531
+ }
532
+
533
+ .fp-card h3 {
255
534
  font-family: var(--tm-font-body);
256
- font-size: 1rem;
257
- font-weight: var(--tm-body-weight);
258
- text-decoration: none;
259
- cursor: pointer;
260
- transition: all 0.25s ease;
261
- margin: 0 0 0.75rem 0;
535
+ font-weight: var(--tm-heading-weight, 700);
536
+ font-size: 1.1rem;
537
+ color: var(--tm-color-text);
538
+ margin-bottom: 10px;
262
539
  }
263
540
 
264
- .brand-btn-tertiary:hover {
265
- opacity: 0.9;
266
- transform: translateY(-1px);
541
+ .fp-card p {
542
+ font-family: var(--tm-font-body);
543
+ font-size: 0.9rem;
544
+ line-height: var(--tm-line-height, 1.55);
545
+ color: var(--tm-color-text);
546
+ opacity: 0.58;
267
547
  }
268
548
 
269
- /* ===== SECTION 5 BORDERED BOX ===== */
270
- .brand-bordered-box {
271
- border-radius: var(--tm-border-radius-round, var(--tm-border-radius));
272
- border: var(--tm-border-weight-medium, 2px) solid var(--tm-color-text-secondary, var(--tm-color-surface));
273
- padding: 0 0.75rem 0.75rem 0.75rem;
274
- background-color: var(--tm-color-background);
275
- transition: all 0.25s ease;
549
+ /* Footer with token chrome */
550
+ .fp-footer {
551
+ display: flex;
552
+ align-items: center;
553
+ justify-content: space-between;
554
+ flex-wrap: wrap;
555
+ gap: 12px;
556
+ padding-top: 24px;
557
+ border-top: 1px solid color-mix(in srgb, var(--tm-color-text) 10%, transparent);
558
+ font-family: "SFMono-Regular", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
559
+ font-size: 0.78rem;
560
+ color: var(--tm-color-text);
561
+ opacity: 0.5;
562
+ }
563
+
564
+ .fp-footer .radius::before {
565
+ content: "--radius: " var(--tm-border-radius-label, "");
276
566
  }
277
567
 
278
- /* ===== TRANSITIONS FOR LIVE PREVIEW ===== */
279
568
  *, *::before, *::after {
280
- transition-property: color, background-color, border-color, box-shadow, transform;
569
+ transition-property: color, background-color, border-color;
281
570
  transition-duration: 0.25s;
282
571
  transition-timing-function: ease;
283
572
  }
284
573
  </style>
285
574
  </head>
286
575
  <body>
287
-
288
- <!-- SECTION 1: Hero Image (a brand logo replaces it when present) -->
289
- <section class="brand-section" data-brand-hero-section>
290
- ${heroImg}${logoImg}
291
- </section>
292
-
293
- <!-- SECTION 2: H1 + Paragraph 1 + Primary Button -->
294
- <section class="brand-section">
295
- <h1 class="brand-h1">Heading 1</h1>
296
- <p class="brand-p1">Here is an example of Paragraph 1 text, along with an example of a Primary button. When you customize most brand values, this panel shows your changes.</p>
297
- <button class="brand-btn-primary">Button</button>
298
- <h2 class="brand-h2">Heading 2</h2>
299
- </section>
300
-
301
- <!-- SECTION 3: Two-column (Image Left, H3+P1+Secondary Button Right) -->
302
- <section class="brand-section brand-two-column">
303
- <div>
304
- ${section3Image ? `<img src="${section3Image}" alt="" style="margin-right: 0.375rem;" />` : ""}
305
- </div>
306
- <div style="padding-left: 0.375rem;">
307
- <h3 class="brand-h3">Heading 3</h3>
308
- <p class="brand-p1">Here's some more Paragraph 1 text, along with an example of a Secondary button.</p>
309
- <button class="brand-btn-secondary">Button</button>
576
+ <main class="fp-page">
577
+ <div class="fp-eyebrow">
578
+ <span class="dot"></span>
579
+ <span>Preview &middot; marketing/index.tsx &middot; <span class="token"></span></span>
310
580
  </div>
311
- </section>
312
-
313
- <!-- SECTION 4: Two-column (H4+P1+Tertiary Button Left, Image Right) -->
314
- <section class="brand-section brand-two-column">
315
- <div style="padding-right: 0.375rem;">
316
- <h4 class="brand-h4">Heading 4</h4>
317
- <p class="brand-p1">Following this Paragraph 1 text is a Tertiary button. This button type often has no border, but you can add one.</p>
318
- <button class="brand-btn-tertiary">Button</button>
319
- </div>
320
- <div>
321
- ${section4Image ? `<img src="${section4Image}" alt="" style="margin-left: 0.375rem;" />` : ""}
581
+
582
+ <span class="fp-chip"><span aria-hidden="true">&#10022;</span> High-Intensity Wellness</span>
583
+
584
+ <h1 class="fp-hero-title">${brandName}, with the calm of a system that remembers.</h1>
585
+
586
+ <p class="fp-hero-body">Built for teams that move quickly without breaking trust. Every surface stays legible, every decision stays reviewable, every ship stays accountable.</p>
587
+
588
+ <div class="fp-actions">
589
+ <button class="fp-btn-primary">Get started <span aria-hidden="true">&#8599;</span></button>
590
+ <button class="fp-btn-secondary">Talk to ${brandName}</button>
322
591
  </div>
323
- </section>
324
592
 
325
- <!-- SECTION 5: Centered Bordered Box (H5 + P1 showing border-radius + border-weight) -->
326
- <section class="brand-section">
327
- <div class="brand-bordered-box">
328
- <h5 class="brand-h5">Heading 5</h5>
329
- <p class="brand-p1 center">The border around this heading and Paragraph 1 text updates to show changes that you make to the border radius and the Medium border weight.</p>
593
+ <div class="fp-grid">
594
+ <div class="fp-card">
595
+ <span class="icon" aria-hidden="true">&#9673;</span>
596
+ <h3>Plan-first execution</h3>
597
+ <p>Every step approved before it runs. No silent writes, no surprise deploys.</p>
598
+ </div>
599
+ <div class="fp-card">
600
+ <span class="icon" aria-hidden="true">&#9776;</span>
601
+ <h3>Org-aware automation</h3>
602
+ <p>Sandbox is sandbox, prod is prod. Type-safe operations across every connected workspace.</p>
603
+ </div>
604
+ <div class="fp-card">
605
+ <span class="icon" aria-hidden="true">&#10022;</span>
606
+ <h3>Reviewable artifacts</h3>
607
+ <p>Diffs, plans, and results stay durable so the team can audit a session a quarter later.</p>
608
+ </div>
330
609
  </div>
331
- </section>
332
610
 
333
- <!-- SECTION 6: Footer (H6 + Paragraph 2) -->
334
- <section class="brand-section">
335
- <h6 class="brand-h6">Heading 6</h6>
336
- <p class="brand-p2">Here's an example of Paragraph 2 text. This text is often smaller than Paragraph 1 text, perfect for content that you want to be slightly less prominent.</p>
337
- </section>
611
+ <div class="fp-footer">
612
+ <span>&copy; ${brandName}. Tokens loaded from tokens.css.</span>
613
+ <span class="radius"></span>
614
+ </div>
615
+ </main>
338
616
 
339
617
  <script${nonce ? ` nonce="${nonce}"` : ""} src="${scriptSrc}"><\/script>
340
618
  </body>
341
619
  </html>`;
342
620
  }
621
+ exports.getBrandFullPreviewHtml = getBrandFullPreviewHtml;
343
622
  exports.getBrandPreviewHtml = getBrandPreviewHtml;
344
623
  exports.getPreviewHtml = getPreviewHtml;