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