@bison-lab/payload-core 3.8.0 → 3.10.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/README.md +49 -49
- package/dist/admin.d.mts +71 -6
- package/dist/admin.d.mts.map +1 -1
- package/dist/admin.mjs +1877 -178
- package/dist/admin.mjs.map +1 -1
- package/dist/identity-Bg-G05mb.mjs +199 -0
- package/dist/identity-Bg-G05mb.mjs.map +1 -0
- package/dist/index.d.mts +62 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +507 -90
- package/dist/index.mjs.map +1 -1
- package/dist/library-BGq4wGif.d.mts +189 -0
- package/dist/library-BGq4wGif.d.mts.map +1 -0
- package/dist/map-BUDkX8g1.mjs +111 -0
- package/dist/map-BUDkX8g1.mjs.map +1 -0
- package/dist/react.mjs +3 -58
- package/dist/react.mjs.map +1 -1
- package/dist/theme.d.mts +12 -7
- package/dist/theme.d.mts.map +1 -1
- package/dist/theme.mjs +19 -6
- package/dist/theme.mjs.map +1 -1
- package/package.json +5 -4
- package/dist/types-DWwL-JEr.mjs +0 -76
- package/dist/types-DWwL-JEr.mjs.map +0 -1
- package/dist/types-pFpmDeSA.d.mts +0 -91
- package/dist/types-pFpmDeSA.d.mts.map +0 -1
package/dist/admin.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { r as stateFromColorDoc, t as colorDocFromState } from "./map-BUDkX8g1.mjs";
|
|
3
|
+
import { FieldDescription, FieldError, FieldLabel, useField, useForm, useFormFields } from "@payloadcms/ui";
|
|
4
|
+
import { GREY_SCALES, SHADE_STEPS, contrastForeground, contrastRatio, createColorScale, hexToHSL, hslToString, includedShadeSteps, isThemeHex, presetHints, regenerateColorScale, setColorScaleInclude, updateColorScale } from "@bison-lab/tokens";
|
|
4
5
|
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import {
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { createPortal } from "react-dom";
|
|
8
|
+
import { findFont, fontFaceCss, isFontId } from "@bison-lab/fonts";
|
|
7
9
|
//#region src/admin/color-field.tsx
|
|
8
10
|
const HEX_ERROR = "Enter a six-digit hex colour like #1e3a5f";
|
|
9
11
|
/**
|
|
@@ -39,7 +41,7 @@ const ColorField = ({ field, path, readOnly }) => {
|
|
|
39
41
|
/* @__PURE__ */ jsx("style", {
|
|
40
42
|
href: "bl-color-field",
|
|
41
43
|
precedence: "default",
|
|
42
|
-
children: CSS$
|
|
44
|
+
children: CSS$4
|
|
43
45
|
}),
|
|
44
46
|
/* @__PURE__ */ jsx(FieldLabel, {
|
|
45
47
|
htmlFor: hexId,
|
|
@@ -98,7 +100,7 @@ const ColorField = ({ field, path, readOnly }) => {
|
|
|
98
100
|
]
|
|
99
101
|
});
|
|
100
102
|
};
|
|
101
|
-
const CSS$
|
|
103
|
+
const CSS$4 = `
|
|
102
104
|
.bl-color-field__control{display:flex;align-items:center;gap:calc(var(--base) * .4)}
|
|
103
105
|
.bl-color-field__swatch{flex:none;width:calc(var(--base) * 1.6);height:calc(var(--base) * 1.6);border:1px solid var(--theme-elevation-150);border-radius:var(--style-radius-s);background:var(--theme-elevation-50)}
|
|
104
106
|
.bl-color-field__picker{flex:none;width:calc(var(--base) * 2);height:calc(var(--base) * 2);padding:0;border:1px solid var(--theme-elevation-150);border-radius:var(--style-radius-s);background:transparent;cursor:pointer}
|
|
@@ -108,11 +110,1050 @@ const CSS$2 = `
|
|
|
108
110
|
.bl-color-field__hex:disabled{color:var(--theme-elevation-400)}
|
|
109
111
|
`;
|
|
110
112
|
//#endregion
|
|
113
|
+
//#region src/theme/appearance-labels.ts
|
|
114
|
+
/**
|
|
115
|
+
* Named choices the Appearance child shows. Values stay the token presets;
|
|
116
|
+
* labels match the mock workflow (Soft → pill, Gentle → minimal, Balanced
|
|
117
|
+
* → default, Follow device → system). No rem, ms, or CSS variable tables.
|
|
118
|
+
*/
|
|
119
|
+
const APPEARANCE_CHOICES = {
|
|
120
|
+
defaultTheme: {
|
|
121
|
+
light: {
|
|
122
|
+
label: "Light",
|
|
123
|
+
hint: "Always opens in light mode"
|
|
124
|
+
},
|
|
125
|
+
dark: {
|
|
126
|
+
label: "Dark",
|
|
127
|
+
hint: "Always opens in dark mode"
|
|
128
|
+
},
|
|
129
|
+
system: {
|
|
130
|
+
label: "Follow device",
|
|
131
|
+
hint: "Matches the visitor’s system setting"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
radius: {
|
|
135
|
+
sharp: {
|
|
136
|
+
label: "Sharp",
|
|
137
|
+
hint: "Crisp and structured"
|
|
138
|
+
},
|
|
139
|
+
subtle: {
|
|
140
|
+
label: "Subtle",
|
|
141
|
+
hint: "Clean with a little softness"
|
|
142
|
+
},
|
|
143
|
+
rounded: {
|
|
144
|
+
label: "Rounded",
|
|
145
|
+
hint: "Friendly and contemporary"
|
|
146
|
+
},
|
|
147
|
+
pill: {
|
|
148
|
+
label: "Soft",
|
|
149
|
+
hint: "Very rounded and expressive"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
shadow: {
|
|
153
|
+
flat: {
|
|
154
|
+
label: "Flat",
|
|
155
|
+
hint: "Minimal visual depth"
|
|
156
|
+
},
|
|
157
|
+
subtle: {
|
|
158
|
+
label: "Subtle",
|
|
159
|
+
hint: "Light separation between surfaces"
|
|
160
|
+
},
|
|
161
|
+
elevated: {
|
|
162
|
+
label: "Elevated",
|
|
163
|
+
hint: "More noticeable layering and depth"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
motion: {
|
|
167
|
+
snappy: {
|
|
168
|
+
label: "Quick",
|
|
169
|
+
hint: "Fast and responsive"
|
|
170
|
+
},
|
|
171
|
+
smooth: {
|
|
172
|
+
label: "Smooth",
|
|
173
|
+
hint: "Balanced and natural"
|
|
174
|
+
},
|
|
175
|
+
minimal: {
|
|
176
|
+
label: "Gentle",
|
|
177
|
+
hint: "Slower and more relaxed"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
density: {
|
|
181
|
+
compact: {
|
|
182
|
+
label: "Compact",
|
|
183
|
+
hint: "Fits more content on screen"
|
|
184
|
+
},
|
|
185
|
+
default: {
|
|
186
|
+
label: "Balanced",
|
|
187
|
+
hint: "Comfortable for most sites"
|
|
188
|
+
},
|
|
189
|
+
spacious: {
|
|
190
|
+
label: "Spacious",
|
|
191
|
+
hint: "More breathing room and larger controls"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const APPEARANCE_FAMILY_COPY = {
|
|
196
|
+
defaultTheme: {
|
|
197
|
+
title: "Default theme",
|
|
198
|
+
hint: "Choose how the public site opens for visitors."
|
|
199
|
+
},
|
|
200
|
+
radius: {
|
|
201
|
+
title: "Corner style",
|
|
202
|
+
hint: "Controls the shape of buttons, inputs, cards, and panels across the site."
|
|
203
|
+
},
|
|
204
|
+
shadow: {
|
|
205
|
+
title: "Depth",
|
|
206
|
+
hint: "Choose how much cards and floating elements stand out from the page."
|
|
207
|
+
},
|
|
208
|
+
motion: {
|
|
209
|
+
title: "Motion",
|
|
210
|
+
hint: "Controls how quickly menus, cards, and interface transitions move."
|
|
211
|
+
},
|
|
212
|
+
density: {
|
|
213
|
+
title: "Spacing",
|
|
214
|
+
hint: "Choose how compact or roomy forms, buttons, cards, and page sections feel."
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const FONT_ROLE_COPY = {
|
|
218
|
+
heading: {
|
|
219
|
+
label: "Heading font",
|
|
220
|
+
hint: "Used for page titles, section headings, and cards"
|
|
221
|
+
},
|
|
222
|
+
body: {
|
|
223
|
+
label: "Body font",
|
|
224
|
+
hint: "Used for paragraphs, navigation, forms, and buttons"
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
const SYSTEM_COLOR_COPY = {
|
|
228
|
+
primary: {
|
|
229
|
+
label: "Primary",
|
|
230
|
+
hint: "Primary actions and key links",
|
|
231
|
+
group: "brand"
|
|
232
|
+
},
|
|
233
|
+
secondary: {
|
|
234
|
+
label: "Secondary",
|
|
235
|
+
hint: "Supporting accents and eyebrows",
|
|
236
|
+
group: "brand"
|
|
237
|
+
},
|
|
238
|
+
accent: {
|
|
239
|
+
label: "Accent",
|
|
240
|
+
hint: "Dark brand surfaces",
|
|
241
|
+
group: "brand"
|
|
242
|
+
},
|
|
243
|
+
highlight: {
|
|
244
|
+
label: "Highlight",
|
|
245
|
+
hint: "Promotional and emphasis fills",
|
|
246
|
+
group: "brand"
|
|
247
|
+
},
|
|
248
|
+
success: {
|
|
249
|
+
label: "Success",
|
|
250
|
+
hint: "Theme chrome for confirmations. Not offered as a page-editor fill.",
|
|
251
|
+
group: "status"
|
|
252
|
+
},
|
|
253
|
+
destructive: {
|
|
254
|
+
label: "Destructive",
|
|
255
|
+
hint: "Theme chrome for errors and dangerous actions. Not offered as a page-editor fill.",
|
|
256
|
+
group: "status"
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/theme/readability.ts
|
|
261
|
+
/** Same threshold `deriveDarkPalette` uses for brand fills. */
|
|
262
|
+
const DARK_FILL_LIGHTEN_BELOW = 40;
|
|
263
|
+
const DARK_FILL_LIGHTEN_BY = 25;
|
|
264
|
+
function clamp(value, min, max) {
|
|
265
|
+
return Math.min(max, Math.max(min, value));
|
|
266
|
+
}
|
|
267
|
+
function darkFill(color) {
|
|
268
|
+
if (color.l < DARK_FILL_LIGHTEN_BELOW) return {
|
|
269
|
+
...color,
|
|
270
|
+
l: clamp(color.l + DARK_FILL_LIGHTEN_BY, 0, 100)
|
|
271
|
+
};
|
|
272
|
+
return color;
|
|
273
|
+
}
|
|
274
|
+
function sample(fill, target) {
|
|
275
|
+
const label = contrastForeground(fill);
|
|
276
|
+
const fillCss = hslToString(fill);
|
|
277
|
+
const ratio = contrastRatio(fillCss, label);
|
|
278
|
+
return {
|
|
279
|
+
ratio,
|
|
280
|
+
shortfall: Math.max(0, Math.round((target - ratio) * 100) / 100),
|
|
281
|
+
fill: fillCss,
|
|
282
|
+
label
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Editable fill + the automatic label, Light and Dark. Target 7:1.
|
|
287
|
+
* Meets only when both pass. Warnings do not block save.
|
|
288
|
+
*/
|
|
289
|
+
function fillLabelWarning(id, name, hex, target = 7) {
|
|
290
|
+
if (!isThemeHex(hex)) return null;
|
|
291
|
+
const lightFill = hexToHSL(hex);
|
|
292
|
+
const light = sample(lightFill, target);
|
|
293
|
+
const dark = sample(darkFill(lightFill), target);
|
|
294
|
+
return {
|
|
295
|
+
id,
|
|
296
|
+
name,
|
|
297
|
+
light,
|
|
298
|
+
dark,
|
|
299
|
+
meets: light.ratio >= target && dark.ratio >= target
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
/** Green/teal family. A warning at admin time, not a schema block. */
|
|
303
|
+
function successHueWarning(hex) {
|
|
304
|
+
if (!isThemeHex(hex)) return null;
|
|
305
|
+
const { h } = hexToHSL(hex);
|
|
306
|
+
if (h >= 80 && h <= 180) return null;
|
|
307
|
+
return "Keep success colors recognizable — stay in a green or teal family.";
|
|
308
|
+
}
|
|
309
|
+
/** Red family. A warning at admin time, not a schema block. */
|
|
310
|
+
function destructiveHueWarning(hex) {
|
|
311
|
+
if (!isThemeHex(hex)) return null;
|
|
312
|
+
const { h } = hexToHSL(hex);
|
|
313
|
+
if (h <= 30 || h >= 330) return null;
|
|
314
|
+
return "Keep destructive colors recognizable — stay in a red family.";
|
|
315
|
+
}
|
|
316
|
+
function automaticLabel(hex) {
|
|
317
|
+
if (!isThemeHex(hex)) return null;
|
|
318
|
+
return contrastForeground(hexToHSL(hex));
|
|
319
|
+
}
|
|
320
|
+
function hslCss(value) {
|
|
321
|
+
return `hsl(${value})`;
|
|
322
|
+
}
|
|
323
|
+
/** Which appearances fail, in words. Empty when both pass. */
|
|
324
|
+
function hardToReadModes(item) {
|
|
325
|
+
const light = item.light.shortfall > 0;
|
|
326
|
+
const dark = item.dark.shortfall > 0;
|
|
327
|
+
if (light && dark) return "in Light and Dark";
|
|
328
|
+
if (light) return "in Light";
|
|
329
|
+
if (dark) return "in Dark";
|
|
330
|
+
return "";
|
|
331
|
+
}
|
|
332
|
+
function readabilityHeadline(item) {
|
|
333
|
+
return item.meets ? `Text on ${item.name} looks readable` : `Text on ${item.name} may be hard to read`;
|
|
334
|
+
}
|
|
335
|
+
function readabilityModesLine(item) {
|
|
336
|
+
const modes = hardToReadModes(item);
|
|
337
|
+
return modes ? `Hard to read ${modes}.` : "Looks good in Light and Dark.";
|
|
338
|
+
}
|
|
339
|
+
function readabilityGapPercent(sample, target = 7) {
|
|
340
|
+
if (sample.shortfall <= 0) return 0;
|
|
341
|
+
return Math.round(sample.shortfall / target * 100);
|
|
342
|
+
}
|
|
343
|
+
function readabilityRatioLine(sample, target = 7) {
|
|
344
|
+
const percent = readabilityGapPercent(sample, target);
|
|
345
|
+
if (percent <= 0) return "Meets the contrast we want";
|
|
346
|
+
if (percent < 10) return "A little short of the contrast we want";
|
|
347
|
+
return `About ${percent}% short of the contrast we want`;
|
|
348
|
+
}
|
|
349
|
+
const READABILITY_FIX = "Darken or lighten the hex, then regenerate the scale. The site picks light or dark type for you.";
|
|
350
|
+
const READABILITY_PUBLISH_NOTE = "You can still publish. To improve a color, darken or lighten it and regenerate.";
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/admin/preview-mode.tsx
|
|
353
|
+
const ATTR = "data-bl-theme-preview";
|
|
354
|
+
const EVENT = "bl-theme-preview";
|
|
355
|
+
function adminIsDark() {
|
|
356
|
+
if (typeof document === "undefined") return false;
|
|
357
|
+
const root = document.documentElement;
|
|
358
|
+
return root.getAttribute("data-theme") === "dark" || root.getAttribute("data-theme-pref") === "dark" || root.classList.contains("dark");
|
|
359
|
+
}
|
|
360
|
+
function readMode() {
|
|
361
|
+
if (typeof document === "undefined") return "light";
|
|
362
|
+
const set = document.documentElement.getAttribute(ATTR);
|
|
363
|
+
if (set === "dark" || set === "light") return set;
|
|
364
|
+
return adminIsDark() ? "dark" : "light";
|
|
365
|
+
}
|
|
366
|
+
function setThemePreviewMode(mode) {
|
|
367
|
+
if (typeof document === "undefined") return;
|
|
368
|
+
document.documentElement.setAttribute(ATTR, mode);
|
|
369
|
+
window.dispatchEvent(new Event(EVENT));
|
|
370
|
+
}
|
|
371
|
+
function useThemePreviewMode() {
|
|
372
|
+
const [mode, setMode] = useState("light");
|
|
373
|
+
useEffect(() => {
|
|
374
|
+
const sync = () => setMode(readMode());
|
|
375
|
+
sync();
|
|
376
|
+
window.addEventListener(EVENT, sync);
|
|
377
|
+
return () => window.removeEventListener(EVENT, sync);
|
|
378
|
+
}, []);
|
|
379
|
+
return mode;
|
|
380
|
+
}
|
|
381
|
+
function ThemePreviewToggle() {
|
|
382
|
+
const mode = useThemePreviewMode();
|
|
383
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
384
|
+
className: "bl-preview-toggle",
|
|
385
|
+
role: "group",
|
|
386
|
+
"aria-label": "Preview fills in",
|
|
387
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
388
|
+
className: "bl-preview-toggle__label",
|
|
389
|
+
children: "Preview fills"
|
|
390
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
391
|
+
className: "bl-preview-toggle__pills",
|
|
392
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
393
|
+
type: "button",
|
|
394
|
+
"aria-pressed": mode === "light",
|
|
395
|
+
onClick: () => setThemePreviewMode("light"),
|
|
396
|
+
children: "Light"
|
|
397
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
398
|
+
type: "button",
|
|
399
|
+
"aria-pressed": mode === "dark",
|
|
400
|
+
onClick: () => setThemePreviewMode("dark"),
|
|
401
|
+
children: "Dark"
|
|
402
|
+
})]
|
|
403
|
+
})]
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const THEME_PREVIEW_TOGGLE_CSS = `
|
|
407
|
+
.bl-preview-toggle{display:inline-flex;align-items:center;gap:.6rem}
|
|
408
|
+
.bl-preview-toggle__label{font-size:12px;font-weight:600;color:#a1a1aa}
|
|
409
|
+
.bl-preview-toggle__pills{display:inline-flex;padding:3px;border:1px solid #3f3f46;border-radius:10px;background:#18181b}
|
|
410
|
+
.bl-preview-toggle__pills button{min-height:2rem;padding:.25rem .8rem;border:0;border-radius:7px;background:transparent;color:#d4d4d8;font:inherit;font-size:13px;font-weight:600;cursor:pointer}
|
|
411
|
+
.bl-preview-toggle__pills button[aria-pressed=true]{background:#f4f4f5;color:#18181b;box-shadow:0 1px 2px rgba(0,0,0,.25)}
|
|
412
|
+
html[data-theme=light] .bl-preview-toggle__label{color:#52525b}
|
|
413
|
+
html[data-theme=light] .bl-preview-toggle__pills{border-color:#d4d4d8;background:#e4e4e7}
|
|
414
|
+
html[data-theme=light] .bl-preview-toggle__pills button{color:#3f3f46}
|
|
415
|
+
html[data-theme=light] .bl-preview-toggle__pills button[aria-pressed=true]{background:#fff;color:#18181b;box-shadow:0 1px 2px rgba(15,23,42,.12)}
|
|
416
|
+
`;
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/admin/readability-list.tsx
|
|
419
|
+
function ReadabilityDetail({ item, target = 7 }) {
|
|
420
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
421
|
+
className: "bl-readability-detail",
|
|
422
|
+
children: [
|
|
423
|
+
/* @__PURE__ */ jsx("p", {
|
|
424
|
+
className: "bl-readability-detail__lead",
|
|
425
|
+
children: readabilityModesLine(item)
|
|
426
|
+
}),
|
|
427
|
+
/* @__PURE__ */ jsx("div", {
|
|
428
|
+
className: "bl-readability-detail__modes",
|
|
429
|
+
children: [["Light", item.light], ["Dark", item.dark]].map(([mode, sample]) => /* @__PURE__ */ jsxs("figure", {
|
|
430
|
+
"data-failing": sample.shortfall > 0 ? "" : void 0,
|
|
431
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
432
|
+
"aria-hidden": "true",
|
|
433
|
+
className: "bl-readability__swatch",
|
|
434
|
+
style: {
|
|
435
|
+
background: hslCss(sample.fill),
|
|
436
|
+
color: hslCss(sample.label)
|
|
437
|
+
},
|
|
438
|
+
children: "Aa"
|
|
439
|
+
}), /* @__PURE__ */ jsxs("figcaption", { children: [
|
|
440
|
+
/* @__PURE__ */ jsx("strong", { children: mode }),
|
|
441
|
+
/* @__PURE__ */ jsx("span", { children: sample.shortfall > 0 ? "Hard to read" : "Looks good" }),
|
|
442
|
+
/* @__PURE__ */ jsx("span", { children: readabilityRatioLine(sample, target) })
|
|
443
|
+
] })]
|
|
444
|
+
}, mode))
|
|
445
|
+
}),
|
|
446
|
+
item.meets ? null : /* @__PURE__ */ jsx("p", {
|
|
447
|
+
className: "bl-readability-detail__fix",
|
|
448
|
+
children: READABILITY_FIX
|
|
449
|
+
})
|
|
450
|
+
]
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
const READABILITY_LIST_CSS = `
|
|
454
|
+
.bl-readability{padding:calc(var(--base,16px) * .8);border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:var(--style-radius-m,12px);background:var(--theme-elevation-0,#fff)}
|
|
455
|
+
.bl-readability__head{display:flex;justify-content:space-between;gap:1rem;align-items:flex-start}
|
|
456
|
+
.bl-readability__head h2{margin:0;font-size:1rem}
|
|
457
|
+
.bl-readability__head p{margin:.35rem 0 0;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
458
|
+
.bl-readability__head span{flex:none;font-size:12px;font-weight:600;padding:.2rem .55rem;border-radius:999px;background:var(--theme-elevation-100,#f1f5f9)}
|
|
459
|
+
.bl-readability__head span[data-attention]{background:#fffbeb;color:#92400e}
|
|
460
|
+
.bl-readability ul{margin:.85rem 0 0;padding:0;list-style:none}
|
|
461
|
+
.bl-readability li{padding:.65rem 0;border-top:1px solid var(--theme-elevation-100,#f1f5f9)}
|
|
462
|
+
.bl-readability li:first-child{border-top:0}
|
|
463
|
+
.bl-readability li[data-failing]{background:#fff7ed;margin:0 -.4rem;padding:.65rem .4rem;border-radius:8px}
|
|
464
|
+
.bl-readability__swatch{flex:none;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2rem;border-radius:6px;font-weight:700;font-size:13px}
|
|
465
|
+
.bl-readability strong{display:block;font-size:13px}
|
|
466
|
+
.bl-readability-detail__lead{margin:0 0 .65rem;font-size:13px;color:var(--theme-elevation-600,#475569)}
|
|
467
|
+
.bl-readability-detail__modes{display:grid;grid-template-columns:1fr 1fr;gap:.65rem}
|
|
468
|
+
.bl-readability-detail__modes figure{display:flex;gap:.6rem;align-items:center;margin:0;padding:.55rem .6rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:10px;background:#fff}
|
|
469
|
+
.bl-readability-detail__modes figure[data-failing]{border-color:#fdba74;background:#fff7ed}
|
|
470
|
+
.bl-readability-detail__modes figcaption{display:flex;flex-direction:column;gap:.1rem;min-width:0}
|
|
471
|
+
.bl-readability-detail__modes figcaption strong{font-size:12px}
|
|
472
|
+
.bl-readability-detail__modes figcaption span{font-size:11px;color:var(--theme-elevation-500,#64748b)}
|
|
473
|
+
.bl-readability-detail__fix{margin:.7rem 0 0;font-size:13px;color:var(--theme-elevation-600,#475569)}
|
|
474
|
+
`;
|
|
475
|
+
//#endregion
|
|
476
|
+
//#region src/admin/color-scale-card.tsx
|
|
477
|
+
function ColorScaleCard({ id, name, hint, required, custom, state, onChange, onDelete, readOnly, role }) {
|
|
478
|
+
const label = automaticLabel(state.hex);
|
|
479
|
+
const warning = fillLabelWarning(id ?? name.toLowerCase(), name, state.hex);
|
|
480
|
+
const previewMode = useThemePreviewMode();
|
|
481
|
+
const preview = warning ? previewMode === "dark" ? warning.dark : warning.light : null;
|
|
482
|
+
const [open, setOpen] = useState(false);
|
|
483
|
+
const titleId = useId();
|
|
484
|
+
const hueWarning = role === "success" ? successHueWarning(state.hex) : role === "destructive" ? destructiveHueWarning(state.hex) : null;
|
|
485
|
+
const included = new Set(includedShadeSteps(state));
|
|
486
|
+
function setHex(hex) {
|
|
487
|
+
onChange(updateColorScale(state, { hex }));
|
|
488
|
+
}
|
|
489
|
+
function setStep(step) {
|
|
490
|
+
onChange(updateColorScale(state, { sourceStep: step }));
|
|
491
|
+
}
|
|
492
|
+
function toggleStep(step) {
|
|
493
|
+
if (step === state.sourceStep) return;
|
|
494
|
+
const next = new Set(included);
|
|
495
|
+
if (next.has(step)) next.delete(step);
|
|
496
|
+
else next.add(step);
|
|
497
|
+
onChange(setColorScaleInclude(state, SHADE_STEPS.filter((item) => next.has(item))));
|
|
498
|
+
}
|
|
499
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
500
|
+
className: "bl-scale-card",
|
|
501
|
+
"data-preview": previewMode,
|
|
502
|
+
children: [
|
|
503
|
+
/* @__PURE__ */ jsxs("header", {
|
|
504
|
+
className: "bl-scale-card__head",
|
|
505
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
|
|
506
|
+
className: "bl-scale-card__title-row",
|
|
507
|
+
children: [
|
|
508
|
+
/* @__PURE__ */ jsx("h3", {
|
|
509
|
+
className: "bl-scale-card__title",
|
|
510
|
+
children: name
|
|
511
|
+
}),
|
|
512
|
+
required ? /* @__PURE__ */ jsx("span", {
|
|
513
|
+
className: "bl-scale-card__badge",
|
|
514
|
+
children: "Required"
|
|
515
|
+
}) : null,
|
|
516
|
+
custom ? /* @__PURE__ */ jsx("span", {
|
|
517
|
+
className: "bl-scale-card__badge",
|
|
518
|
+
children: "Optional"
|
|
519
|
+
}) : null,
|
|
520
|
+
warning ? warning.meets ? /* @__PURE__ */ jsxs("span", {
|
|
521
|
+
className: "bl-scale-card__status",
|
|
522
|
+
"data-ok": "",
|
|
523
|
+
title: `Text on ${name} looks readable`,
|
|
524
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
525
|
+
"aria-hidden": "true",
|
|
526
|
+
children: "✓"
|
|
527
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
528
|
+
className: "bl-scale-card__sr",
|
|
529
|
+
children: [
|
|
530
|
+
"Text on ",
|
|
531
|
+
name,
|
|
532
|
+
" looks readable"
|
|
533
|
+
]
|
|
534
|
+
})]
|
|
535
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
536
|
+
type: "button",
|
|
537
|
+
className: "bl-scale-card__badge bl-scale-card__badge--attention",
|
|
538
|
+
"aria-haspopup": "dialog",
|
|
539
|
+
onClick: () => setOpen(true),
|
|
540
|
+
children: "Needs attention"
|
|
541
|
+
}) : null
|
|
542
|
+
]
|
|
543
|
+
}), hint ? /* @__PURE__ */ jsx("p", {
|
|
544
|
+
className: "bl-scale-card__hint",
|
|
545
|
+
children: hint
|
|
546
|
+
}) : null] }), /* @__PURE__ */ jsxs("div", {
|
|
547
|
+
className: "bl-scale-card__head-actions",
|
|
548
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
549
|
+
className: "bl-scale-card__mark",
|
|
550
|
+
style: { background: state.hex },
|
|
551
|
+
"aria-hidden": "true"
|
|
552
|
+
}), onDelete ? /* @__PURE__ */ jsx("button", {
|
|
553
|
+
type: "button",
|
|
554
|
+
className: "bl-scale-card__delete",
|
|
555
|
+
disabled: readOnly,
|
|
556
|
+
onClick: onDelete,
|
|
557
|
+
children: "Delete"
|
|
558
|
+
}) : null]
|
|
559
|
+
})]
|
|
560
|
+
}),
|
|
561
|
+
/* @__PURE__ */ jsxs("div", {
|
|
562
|
+
className: "bl-scale-card__row",
|
|
563
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
564
|
+
className: "bl-scale-card__field",
|
|
565
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Brand hex" }), /* @__PURE__ */ jsxs("span", {
|
|
566
|
+
className: "bl-scale-card__hex-wrap",
|
|
567
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
568
|
+
type: "color",
|
|
569
|
+
"aria-label": `${name} picker`,
|
|
570
|
+
disabled: readOnly,
|
|
571
|
+
value: state.hex,
|
|
572
|
+
onChange: (event) => setHex(event.target.value)
|
|
573
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
574
|
+
type: "text",
|
|
575
|
+
spellCheck: false,
|
|
576
|
+
autoComplete: "off",
|
|
577
|
+
disabled: readOnly,
|
|
578
|
+
value: state.hex,
|
|
579
|
+
onChange: (event) => {
|
|
580
|
+
const next = event.target.value.trim();
|
|
581
|
+
if (next) setHex(next);
|
|
582
|
+
}
|
|
583
|
+
})]
|
|
584
|
+
})]
|
|
585
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
586
|
+
className: "bl-scale-card__field",
|
|
587
|
+
children: [/* @__PURE__ */ jsx("span", { children: "This color is" }), /* @__PURE__ */ jsx("select", {
|
|
588
|
+
disabled: readOnly,
|
|
589
|
+
value: String(state.sourceStep),
|
|
590
|
+
onChange: (event) => setStep(Number(event.target.value)),
|
|
591
|
+
children: SHADE_STEPS.map((step) => /* @__PURE__ */ jsx("option", {
|
|
592
|
+
value: step,
|
|
593
|
+
children: step
|
|
594
|
+
}, step))
|
|
595
|
+
})]
|
|
596
|
+
})]
|
|
597
|
+
}),
|
|
598
|
+
/* @__PURE__ */ jsxs("div", {
|
|
599
|
+
className: "bl-scale-card__label-sample",
|
|
600
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
601
|
+
className: "bl-scale-card__swatch",
|
|
602
|
+
style: {
|
|
603
|
+
background: preview ? hslCss(preview.fill) : state.hex,
|
|
604
|
+
color: preview ? hslCss(preview.label) : label ? hslCss(label) : void 0
|
|
605
|
+
},
|
|
606
|
+
children: "Aa"
|
|
607
|
+
}), /* @__PURE__ */ jsx("p", { children: previewMode === "dark" ? "Previewing this fill in Dark. A dark-enough hex lightens; otherwise it stays the typed color." : "The site chooses light or dark type on this fill. Darken or lighten the hex and regenerate to flip it. There is no ink picker." })]
|
|
608
|
+
}),
|
|
609
|
+
hueWarning ? /* @__PURE__ */ jsx("p", {
|
|
610
|
+
className: "bl-scale-card__warn",
|
|
611
|
+
children: hueWarning
|
|
612
|
+
}) : null,
|
|
613
|
+
state.stale ? /* @__PURE__ */ jsx("p", {
|
|
614
|
+
className: "bl-scale-card__stale",
|
|
615
|
+
children: "This scale is out of date. Regenerate to rebuild 50–950 around the hex."
|
|
616
|
+
}) : null,
|
|
617
|
+
/* @__PURE__ */ jsx("button", {
|
|
618
|
+
type: "button",
|
|
619
|
+
className: "bl-scale-card__regen",
|
|
620
|
+
disabled: readOnly || !state.stale,
|
|
621
|
+
onClick: () => onChange(regenerateColorScale(state)),
|
|
622
|
+
children: "Regenerate scale"
|
|
623
|
+
}),
|
|
624
|
+
/* @__PURE__ */ jsxs("div", {
|
|
625
|
+
className: "bl-scale-card__scale-head",
|
|
626
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Generated scale" }), /* @__PURE__ */ jsxs("span", { children: [
|
|
627
|
+
/* @__PURE__ */ jsx("button", {
|
|
628
|
+
type: "button",
|
|
629
|
+
disabled: readOnly,
|
|
630
|
+
onClick: () => onChange(setColorScaleInclude(state, "all")),
|
|
631
|
+
children: "Include all"
|
|
632
|
+
}),
|
|
633
|
+
" · ",
|
|
634
|
+
/* @__PURE__ */ jsx("button", {
|
|
635
|
+
type: "button",
|
|
636
|
+
disabled: readOnly,
|
|
637
|
+
onClick: () => onChange(setColorScaleInclude(state, "source")),
|
|
638
|
+
children: "Source only"
|
|
639
|
+
})
|
|
640
|
+
] })]
|
|
641
|
+
}),
|
|
642
|
+
/* @__PURE__ */ jsx("div", {
|
|
643
|
+
className: "bl-scale-card__steps",
|
|
644
|
+
role: "group",
|
|
645
|
+
"aria-label": `${name} scale steps`,
|
|
646
|
+
children: SHADE_STEPS.map((step) => {
|
|
647
|
+
const source = step === state.sourceStep;
|
|
648
|
+
const on = included.has(step);
|
|
649
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
650
|
+
type: "button",
|
|
651
|
+
className: "bl-scale-card__step",
|
|
652
|
+
"data-source": source ? "" : void 0,
|
|
653
|
+
"data-excluded": on ? void 0 : "",
|
|
654
|
+
disabled: readOnly || source,
|
|
655
|
+
title: source ? `Source step ${step} cannot be excluded` : on ? `Exclude ${step} from page editors` : `Include ${step} for page editors`,
|
|
656
|
+
onClick: () => toggleStep(step),
|
|
657
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
658
|
+
className: "bl-scale-card__chip",
|
|
659
|
+
style: { background: hslCss(state.scale[step]) },
|
|
660
|
+
children: source ? null : /* @__PURE__ */ jsx("span", {
|
|
661
|
+
className: "bl-scale-card__intent",
|
|
662
|
+
"data-intent": on ? "exclude" : "include",
|
|
663
|
+
"aria-hidden": "true",
|
|
664
|
+
children: on ? /* @__PURE__ */ jsxs("svg", {
|
|
665
|
+
viewBox: "0 0 24 24",
|
|
666
|
+
width: "16",
|
|
667
|
+
height: "16",
|
|
668
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
669
|
+
cx: "12",
|
|
670
|
+
cy: "12",
|
|
671
|
+
r: "8",
|
|
672
|
+
fill: "none",
|
|
673
|
+
stroke: "currentColor",
|
|
674
|
+
strokeWidth: "2.4"
|
|
675
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
676
|
+
d: "M7.2 16.8 16.8 7.2",
|
|
677
|
+
stroke: "currentColor",
|
|
678
|
+
strokeWidth: "2.4"
|
|
679
|
+
})]
|
|
680
|
+
}) : /* @__PURE__ */ jsx("svg", {
|
|
681
|
+
viewBox: "0 0 24 24",
|
|
682
|
+
width: "16",
|
|
683
|
+
height: "16",
|
|
684
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
685
|
+
d: "M12 6v12M6 12h12",
|
|
686
|
+
fill: "none",
|
|
687
|
+
stroke: "currentColor",
|
|
688
|
+
strokeWidth: "2.6"
|
|
689
|
+
})
|
|
690
|
+
})
|
|
691
|
+
})
|
|
692
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
693
|
+
className: "bl-scale-card__step-label",
|
|
694
|
+
children: step
|
|
695
|
+
})]
|
|
696
|
+
}, step);
|
|
697
|
+
})
|
|
698
|
+
}),
|
|
699
|
+
open && warning ? createPortal(/* @__PURE__ */ jsx("div", {
|
|
700
|
+
className: "bl-scale-card__modal",
|
|
701
|
+
role: "dialog",
|
|
702
|
+
"aria-modal": "true",
|
|
703
|
+
"aria-labelledby": titleId,
|
|
704
|
+
onClick: (event) => {
|
|
705
|
+
if (event.target === event.currentTarget) setOpen(false);
|
|
706
|
+
},
|
|
707
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
708
|
+
className: "bl-scale-card__panel",
|
|
709
|
+
children: [
|
|
710
|
+
/* @__PURE__ */ jsxs("div", {
|
|
711
|
+
className: "bl-scale-card__panel-head",
|
|
712
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
713
|
+
id: titleId,
|
|
714
|
+
children: readabilityHeadline(warning)
|
|
715
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
716
|
+
type: "button",
|
|
717
|
+
onClick: () => setOpen(false),
|
|
718
|
+
"aria-label": "Close",
|
|
719
|
+
children: "×"
|
|
720
|
+
})]
|
|
721
|
+
}),
|
|
722
|
+
/* @__PURE__ */ jsx(ReadabilityDetail, { item: warning }),
|
|
723
|
+
/* @__PURE__ */ jsx("button", {
|
|
724
|
+
type: "button",
|
|
725
|
+
className: "bl-scale-card__panel-done",
|
|
726
|
+
onClick: () => setOpen(false),
|
|
727
|
+
children: "Keep editing this color"
|
|
728
|
+
})
|
|
729
|
+
]
|
|
730
|
+
})
|
|
731
|
+
}), document.body) : null
|
|
732
|
+
]
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
const COLOR_SCALE_CARD_CSS = `
|
|
736
|
+
.bl-scale-card{padding:calc(var(--base,16px) * .8);border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:var(--style-radius-m,12px);background:var(--theme-elevation-0,#fff)}
|
|
737
|
+
.bl-scale-card__head{display:flex;justify-content:space-between;gap:1rem;align-items:flex-start}
|
|
738
|
+
.bl-scale-card__title-row{position:relative;display:flex;align-items:center;flex-wrap:wrap;gap:.5rem}
|
|
739
|
+
.bl-scale-card__title{margin:0;font-size:1rem}
|
|
740
|
+
.bl-scale-card__badge{font:inherit;font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:.15rem .45rem;border-radius:999px;border:0;background:var(--theme-elevation-100,#f1f5f9);color:var(--theme-elevation-500,#64748b)}
|
|
741
|
+
.bl-scale-card__badge--attention{background:#fff1f2;color:#be123c;cursor:pointer}
|
|
742
|
+
.bl-scale-card__status{display:inline-grid;place-items:center;width:1.4rem;height:1.4rem;border-radius:999px;border:0;font:inherit;font-weight:700;line-height:1}
|
|
743
|
+
.bl-scale-card__status[data-ok]{background:#ecfdf5;color:#047857}
|
|
744
|
+
.bl-scale-card__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
745
|
+
.bl-scale-card__hint{margin:.35rem 0 0;color:var(--theme-elevation-500,#64748b);font-size:12px}
|
|
746
|
+
.bl-scale-card__head-actions{display:flex;align-items:center;gap:.5rem}
|
|
747
|
+
.bl-scale-card__mark{display:block;width:2.5rem;height:2.5rem;border-radius:8px;border:1px solid rgba(15,23,42,.12);flex:none}
|
|
748
|
+
.bl-scale-card__delete{border:0;background:none;color:#be123c;font:inherit;font-size:13px;font-weight:600;cursor:pointer}
|
|
749
|
+
.bl-scale-card__row{display:grid;grid-template-columns:1fr 8rem;gap:.75rem;margin-top:1rem}
|
|
750
|
+
.bl-scale-card__field{display:flex;flex-direction:column;gap:.35rem;font-size:12px;font-weight:600;color:var(--theme-elevation-600,#475569)}
|
|
751
|
+
.bl-scale-card__hex-wrap{display:flex;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;overflow:hidden;background:var(--theme-input-bg,#fff)}
|
|
752
|
+
.bl-scale-card__hex-wrap input[type=color]{width:2.75rem;height:2.25rem;padding:0;border:0;border-right:1px solid var(--theme-elevation-150,#e2e8f0);background:transparent;cursor:pointer}
|
|
753
|
+
.bl-scale-card__hex-wrap input[type=text]{flex:1;min-width:0;border:0;padding:.4rem .7rem;font-family:var(--font-mono,ui-monospace,monospace);font-size:13px;background:transparent;color:inherit}
|
|
754
|
+
.bl-scale-card__field select{min-height:2.25rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;padding:.35rem .5rem;background:var(--theme-input-bg,#fff);color:inherit}
|
|
755
|
+
.bl-scale-card__label-sample{display:flex;gap:.75rem;align-items:center;margin-top:1rem}
|
|
756
|
+
.bl-scale-card__swatch{flex:none;display:inline-flex;align-items:center;justify-content:center;width:3rem;height:2.25rem;border-radius:8px;border:1px solid var(--theme-elevation-150,#e2e8f0);font-weight:700}
|
|
757
|
+
.bl-scale-card__label-sample p{margin:0;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
758
|
+
.bl-scale-card__warn,.bl-scale-card__stale{margin:.75rem 0 0;padding:.65rem .75rem;border-radius:8px;font-size:12px}
|
|
759
|
+
.bl-scale-card__warn{border:1px solid #a7f3d0;background:#ecfdf5;color:#065f46}
|
|
760
|
+
.bl-scale-card__stale{border:1px solid #fde68a;background:#fffbeb;color:#92400e}
|
|
761
|
+
.bl-scale-card__regen{width:100%;margin-top:.85rem;min-height:2.25rem;border:0;border-radius:8px;background:#0f172a;color:#fff;font:inherit;font-weight:600;cursor:pointer}
|
|
762
|
+
.bl-scale-card__regen:disabled{background:#1e293b;color:#f8fafc;opacity:1;cursor:not-allowed}
|
|
763
|
+
.bl-scale-card__scale-head{display:flex;justify-content:space-between;margin:1rem 0 .4rem;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
764
|
+
.bl-scale-card__scale-head button{border:0;background:none;padding:0;font:inherit;color:inherit;text-decoration:underline;cursor:pointer}
|
|
765
|
+
.bl-scale-card__steps{display:grid;grid-template-columns:repeat(11,minmax(0,1fr));gap:.45rem 3px}
|
|
766
|
+
.bl-scale-card__step{display:flex;flex-direction:column;align-items:center;gap:.3rem;border:0;padding:0;background:transparent;cursor:pointer}
|
|
767
|
+
.bl-scale-card__chip{position:relative;display:block;width:100%;height:2.15rem;border-radius:6px;overflow:hidden}
|
|
768
|
+
.bl-scale-card__intent{position:absolute;top:3px;right:3px;width:1.15rem;height:1.15rem;border-radius:999px;display:grid;place-items:center;opacity:0;color:#be123c;background:#fff;box-shadow:0 1px 3px rgba(15,23,42,.28);transition:opacity .15s ease,transform .15s ease;pointer-events:none}
|
|
769
|
+
.bl-scale-card__intent[data-intent=include]{color:#047857}
|
|
770
|
+
.bl-scale-card__step:hover:not(:disabled) .bl-scale-card__chip{transform:translateY(-1px)}
|
|
771
|
+
.bl-scale-card__step:hover:not(:disabled) .bl-scale-card__intent{opacity:1}
|
|
772
|
+
.bl-scale-card__step-label{font-size:10px;line-height:1;color:var(--theme-elevation-600,#475569)}
|
|
773
|
+
.bl-scale-card__step[data-source] .bl-scale-card__chip{box-shadow:0 0 0 2px var(--theme-elevation-800,#0f172a)}
|
|
774
|
+
.bl-scale-card__step[data-excluded]{opacity:.55}
|
|
775
|
+
.bl-scale-card__step:disabled{cursor:default}
|
|
776
|
+
.bl-scale-card__modal{position:fixed;inset:0;z-index:100000;display:grid;place-items:center;padding:1rem;background:rgba(15,23,42,.45)}
|
|
777
|
+
.bl-scale-card__panel{width:min(32rem,100%);padding:1.15rem 1.2rem;border-radius:16px;background:#fff;color:#0f172a}
|
|
778
|
+
.bl-scale-card__panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem}
|
|
779
|
+
.bl-scale-card__panel-head h4{margin:0;font-size:1rem}
|
|
780
|
+
.bl-scale-card__panel-head button{border:0;background:none;font:inherit;font-size:1.25rem;line-height:1;color:#64748b;cursor:pointer}
|
|
781
|
+
.bl-scale-card__panel-done{margin-top:1rem;min-height:2.1rem;padding:.35rem .85rem;border:0;border-radius:8px;background:#0f172a;color:#fff;font:inherit;font-weight:600;cursor:pointer}
|
|
782
|
+
` + READABILITY_LIST_CSS;
|
|
783
|
+
//#endregion
|
|
784
|
+
//#region src/admin/color-scale-field.tsx
|
|
785
|
+
const SYSTEM_ROLES = new Set(Object.keys(SYSTEM_COLOR_COPY));
|
|
786
|
+
/**
|
|
787
|
+
* One system (or library row) color: hex, source step, regenerate, stale,
|
|
788
|
+
* include/exclude, automatic label. Writes the group's stored fields.
|
|
789
|
+
*/
|
|
790
|
+
const ColorScaleField = ({ field, path, readOnly }) => {
|
|
791
|
+
const hex = useField({ path: `${path}.hex` });
|
|
792
|
+
const sourceStep = useField({ path: `${path}.sourceStep` });
|
|
793
|
+
const scale = useField({ path: `${path}.scale` });
|
|
794
|
+
const stale = useField({ path: `${path}.stale` });
|
|
795
|
+
const include = useField({ path: `${path}.include` });
|
|
796
|
+
const includedSteps = useField({ path: `${path}.includedSteps` });
|
|
797
|
+
const key = path.split(".").pop() ?? "";
|
|
798
|
+
const copy = SYSTEM_ROLES.has(key) ? SYSTEM_COLOR_COPY[key] : void 0;
|
|
799
|
+
const state = stateFromColorDoc({
|
|
800
|
+
hex: hex.value,
|
|
801
|
+
sourceStep: sourceStep.value,
|
|
802
|
+
scale: scale.value,
|
|
803
|
+
stale: stale.value,
|
|
804
|
+
include: include.value === "source" || include.value === "custom" || include.value === "all" ? include.value : "all",
|
|
805
|
+
includedSteps: includedSteps.value
|
|
806
|
+
});
|
|
807
|
+
function write(next) {
|
|
808
|
+
const doc = colorDocFromState(next);
|
|
809
|
+
hex.setValue(doc.hex);
|
|
810
|
+
sourceStep.setValue(String(doc.sourceStep));
|
|
811
|
+
scale.setValue(doc.scale);
|
|
812
|
+
stale.setValue(doc.stale);
|
|
813
|
+
include.setValue(doc.include);
|
|
814
|
+
includedSteps.setValue(doc.includedSteps ?? (doc.include === "source" ? [doc.sourceStep] : [...SHADE_STEPS]));
|
|
815
|
+
}
|
|
816
|
+
const label = copy?.label ?? (typeof field.label === "string" ? field.label : key);
|
|
817
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
818
|
+
className: "field-type bl-scale-field",
|
|
819
|
+
children: [/* @__PURE__ */ jsx("style", {
|
|
820
|
+
href: "bl-scale-card",
|
|
821
|
+
precedence: "default",
|
|
822
|
+
children: COLOR_SCALE_CARD_CSS
|
|
823
|
+
}), /* @__PURE__ */ jsx(ColorScaleCard, {
|
|
824
|
+
id: key,
|
|
825
|
+
name: label,
|
|
826
|
+
hint: copy?.hint,
|
|
827
|
+
required: Boolean(copy) || Boolean("required" in field && field.required),
|
|
828
|
+
state: isThemeHex(state.hex) ? state : {
|
|
829
|
+
...state,
|
|
830
|
+
hex: "#000000"
|
|
831
|
+
},
|
|
832
|
+
onChange: write,
|
|
833
|
+
readOnly,
|
|
834
|
+
role: key === "success" || key === "destructive" ? key : null
|
|
835
|
+
})]
|
|
836
|
+
});
|
|
837
|
+
};
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/theme/types.ts
|
|
840
|
+
const SYSTEM_COLOR_KEYS = [
|
|
841
|
+
"primary",
|
|
842
|
+
"secondary",
|
|
843
|
+
"accent",
|
|
844
|
+
"highlight",
|
|
845
|
+
"success",
|
|
846
|
+
"destructive"
|
|
847
|
+
];
|
|
848
|
+
function themeColorKeys(doc) {
|
|
849
|
+
const custom = (Array.isArray(doc?.colors?.library) ? doc.colors.library : []).map((row) => row.key?.trim()).filter((key) => Boolean(key));
|
|
850
|
+
return [...SYSTEM_COLOR_KEYS, ...custom];
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Remove a custom color. Unused: omit `replacement` and the row is gone.
|
|
854
|
+
* In use: pass another system or custom key; the caller rewrites page
|
|
855
|
+
* tokens with `rewriteColorToken`. After this, no library row still
|
|
856
|
+
* has `key`.
|
|
857
|
+
*/
|
|
858
|
+
function deleteLibraryColor(doc, key, replacement) {
|
|
859
|
+
const library = [...Array.isArray(doc.colors?.library) ? doc.colors.library : []];
|
|
860
|
+
const index = library.findIndex((row) => row.key === key);
|
|
861
|
+
if (index === -1) throw new Error(`No custom color named ${JSON.stringify(key)}`);
|
|
862
|
+
library.splice(index, 1);
|
|
863
|
+
const next = {
|
|
864
|
+
...doc,
|
|
865
|
+
colors: {
|
|
866
|
+
...doc.colors,
|
|
867
|
+
library
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
if (replacement !== void 0) {
|
|
871
|
+
if (replacement === key) throw new Error("Replacement must be a different color");
|
|
872
|
+
if (!themeColorKeys(next).includes(replacement)) throw new Error(`Replacement ${JSON.stringify(replacement)} is not a system or remaining custom color`);
|
|
873
|
+
}
|
|
874
|
+
return next;
|
|
875
|
+
}
|
|
876
|
+
//#endregion
|
|
877
|
+
//#region src/admin/library-field.tsx
|
|
878
|
+
function slugify(value) {
|
|
879
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Custom colors: the same scale controls as system colors, plus add and
|
|
883
|
+
* in-use delete (one replacement scale; unused rows delete immediately).
|
|
884
|
+
*/
|
|
885
|
+
const LibraryField = ({ path, readOnly }) => {
|
|
886
|
+
const { value, setValue } = useField({ path });
|
|
887
|
+
const rows = Array.isArray(value) ? value : [];
|
|
888
|
+
const [adding, setAdding] = useState(false);
|
|
889
|
+
const [name, setName] = useState("");
|
|
890
|
+
const [hex, setHex] = useState("#E07A5F");
|
|
891
|
+
const [pendingDelete, setPendingDelete] = useState(null);
|
|
892
|
+
const [replacement, setReplacement] = useState("");
|
|
893
|
+
function write(next) {
|
|
894
|
+
setValue(next);
|
|
895
|
+
}
|
|
896
|
+
function add() {
|
|
897
|
+
const key = slugify(name);
|
|
898
|
+
if (!key || rows.some((row) => row.key === key)) return;
|
|
899
|
+
write([...rows, {
|
|
900
|
+
key,
|
|
901
|
+
label: name.trim(),
|
|
902
|
+
...colorDocFromState(createColorScale(hex, 500))
|
|
903
|
+
}]);
|
|
904
|
+
setName("");
|
|
905
|
+
setAdding(false);
|
|
906
|
+
}
|
|
907
|
+
function remove(row, replaceWith) {
|
|
908
|
+
write(deleteLibraryColor({ colors: { library: rows } }, row.key ?? "", replaceWith).colors?.library ?? []);
|
|
909
|
+
setPendingDelete(null);
|
|
910
|
+
setReplacement("");
|
|
911
|
+
}
|
|
912
|
+
const replacements = [...SYSTEM_COLOR_KEYS.map((key) => ({
|
|
913
|
+
value: key,
|
|
914
|
+
label: key[0].toUpperCase() + key.slice(1)
|
|
915
|
+
})), ...rows.filter((row) => row.key && row.key !== pendingDelete?.key).map((row) => ({
|
|
916
|
+
value: row.key ?? "",
|
|
917
|
+
label: row.label ?? row.key ?? ""
|
|
918
|
+
}))];
|
|
919
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
920
|
+
className: "field-type bl-library",
|
|
921
|
+
children: [
|
|
922
|
+
/* @__PURE__ */ jsx("style", {
|
|
923
|
+
href: "bl-scale-card",
|
|
924
|
+
precedence: "default",
|
|
925
|
+
children: COLOR_SCALE_CARD_CSS
|
|
926
|
+
}),
|
|
927
|
+
/* @__PURE__ */ jsx("style", {
|
|
928
|
+
href: "bl-library",
|
|
929
|
+
precedence: "default",
|
|
930
|
+
children: CSS$3
|
|
931
|
+
}),
|
|
932
|
+
/* @__PURE__ */ jsxs("div", {
|
|
933
|
+
className: "bl-library__head",
|
|
934
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", { children: "Custom colors" }), /* @__PURE__ */ jsx("p", { children: "Named scales that sit with the house colors. Page editors pick them as keys (`coral-400`)." })] }), /* @__PURE__ */ jsx("button", {
|
|
935
|
+
type: "button",
|
|
936
|
+
disabled: readOnly,
|
|
937
|
+
onClick: () => setAdding(true),
|
|
938
|
+
children: "Add color"
|
|
939
|
+
})]
|
|
940
|
+
}),
|
|
941
|
+
adding ? /* @__PURE__ */ jsxs("div", {
|
|
942
|
+
className: "bl-library__add",
|
|
943
|
+
children: [
|
|
944
|
+
/* @__PURE__ */ jsxs("label", { children: ["Name", /* @__PURE__ */ jsx("input", {
|
|
945
|
+
value: name,
|
|
946
|
+
onChange: (event) => setName(event.target.value)
|
|
947
|
+
})] }),
|
|
948
|
+
/* @__PURE__ */ jsxs("label", { children: ["Hex", /* @__PURE__ */ jsx("input", {
|
|
949
|
+
value: hex,
|
|
950
|
+
onChange: (event) => setHex(event.target.value)
|
|
951
|
+
})] }),
|
|
952
|
+
/* @__PURE__ */ jsx("button", {
|
|
953
|
+
type: "button",
|
|
954
|
+
onClick: () => setAdding(false),
|
|
955
|
+
children: "Cancel"
|
|
956
|
+
}),
|
|
957
|
+
/* @__PURE__ */ jsx("button", {
|
|
958
|
+
type: "button",
|
|
959
|
+
disabled: !slugify(name),
|
|
960
|
+
onClick: add,
|
|
961
|
+
children: "Create color scale"
|
|
962
|
+
})
|
|
963
|
+
]
|
|
964
|
+
}) : null,
|
|
965
|
+
/* @__PURE__ */ jsx("div", {
|
|
966
|
+
className: "bl-library__list",
|
|
967
|
+
children: rows.map((row, index) => /* @__PURE__ */ jsx(ColorScaleCard, {
|
|
968
|
+
id: row.key ?? `custom-${index}`,
|
|
969
|
+
name: row.label ?? row.key ?? "Color",
|
|
970
|
+
hint: row.key ? `Key: ${row.key}` : void 0,
|
|
971
|
+
custom: true,
|
|
972
|
+
state: stateFromColorDoc(row),
|
|
973
|
+
onChange: (next) => {
|
|
974
|
+
const copy = [...rows];
|
|
975
|
+
copy[index] = {
|
|
976
|
+
...row,
|
|
977
|
+
...colorDocFromState(next)
|
|
978
|
+
};
|
|
979
|
+
write(copy);
|
|
980
|
+
},
|
|
981
|
+
onDelete: readOnly ? void 0 : () => setPendingDelete(row),
|
|
982
|
+
readOnly
|
|
983
|
+
}, row.key ?? index))
|
|
984
|
+
}),
|
|
985
|
+
pendingDelete ? /* @__PURE__ */ jsx("div", {
|
|
986
|
+
className: "bl-library__modal",
|
|
987
|
+
role: "dialog",
|
|
988
|
+
"aria-modal": "true",
|
|
989
|
+
"aria-labelledby": "bl-library-delete-title",
|
|
990
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
991
|
+
className: "bl-library__panel",
|
|
992
|
+
children: [
|
|
993
|
+
/* @__PURE__ */ jsxs("h2", {
|
|
994
|
+
id: "bl-library-delete-title",
|
|
995
|
+
children: ["Delete ", pendingDelete.label ?? pendingDelete.key]
|
|
996
|
+
}),
|
|
997
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
998
|
+
"Unused custom colors delete immediately. If this scale is in use, pick one replacement — every",
|
|
999
|
+
" ",
|
|
1000
|
+
/* @__PURE__ */ jsxs("code", { children: [pendingDelete.key, "-400"] }),
|
|
1001
|
+
" becomes ",
|
|
1002
|
+
/* @__PURE__ */ jsxs("code", { children: [replacement || "…", "-400"] }),
|
|
1003
|
+
"."
|
|
1004
|
+
] }),
|
|
1005
|
+
/* @__PURE__ */ jsxs("label", { children: ["Replacement scale", /* @__PURE__ */ jsxs("select", {
|
|
1006
|
+
value: replacement,
|
|
1007
|
+
onChange: (event) => setReplacement(event.target.value),
|
|
1008
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
1009
|
+
value: "",
|
|
1010
|
+
children: "Choose a scale…"
|
|
1011
|
+
}), replacements.map((option) => /* @__PURE__ */ jsx("option", {
|
|
1012
|
+
value: option.value,
|
|
1013
|
+
children: option.label
|
|
1014
|
+
}, option.value))]
|
|
1015
|
+
})] }),
|
|
1016
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1017
|
+
className: "bl-library__actions",
|
|
1018
|
+
children: [
|
|
1019
|
+
/* @__PURE__ */ jsx("button", {
|
|
1020
|
+
type: "button",
|
|
1021
|
+
onClick: () => setPendingDelete(null),
|
|
1022
|
+
children: "Cancel"
|
|
1023
|
+
}),
|
|
1024
|
+
/* @__PURE__ */ jsx("button", {
|
|
1025
|
+
type: "button",
|
|
1026
|
+
onClick: () => remove(pendingDelete),
|
|
1027
|
+
children: "Delete immediately"
|
|
1028
|
+
}),
|
|
1029
|
+
/* @__PURE__ */ jsx("button", {
|
|
1030
|
+
type: "button",
|
|
1031
|
+
disabled: !replacement,
|
|
1032
|
+
onClick: () => remove(pendingDelete, replacement),
|
|
1033
|
+
children: "Replace & delete"
|
|
1034
|
+
})
|
|
1035
|
+
]
|
|
1036
|
+
})
|
|
1037
|
+
]
|
|
1038
|
+
})
|
|
1039
|
+
}) : null
|
|
1040
|
+
]
|
|
1041
|
+
});
|
|
1042
|
+
};
|
|
1043
|
+
const CSS$3 = `
|
|
1044
|
+
.bl-library__head{display:flex;justify-content:space-between;gap:1rem;align-items:flex-start;margin-bottom:.85rem}
|
|
1045
|
+
.bl-library__head h2{margin:0;font-size:1.1rem}
|
|
1046
|
+
.bl-library__head p{margin:.35rem 0 0;font-size:13px;color:var(--theme-elevation-500,#64748b)}
|
|
1047
|
+
.bl-library__head button,.bl-library__add button,.bl-library__actions button{min-height:2.1rem;padding:.35rem .75rem;border-radius:8px;border:1px solid var(--theme-elevation-150,#e2e8f0);background:var(--theme-elevation-0,#fff);font:inherit;cursor:pointer}
|
|
1048
|
+
.bl-library__list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}
|
|
1049
|
+
@media (max-width:860px){.bl-library__list{grid-template-columns:1fr}}
|
|
1050
|
+
.bl-library__add{display:flex;flex-wrap:wrap;gap:.6rem;align-items:end;margin-bottom:.85rem;padding:.85rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px}
|
|
1051
|
+
.bl-library__add label{display:flex;flex-direction:column;gap:.3rem;font-size:12px;font-weight:600}
|
|
1052
|
+
.bl-library__add input{min-height:2.1rem;padding:.3rem .55rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px}
|
|
1053
|
+
.bl-library__modal{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:1rem;background:rgba(15,23,42,.45)}
|
|
1054
|
+
.bl-library__panel{width:min(28rem,100%);padding:1.2rem;border-radius:16px;background:#fff;color:#0f172a}
|
|
1055
|
+
.bl-library__panel h2{margin:0}
|
|
1056
|
+
.bl-library__panel p{margin:.5rem 0 0;color:#475569;font-size:14px}
|
|
1057
|
+
.bl-library__panel label{display:flex;flex-direction:column;gap:.4rem;margin-top:1rem;font-size:13px;font-weight:600}
|
|
1058
|
+
.bl-library__panel select{min-height:2.25rem;border:1px solid #cbd5e1;border-radius:8px;padding:.35rem .5rem}
|
|
1059
|
+
.bl-library__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;margin-top:1rem}
|
|
1060
|
+
`;
|
|
1061
|
+
//#endregion
|
|
1062
|
+
//#region src/admin/typography-pairing.tsx
|
|
1063
|
+
function FontRoleCard({ title, hint, required, family, preview, children }) {
|
|
1064
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
1065
|
+
className: "bl-font-role",
|
|
1066
|
+
children: [
|
|
1067
|
+
/* @__PURE__ */ jsxs("header", {
|
|
1068
|
+
className: "bl-font-role__head",
|
|
1069
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", { children: title }), /* @__PURE__ */ jsx("p", { children: hint })] }), required ? /* @__PURE__ */ jsx("span", {
|
|
1070
|
+
className: "bl-font-role__badge",
|
|
1071
|
+
children: "Required"
|
|
1072
|
+
}) : null]
|
|
1073
|
+
}),
|
|
1074
|
+
children,
|
|
1075
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1076
|
+
className: "bl-font-role__preview",
|
|
1077
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Preview" }), preview === "heading" ? /* @__PURE__ */ jsx("p", {
|
|
1078
|
+
className: "bl-font-role__heading",
|
|
1079
|
+
style: { fontFamily: `"${family}"` },
|
|
1080
|
+
children: "Advancing the work with clarity."
|
|
1081
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
1082
|
+
className: "bl-font-role__body",
|
|
1083
|
+
style: { fontFamily: `"${family}"` },
|
|
1084
|
+
children: "Clear, comfortable text for longer reading across the site."
|
|
1085
|
+
})]
|
|
1086
|
+
})
|
|
1087
|
+
]
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function TypographyPairing({ headingFamily, bodyFamily }) {
|
|
1091
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1092
|
+
className: "bl-pairing",
|
|
1093
|
+
"aria-label": "Font pairing sample",
|
|
1094
|
+
children: [/* @__PURE__ */ jsxs("header", { children: [/* @__PURE__ */ jsx("h2", { children: "Pairing sample" }), /* @__PURE__ */ jsx("p", { children: "Heading and body from the approved catalogue. Type scale stays with the library." })] }), /* @__PURE__ */ jsxs("div", {
|
|
1095
|
+
className: "bl-pairing__page",
|
|
1096
|
+
children: [
|
|
1097
|
+
/* @__PURE__ */ jsx("p", {
|
|
1098
|
+
className: "bl-pairing__eyebrow",
|
|
1099
|
+
style: { fontFamily: `"${bodyFamily}"` },
|
|
1100
|
+
children: "For visitors"
|
|
1101
|
+
}),
|
|
1102
|
+
/* @__PURE__ */ jsx("p", {
|
|
1103
|
+
className: "bl-pairing__title",
|
|
1104
|
+
style: { fontFamily: `"${headingFamily}"` },
|
|
1105
|
+
children: "Stay in motion."
|
|
1106
|
+
}),
|
|
1107
|
+
/* @__PURE__ */ jsx("p", {
|
|
1108
|
+
className: "bl-pairing__copy",
|
|
1109
|
+
style: { fontFamily: `"${bodyFamily}"` },
|
|
1110
|
+
children: "Heading and body come from the catalogue. This is the pairing, not a full site preview."
|
|
1111
|
+
}),
|
|
1112
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1113
|
+
className: "bl-pairing__actions",
|
|
1114
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1115
|
+
className: "bl-pairing__btn",
|
|
1116
|
+
style: { fontFamily: `"${bodyFamily}"` },
|
|
1117
|
+
children: "Primary action"
|
|
1118
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1119
|
+
className: "bl-pairing__btn bl-pairing__btn--ghost",
|
|
1120
|
+
style: { fontFamily: `"${bodyFamily}"` },
|
|
1121
|
+
children: "Learn more"
|
|
1122
|
+
})]
|
|
1123
|
+
})
|
|
1124
|
+
]
|
|
1125
|
+
})]
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
const TYPOGRAPHY_PAIRING_CSS = `
|
|
1129
|
+
.bl-font-role{padding:1.1rem 1.15rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-0,#fff)}
|
|
1130
|
+
.bl-font-role__head{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem}
|
|
1131
|
+
.bl-font-role__head h3{margin:0;font-size:1rem}
|
|
1132
|
+
.bl-font-role__head p{margin:.3rem 0 0;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
1133
|
+
.bl-font-role__badge{font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:.15rem .45rem;border-radius:999px;background:var(--theme-elevation-100,#f1f5f9);color:var(--theme-elevation-500,#64748b)}
|
|
1134
|
+
.bl-font-role__preview{margin-top:1rem;padding:.85rem .9rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-50,#f8fafc)}
|
|
1135
|
+
.bl-font-role__preview > span{display:block;margin-bottom:.4rem;font-size:11px;color:var(--theme-elevation-500,#64748b)}
|
|
1136
|
+
.bl-font-role__heading{margin:0;font-size:1.5rem;line-height:1.2;font-weight:600}
|
|
1137
|
+
.bl-font-role__body{margin:0;font-size:1rem;line-height:1.6;color:var(--theme-elevation-600,#475569)}
|
|
1138
|
+
.bl-pairing{padding:0;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-0,#fff);overflow:hidden}
|
|
1139
|
+
.bl-pairing > header{padding:1rem 1.15rem;border-bottom:1px solid var(--theme-elevation-150,#e2e8f0)}
|
|
1140
|
+
.bl-pairing > header h2{margin:0;font-size:1rem}
|
|
1141
|
+
.bl-pairing > header p{margin:.3rem 0 0;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
1142
|
+
.bl-pairing__page{padding:1.35rem 1.4rem 1.5rem}
|
|
1143
|
+
.bl-pairing__eyebrow{margin:0;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#7c1fa2}
|
|
1144
|
+
.bl-pairing__title{margin:.5rem 0 0;font-size:2rem;line-height:1.1;font-weight:600}
|
|
1145
|
+
.bl-pairing__copy{margin:.75rem 0 0;max-width:36rem;font-size:1rem;line-height:1.6;color:var(--theme-elevation-600,#475569)}
|
|
1146
|
+
.bl-pairing__actions{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:1.1rem}
|
|
1147
|
+
.bl-pairing__btn{display:inline-flex;align-items:center;min-height:2.15rem;padding:.4rem .85rem;border-radius:8px;background:#db0082;color:#fff;font-size:14px;font-weight:600}
|
|
1148
|
+
.bl-pairing__btn--ghost{background:#fff;color:#0f172a;border:1px solid #cbd5e1}
|
|
1149
|
+
`;
|
|
1150
|
+
//#endregion
|
|
111
1151
|
//#region src/admin/font-field.tsx
|
|
112
1152
|
/**
|
|
113
1153
|
* Listbox over the catalogue ids in `admin.custom.ids`. Each option is the
|
|
114
1154
|
* family name in that face, with the catalogue hint beneath. Faces load
|
|
115
|
-
* from the site's font route the
|
|
1155
|
+
* from the site's font route for the selected family, and for every
|
|
1156
|
+
* catalogue id once the picker opens.
|
|
116
1157
|
*/
|
|
117
1158
|
const FontField = ({ field, path, readOnly }) => {
|
|
118
1159
|
const { value, setValue, showError, errorMessage } = useField({ path });
|
|
@@ -142,6 +1183,8 @@ const FontField = ({ field, path, readOnly }) => {
|
|
|
142
1183
|
const [open, setOpen] = useState(false);
|
|
143
1184
|
const [faces, setFaces] = useState(false);
|
|
144
1185
|
const [active, setActive] = useState(-1);
|
|
1186
|
+
const [query, setQuery] = useState("");
|
|
1187
|
+
const [category, setCategory] = useState("all");
|
|
145
1188
|
const typed = useRef("");
|
|
146
1189
|
const typedAt = useRef(0);
|
|
147
1190
|
const id = useId();
|
|
@@ -152,10 +1195,18 @@ const FontField = ({ field, path, readOnly }) => {
|
|
|
152
1195
|
if (open) setFaces(true);
|
|
153
1196
|
}, [open]);
|
|
154
1197
|
const selected = options.find((option) => option.value === (value ?? "")) ?? options[0];
|
|
1198
|
+
const visible = options.filter((option) => {
|
|
1199
|
+
if (query && !option.family.toLowerCase().includes(query.trim().toLowerCase())) return false;
|
|
1200
|
+
if (category === "all" || !option.specimen) return true;
|
|
1201
|
+
return findFont(option.value)?.category === category;
|
|
1202
|
+
});
|
|
1203
|
+
const role = sameAsBody ? FONT_ROLE_COPY.heading : FONT_ROLE_COPY.body;
|
|
155
1204
|
function choose(option) {
|
|
156
1205
|
setValue(option.value);
|
|
157
1206
|
setOpen(false);
|
|
158
1207
|
setActive(-1);
|
|
1208
|
+
setQuery("");
|
|
1209
|
+
setCategory("all");
|
|
159
1210
|
}
|
|
160
1211
|
function onKeyDown(event) {
|
|
161
1212
|
if (event.key === "ArrowDown") {
|
|
@@ -198,26 +1249,35 @@ const FontField = ({ field, path, readOnly }) => {
|
|
|
198
1249
|
}
|
|
199
1250
|
}
|
|
200
1251
|
const activeId = active >= 0 && open ? `${id}-option-${active}` : void 0;
|
|
1252
|
+
const faceIds = faces ? ids : isFontId(value) ? [value] : [];
|
|
201
1253
|
return /* @__PURE__ */ jsxs("div", {
|
|
202
1254
|
className: "field-type bl-font-field",
|
|
203
1255
|
children: [
|
|
1256
|
+
/* @__PURE__ */ jsx("style", {
|
|
1257
|
+
href: "bl-pairing",
|
|
1258
|
+
precedence: "default",
|
|
1259
|
+
children: TYPOGRAPHY_PAIRING_CSS
|
|
1260
|
+
}),
|
|
204
1261
|
/* @__PURE__ */ jsx("style", {
|
|
205
1262
|
href: "bl-font-field",
|
|
206
1263
|
precedence: "default",
|
|
207
|
-
children: CSS$
|
|
1264
|
+
children: CSS$2
|
|
208
1265
|
}),
|
|
209
|
-
|
|
1266
|
+
faceIds.length > 0 ? /* @__PURE__ */ jsx("style", {
|
|
210
1267
|
"data-bl-font-faces": "",
|
|
211
|
-
children: fontFaceCss(
|
|
1268
|
+
children: fontFaceCss(faceIds, fontsBaseUrl)
|
|
212
1269
|
}) : null,
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
required
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
1270
|
+
/* @__PURE__ */ jsxs(FontRoleCard, {
|
|
1271
|
+
title: typeof field.label === "string" ? field.label : role.label,
|
|
1272
|
+
hint: role.hint,
|
|
1273
|
+
required,
|
|
1274
|
+
family: selected?.family ?? "Inter",
|
|
1275
|
+
preview: sameAsBody ? "heading" : "body",
|
|
1276
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
1277
|
+
htmlFor: buttonId,
|
|
1278
|
+
label: field.label,
|
|
1279
|
+
required
|
|
1280
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
221
1281
|
id: buttonId,
|
|
222
1282
|
type: "button",
|
|
223
1283
|
className: "bl-font-field__trigger",
|
|
@@ -227,34 +1287,94 @@ const FontField = ({ field, path, readOnly }) => {
|
|
|
227
1287
|
"aria-controls": listId,
|
|
228
1288
|
"aria-activedescendant": activeId,
|
|
229
1289
|
onClick: () => setOpen((was) => !was),
|
|
230
|
-
onBlur: () => setOpen(false),
|
|
231
1290
|
onKeyDown,
|
|
232
|
-
children:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
"aria-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
choose(option);
|
|
245
|
-
},
|
|
246
|
-
onMouseEnter: () => setActive(i),
|
|
247
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
248
|
-
className: "bl-font-field__family",
|
|
249
|
-
style: option.specimen ? { fontFamily: `"${option.family}"` } : void 0,
|
|
250
|
-
children: option.family
|
|
251
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
252
|
-
className: "bl-font-field__hint",
|
|
253
|
-
children: option.hint
|
|
254
|
-
})]
|
|
255
|
-
}, option.value || "same"))
|
|
256
|
-
}) : null]
|
|
1291
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
1292
|
+
className: "bl-font-field__selected-label",
|
|
1293
|
+
children: "Selected"
|
|
1294
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1295
|
+
className: "bl-font-field__selected-name",
|
|
1296
|
+
style: selected?.specimen ? { fontFamily: `"${selected.family}"` } : void 0,
|
|
1297
|
+
children: selected?.family ?? "Choose a font"
|
|
1298
|
+
})] }), /* @__PURE__ */ jsx("span", {
|
|
1299
|
+
"aria-hidden": "true",
|
|
1300
|
+
children: "⌄"
|
|
1301
|
+
})]
|
|
1302
|
+
})]
|
|
257
1303
|
}),
|
|
1304
|
+
open ? /* @__PURE__ */ jsx("div", {
|
|
1305
|
+
className: "bl-font-field__modal",
|
|
1306
|
+
onClick: (event) => {
|
|
1307
|
+
if (event.target === event.currentTarget) setOpen(false);
|
|
1308
|
+
},
|
|
1309
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1310
|
+
className: "bl-font-field__panel",
|
|
1311
|
+
children: [
|
|
1312
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1313
|
+
className: "bl-font-field__panel-head",
|
|
1314
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", { children: "Choose a font" }), /* @__PURE__ */ jsx("h2", { children: role.label })] }), /* @__PURE__ */ jsx("button", {
|
|
1315
|
+
type: "button",
|
|
1316
|
+
onClick: () => setOpen(false),
|
|
1317
|
+
"aria-label": "Close",
|
|
1318
|
+
children: "×"
|
|
1319
|
+
})]
|
|
1320
|
+
}),
|
|
1321
|
+
/* @__PURE__ */ jsx("input", {
|
|
1322
|
+
type: "search",
|
|
1323
|
+
className: "bl-font-field__search",
|
|
1324
|
+
placeholder: "Search fonts…",
|
|
1325
|
+
value: query,
|
|
1326
|
+
onChange: (event) => setQuery(event.target.value)
|
|
1327
|
+
}),
|
|
1328
|
+
/* @__PURE__ */ jsx("div", {
|
|
1329
|
+
className: "bl-font-field__filters",
|
|
1330
|
+
children: [
|
|
1331
|
+
"all",
|
|
1332
|
+
"sans",
|
|
1333
|
+
"serif",
|
|
1334
|
+
"display",
|
|
1335
|
+
"mono"
|
|
1336
|
+
].map((item) => /* @__PURE__ */ jsx("button", {
|
|
1337
|
+
type: "button",
|
|
1338
|
+
"data-on": category === item ? "" : void 0,
|
|
1339
|
+
onClick: () => setCategory(item),
|
|
1340
|
+
children: item === "all" ? "All" : item === "sans" ? "Sans serif" : item === "serif" ? "Serif" : item === "display" ? "Display" : "Mono"
|
|
1341
|
+
}, item))
|
|
1342
|
+
}),
|
|
1343
|
+
/* @__PURE__ */ jsx("ul", {
|
|
1344
|
+
id: listId,
|
|
1345
|
+
role: "listbox",
|
|
1346
|
+
className: "bl-font-field__menu",
|
|
1347
|
+
children: visible.map((option, i) => /* @__PURE__ */ jsxs("li", {
|
|
1348
|
+
id: `${id}-option-${i}`,
|
|
1349
|
+
role: "option",
|
|
1350
|
+
"aria-selected": option.value === (value ?? ""),
|
|
1351
|
+
className: "bl-font-field__option",
|
|
1352
|
+
onMouseDown: (event) => {
|
|
1353
|
+
event.preventDefault();
|
|
1354
|
+
choose(option);
|
|
1355
|
+
},
|
|
1356
|
+
onMouseEnter: () => setActive(i),
|
|
1357
|
+
children: [
|
|
1358
|
+
/* @__PURE__ */ jsx("span", {
|
|
1359
|
+
className: "bl-font-field__family",
|
|
1360
|
+
style: option.specimen ? { fontFamily: `"${option.family}"` } : void 0,
|
|
1361
|
+
children: option.family
|
|
1362
|
+
}),
|
|
1363
|
+
/* @__PURE__ */ jsx("span", {
|
|
1364
|
+
className: "bl-font-field__hint",
|
|
1365
|
+
children: option.hint
|
|
1366
|
+
}),
|
|
1367
|
+
/* @__PURE__ */ jsx("span", {
|
|
1368
|
+
className: "bl-font-field__specimen",
|
|
1369
|
+
style: option.specimen ? { fontFamily: `"${option.family}"` } : void 0,
|
|
1370
|
+
children: "The quick brown fox jumps over the lazy dog."
|
|
1371
|
+
})
|
|
1372
|
+
]
|
|
1373
|
+
}, option.value || "same"))
|
|
1374
|
+
})
|
|
1375
|
+
]
|
|
1376
|
+
})
|
|
1377
|
+
}) : null,
|
|
258
1378
|
/* @__PURE__ */ jsx(FieldDescription, {
|
|
259
1379
|
description: field.admin?.description,
|
|
260
1380
|
path
|
|
@@ -267,167 +1387,746 @@ const FontField = ({ field, path, readOnly }) => {
|
|
|
267
1387
|
]
|
|
268
1388
|
});
|
|
269
1389
|
};
|
|
270
|
-
const CSS$
|
|
271
|
-
.bl-font-
|
|
272
|
-
.bl-font-
|
|
273
|
-
.bl-font-field__trigger:
|
|
274
|
-
.bl-font-field__trigger:
|
|
275
|
-
.bl-font-
|
|
276
|
-
.bl-font-
|
|
277
|
-
.bl-font-
|
|
278
|
-
.bl-font-
|
|
279
|
-
.bl-font-
|
|
1390
|
+
const CSS$2 = `
|
|
1391
|
+
.bl-font-field{margin-bottom:1rem}
|
|
1392
|
+
.bl-font-field .field-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
1393
|
+
.bl-font-field__trigger{display:flex;align-items:center;justify-content:space-between;width:100%;margin-top:1rem;min-height:3.25rem;padding:.7rem 1rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-input-bg,#fff);color:inherit;font:inherit;text-align:left;cursor:pointer}
|
|
1394
|
+
.bl-font-field__trigger:focus{border-color:var(--theme-elevation-400,#94a3b8);outline:none;box-shadow:0 0 0 1px var(--theme-elevation-400,#94a3b8)}
|
|
1395
|
+
.bl-font-field__trigger:disabled{color:var(--theme-elevation-400,#94a3b8);cursor:not-allowed}
|
|
1396
|
+
.bl-font-field__selected-label{display:block;font-size:11px;color:var(--theme-elevation-500,#64748b)}
|
|
1397
|
+
.bl-font-field__selected-name{display:block;margin-top:.15rem;font-weight:600}
|
|
1398
|
+
.bl-font-field__modal{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:1rem;background:rgba(15,23,42,.45)}
|
|
1399
|
+
.bl-font-field__panel{width:min(36rem,100%);max-height:min(36rem,90vh);display:flex;flex-direction:column;padding:1.15rem 1.2rem;border-radius:16px;background:#fff;color:#0f172a}
|
|
1400
|
+
.bl-font-field__panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem}
|
|
1401
|
+
.bl-font-field__panel-head p{margin:0;font-size:12px;color:#64748b}
|
|
1402
|
+
.bl-font-field__panel-head h2{margin:.2rem 0 0;font-size:1.15rem}
|
|
1403
|
+
.bl-font-field__panel-head button{border:0;background:none;font:inherit;font-size:1.25rem;color:#64748b;cursor:pointer}
|
|
1404
|
+
.bl-font-field__search{margin-top:.9rem;min-height:2.5rem;padding:.5rem .75rem;border:1px solid #cbd5e1;border-radius:10px;font:inherit}
|
|
1405
|
+
.bl-font-field__filters{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.75rem}
|
|
1406
|
+
.bl-font-field__filters button{min-height:1.8rem;padding:.2rem .7rem;border:0;border-radius:999px;background:#f1f5f9;color:#475569;font:inherit;font-size:12px;cursor:pointer}
|
|
1407
|
+
.bl-font-field__filters button[data-on]{background:#0f172a;color:#fff}
|
|
1408
|
+
.bl-font-field__menu{margin:.75rem 0 0;padding:0;list-style:none;overflow:auto;max-height:18rem}
|
|
1409
|
+
.bl-font-field__option{display:flex;flex-direction:column;gap:2px;margin-bottom:.5rem;padding:.75rem .85rem;border:1px solid #e2e8f0;border-radius:12px;cursor:pointer}
|
|
1410
|
+
.bl-font-field__option[aria-selected="true"]{border-color:#7c1fa2;background:#f5f3ff}
|
|
1411
|
+
.bl-font-field__family{font-weight:600}
|
|
1412
|
+
.bl-font-field__hint{color:#64748b;font-size:12px}
|
|
1413
|
+
.bl-font-field__specimen{margin-top:.45rem;font-size:1.25rem;line-height:1.3}
|
|
280
1414
|
`;
|
|
281
1415
|
//#endregion
|
|
282
|
-
//#region src/admin/
|
|
283
|
-
|
|
284
|
-
|
|
1416
|
+
//#region src/admin/pairing-field.tsx
|
|
1417
|
+
/**
|
|
1418
|
+
* Pairing sample only — no weight, letter-spacing, line-height, or health
|
|
1419
|
+
* scores. Faces load from the site's font route.
|
|
1420
|
+
*/
|
|
1421
|
+
const PairingField = ({ field }) => {
|
|
1422
|
+
const fontsBaseUrl = (field.admin?.custom ?? {}).fontsBaseUrl ?? "/fonts";
|
|
1423
|
+
const { body, heading } = useFormFields(([fields]) => {
|
|
1424
|
+
const record = fields;
|
|
1425
|
+
return {
|
|
1426
|
+
body: String(record["typography.body"]?.value ?? ""),
|
|
1427
|
+
heading: String(record["typography.heading"]?.value ?? "")
|
|
1428
|
+
};
|
|
1429
|
+
});
|
|
1430
|
+
const headingId = heading || body;
|
|
1431
|
+
const bodyFont = findFont(body);
|
|
1432
|
+
const headingFont = findFont(headingId);
|
|
1433
|
+
const ids = [body, headingId].filter(Boolean);
|
|
1434
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1435
|
+
className: "field-type bl-pairing-field",
|
|
1436
|
+
children: [
|
|
1437
|
+
/* @__PURE__ */ jsx("style", {
|
|
1438
|
+
href: "bl-pairing",
|
|
1439
|
+
precedence: "default",
|
|
1440
|
+
children: TYPOGRAPHY_PAIRING_CSS
|
|
1441
|
+
}),
|
|
1442
|
+
ids.length > 0 ? /* @__PURE__ */ jsx("style", {
|
|
1443
|
+
"data-bl-font-faces": "",
|
|
1444
|
+
children: fontFaceCss(ids, fontsBaseUrl)
|
|
1445
|
+
}) : null,
|
|
1446
|
+
/* @__PURE__ */ jsx(TypographyPairing, {
|
|
1447
|
+
headingFamily: headingFont?.family ?? "Georgia",
|
|
1448
|
+
bodyFamily: bodyFont?.family ?? "system-ui"
|
|
1449
|
+
})
|
|
1450
|
+
]
|
|
1451
|
+
});
|
|
1452
|
+
};
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/admin/appearance-choices.tsx
|
|
1455
|
+
function ChoicePreview({ family, value }) {
|
|
1456
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1457
|
+
className: "bl-appearance__preview",
|
|
1458
|
+
"data-family": family,
|
|
1459
|
+
"data-value": value,
|
|
1460
|
+
"aria-hidden": "true",
|
|
1461
|
+
children: family === "defaultTheme" && value === "system" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
1462
|
+
className: "bl-appearance__pane bl-appearance__pane--light",
|
|
1463
|
+
children: [/* @__PURE__ */ jsx("i", {}), /* @__PURE__ */ jsx("b", {})]
|
|
1464
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1465
|
+
className: "bl-appearance__pane bl-appearance__pane--dark",
|
|
1466
|
+
children: [/* @__PURE__ */ jsx("i", {}), /* @__PURE__ */ jsx("b", {})]
|
|
1467
|
+
})] }) : family === "motion" ? /* @__PURE__ */ jsx("span", { className: "bl-appearance__dot" }) : family === "shadow" ? /* @__PURE__ */ jsx("div", { className: "bl-appearance__lift" }) : family === "radius" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1468
|
+
/* @__PURE__ */ jsx("b", { children: "Button" }),
|
|
1469
|
+
/* @__PURE__ */ jsx("i", { className: "bl-appearance__input" }),
|
|
1470
|
+
/* @__PURE__ */ jsx("i", { className: "bl-appearance__card" })
|
|
1471
|
+
] }) : family === "density" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1472
|
+
/* @__PURE__ */ jsx("b", {}),
|
|
1473
|
+
/* @__PURE__ */ jsx("i", { className: "bl-appearance__input" }),
|
|
1474
|
+
/* @__PURE__ */ jsx("i", { className: "bl-appearance__card" })
|
|
1475
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1476
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
1477
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
1478
|
+
/* @__PURE__ */ jsx("b", {})
|
|
1479
|
+
] })
|
|
1480
|
+
});
|
|
285
1481
|
}
|
|
286
|
-
function
|
|
287
|
-
return
|
|
1482
|
+
function AppearanceChoices({ values, onChange, readOnly }) {
|
|
1483
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1484
|
+
className: "bl-appearance",
|
|
1485
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
1486
|
+
className: "bl-appearance__intro",
|
|
1487
|
+
children: [/* @__PURE__ */ jsx("h2", { children: "Set the overall feel of your site" }), /* @__PURE__ */ jsx("p", { children: "Choose how rounded, elevated, animated, and spacious the interface feels. You don’t manage the technical values — the system handles those." })]
|
|
1488
|
+
}), Object.keys(APPEARANCE_CHOICES).map((family) => {
|
|
1489
|
+
const choices = APPEARANCE_CHOICES[family];
|
|
1490
|
+
const copy = APPEARANCE_FAMILY_COPY[family];
|
|
1491
|
+
return /* @__PURE__ */ jsxs("fieldset", {
|
|
1492
|
+
className: "bl-appearance__family",
|
|
1493
|
+
children: [
|
|
1494
|
+
/* @__PURE__ */ jsxs("legend", { children: [/* @__PURE__ */ jsx("strong", { children: copy.title }), /* @__PURE__ */ jsx("span", { children: copy.hint })] }),
|
|
1495
|
+
/* @__PURE__ */ jsx("div", {
|
|
1496
|
+
className: "bl-appearance__choices",
|
|
1497
|
+
"data-family": family,
|
|
1498
|
+
children: Object.entries(choices).map(([value, item]) => {
|
|
1499
|
+
const selected = values[family] === value;
|
|
1500
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
1501
|
+
className: "bl-appearance__choice",
|
|
1502
|
+
"data-selected": selected ? "" : void 0,
|
|
1503
|
+
children: [
|
|
1504
|
+
/* @__PURE__ */ jsx("input", {
|
|
1505
|
+
type: "radio",
|
|
1506
|
+
name: family,
|
|
1507
|
+
value,
|
|
1508
|
+
checked: selected,
|
|
1509
|
+
disabled: readOnly,
|
|
1510
|
+
onChange: () => onChange(family, value)
|
|
1511
|
+
}),
|
|
1512
|
+
/* @__PURE__ */ jsx(ChoicePreview, {
|
|
1513
|
+
family,
|
|
1514
|
+
value
|
|
1515
|
+
}),
|
|
1516
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1517
|
+
className: "bl-appearance__copy",
|
|
1518
|
+
children: [/* @__PURE__ */ jsxs("strong", { children: [item.label, selected ? /* @__PURE__ */ jsx("em", {
|
|
1519
|
+
"aria-hidden": "true",
|
|
1520
|
+
children: "✓"
|
|
1521
|
+
}) : null] }), /* @__PURE__ */ jsx("span", { children: item.hint })]
|
|
1522
|
+
})
|
|
1523
|
+
]
|
|
1524
|
+
}, value);
|
|
1525
|
+
})
|
|
1526
|
+
}),
|
|
1527
|
+
family === "motion" ? /* @__PURE__ */ jsx("p", {
|
|
1528
|
+
className: "bl-appearance__note",
|
|
1529
|
+
children: "Visitors who prefer reduced motion will still see fewer animations automatically."
|
|
1530
|
+
}) : null
|
|
1531
|
+
]
|
|
1532
|
+
}, family);
|
|
1533
|
+
})]
|
|
1534
|
+
});
|
|
288
1535
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
1536
|
+
const APPEARANCE_CHOICES_CSS = `
|
|
1537
|
+
.bl-appearance{display:flex;flex-direction:column;gap:1.25rem}
|
|
1538
|
+
.bl-appearance__intro h2{margin:0;font-size:1.1rem}
|
|
1539
|
+
.bl-appearance__intro p{margin:.35rem 0 0;font-size:13px;color:var(--theme-elevation-500,#64748b);max-width:40rem}
|
|
1540
|
+
.bl-appearance__family{margin:0;padding:1rem 1.1rem 1.15rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-0,#fff)}
|
|
1541
|
+
.bl-appearance__family legend{padding:0;margin-bottom:.85rem}
|
|
1542
|
+
.bl-appearance__family legend strong{display:block;font-size:14px}
|
|
1543
|
+
.bl-appearance__family legend span{display:block;margin-top:.25rem;font-size:12px;color:var(--theme-elevation-500,#64748b);font-weight:400}
|
|
1544
|
+
.bl-appearance__choices{display:grid;gap:.6rem}
|
|
1545
|
+
.bl-appearance__choices[data-family=defaultTheme],.bl-appearance__choices[data-family=shadow],.bl-appearance__choices[data-family=motion],.bl-appearance__choices[data-family=density]{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
1546
|
+
.bl-appearance__choices[data-family=radius]{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
1547
|
+
.bl-appearance__choice{display:flex;flex-direction:column;gap:.65rem;padding:.75rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-0,#fff);cursor:pointer}
|
|
1548
|
+
.bl-appearance__choice[data-selected]{box-shadow:inset 0 0 0 2px #0f172a;border-color:#0f172a}
|
|
1549
|
+
.bl-appearance__choice input{position:absolute;opacity:0;pointer-events:none}
|
|
1550
|
+
.bl-appearance__copy{display:flex;flex-direction:column;gap:.2rem}
|
|
1551
|
+
.bl-appearance__copy strong{display:flex;justify-content:space-between;align-items:center;gap:.4rem;font-size:14px}
|
|
1552
|
+
.bl-appearance__copy em{font-style:normal}
|
|
1553
|
+
.bl-appearance__copy span{font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
1554
|
+
.bl-appearance__note{margin:.75rem 0 0;font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
1555
|
+
.bl-appearance__preview{--bl-preview-brand:#db0082;--bl-preview-radius:8px;border-radius:10px;background:#f8fafc;overflow:hidden}
|
|
1556
|
+
.bl-appearance__preview[data-family=defaultTheme]{display:grid;min-height:5.5rem;padding:.65rem}
|
|
1557
|
+
.bl-appearance__preview[data-family=defaultTheme][data-value=system]{grid-template-columns:1fr 1fr;padding:0}
|
|
1558
|
+
.bl-appearance__preview[data-family=defaultTheme][data-value=dark]{background:#020617}
|
|
1559
|
+
.bl-appearance__pane{display:flex;flex-direction:column;gap:.45rem;padding:.65rem}
|
|
1560
|
+
.bl-appearance__pane--dark{background:#020617}
|
|
1561
|
+
.bl-appearance__pane i,.bl-appearance__preview[data-family=defaultTheme] > i{display:block;height:.4rem;width:42%;border-radius:99px;background:#0f172a}
|
|
1562
|
+
.bl-appearance__pane i + i,.bl-appearance__preview[data-family=defaultTheme] > i + i{width:64%;background:#cbd5e1}
|
|
1563
|
+
.bl-appearance__pane--dark i{background:#fff}
|
|
1564
|
+
.bl-appearance__pane--dark i + i{background:#334155}
|
|
1565
|
+
.bl-appearance__preview[data-family=defaultTheme][data-value=dark] > i{background:#fff}
|
|
1566
|
+
.bl-appearance__preview[data-family=defaultTheme][data-value=dark] > i + i{background:#334155}
|
|
1567
|
+
.bl-appearance__pane b,.bl-appearance__preview[data-family=defaultTheme] > b{display:block;height:1.6rem;margin-top:auto;border-radius:6px;background:var(--bl-preview-brand)}
|
|
1568
|
+
.bl-appearance__pane--dark b{background:var(--bl-preview-brand)}
|
|
1569
|
+
.bl-appearance__preview[data-family=radius]{display:flex;flex-direction:column;gap:.45rem;padding:.65rem;background:#fff}
|
|
1570
|
+
.bl-appearance__preview[data-family=radius] b{display:grid;place-items:center;height:1.85rem;background:var(--bl-preview-brand);color:#fff;font-size:10px;font-weight:600;border-radius:var(--bl-preview-radius)}
|
|
1571
|
+
.bl-appearance__input{display:block;height:1.85rem;border:1px solid #cbd5e1;background:#f8fafc;border-radius:var(--bl-preview-radius)}
|
|
1572
|
+
.bl-appearance__card{display:block;height:2.4rem;border:1px solid #e2e8f0;background:#fff;border-radius:var(--bl-preview-radius)}
|
|
1573
|
+
.bl-appearance__preview[data-value=sharp]{--bl-preview-radius:0}
|
|
1574
|
+
.bl-appearance__preview[data-value=subtle]{--bl-preview-radius:6px}
|
|
1575
|
+
.bl-appearance__preview[data-value=rounded]{--bl-preview-radius:12px}
|
|
1576
|
+
.bl-appearance__preview[data-value=pill]{--bl-preview-radius:999px}
|
|
1577
|
+
.bl-appearance__preview[data-value=pill] .bl-appearance__card{--bl-preview-radius:1.25rem}
|
|
1578
|
+
.bl-appearance__preview[data-family=shadow]{display:grid;place-items:center;min-height:6.5rem;padding:.85rem}
|
|
1579
|
+
.bl-appearance__lift{width:100%;height:3.5rem;border-radius:10px;background:#fff}
|
|
1580
|
+
.bl-appearance__preview[data-value=flat] .bl-appearance__lift{border:1px solid #e2e8f0}
|
|
1581
|
+
.bl-appearance__preview[data-value=subtle] .bl-appearance__lift{box-shadow:0 1px 2px rgba(15,23,42,.06),0 4px 12px rgba(15,23,42,.06)}
|
|
1582
|
+
.bl-appearance__preview[data-value=elevated] .bl-appearance__lift{box-shadow:0 10px 30px rgba(15,23,42,.12)}
|
|
1583
|
+
.bl-appearance__preview[data-family=motion]{display:flex;align-items:center;min-height:4.5rem;padding:0 .85rem}
|
|
1584
|
+
.bl-appearance__dot{width:1.15rem;height:1.15rem;border-radius:999px;background:var(--bl-preview-brand);animation:bl-appear-dot 1.8s ease-in-out infinite}
|
|
1585
|
+
.bl-appearance__preview[data-value=snappy] .bl-appearance__dot{animation-duration:1s}
|
|
1586
|
+
.bl-appearance__preview[data-value=minimal] .bl-appearance__dot{animation-duration:2.5s}
|
|
1587
|
+
@keyframes bl-appear-dot{0%,100%{transform:translateX(0)}50%{transform:translateX(4.5rem)}}
|
|
1588
|
+
.bl-appearance__preview[data-family=density]{display:flex;flex-direction:column;background:#f8fafc}
|
|
1589
|
+
.bl-appearance__preview[data-value=compact]{gap:.4rem;padding:.55rem}
|
|
1590
|
+
.bl-appearance__preview[data-value=default]{gap:.65rem;padding:.8rem}
|
|
1591
|
+
.bl-appearance__preview[data-value=spacious]{gap:.9rem;padding:1.05rem}
|
|
1592
|
+
.bl-appearance__preview[data-family=density] b{display:block;height:1.7rem;background:var(--bl-preview-brand);border-radius:6px}
|
|
1593
|
+
.bl-appearance__preview[data-value=compact] b,.bl-appearance__preview[data-value=compact] .bl-appearance__input{height:1.45rem}
|
|
1594
|
+
.bl-appearance__preview[data-value=spacious] b,.bl-appearance__preview[data-value=spacious] .bl-appearance__input{height:2.1rem}
|
|
1595
|
+
@media (max-width:720px){
|
|
1596
|
+
.bl-appearance__choices[data-family=defaultTheme],.bl-appearance__choices[data-family=shadow],.bl-appearance__choices[data-family=motion],.bl-appearance__choices[data-family=density],.bl-appearance__choices[data-family=radius]{grid-template-columns:1fr}
|
|
1597
|
+
}
|
|
1598
|
+
@media (prefers-reduced-motion:reduce){
|
|
1599
|
+
.bl-appearance__dot{animation:none}
|
|
1600
|
+
}
|
|
1601
|
+
`;
|
|
1602
|
+
//#endregion
|
|
1603
|
+
//#region src/admin/appearance-field.tsx
|
|
1604
|
+
/**
|
|
1605
|
+
* Named appearance choices. Soft → pill, Gentle → minimal, Balanced →
|
|
1606
|
+
* default, Follow device → system. No rem or ms.
|
|
1607
|
+
*/
|
|
1608
|
+
const AppearanceField = ({ path, readOnly }) => {
|
|
1609
|
+
const defaultTheme = useField({ path: `${path}.defaultTheme` });
|
|
1610
|
+
const radius = useField({ path: `${path}.radius` });
|
|
1611
|
+
const shadow = useField({ path: `${path}.shadow` });
|
|
1612
|
+
const motion = useField({ path: `${path}.motion` });
|
|
1613
|
+
const density = useField({ path: `${path}.density` });
|
|
1614
|
+
const fields = {
|
|
1615
|
+
defaultTheme,
|
|
1616
|
+
radius,
|
|
1617
|
+
shadow,
|
|
1618
|
+
motion,
|
|
1619
|
+
density
|
|
1620
|
+
};
|
|
1621
|
+
function onChange(name, value) {
|
|
1622
|
+
fields[name].setValue(value);
|
|
1623
|
+
}
|
|
1624
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1625
|
+
className: "field-type bl-appearance-field",
|
|
1626
|
+
children: [/* @__PURE__ */ jsx("style", {
|
|
1627
|
+
href: "bl-appearance",
|
|
1628
|
+
precedence: "default",
|
|
1629
|
+
children: APPEARANCE_CHOICES_CSS
|
|
1630
|
+
}), /* @__PURE__ */ jsx(AppearanceChoices, {
|
|
1631
|
+
values: {
|
|
1632
|
+
defaultTheme: defaultTheme.value ?? "system",
|
|
1633
|
+
radius: radius.value ?? "rounded",
|
|
1634
|
+
shadow: shadow.value ?? "subtle",
|
|
1635
|
+
motion: motion.value ?? "smooth",
|
|
1636
|
+
density: density.value ?? "default"
|
|
1637
|
+
},
|
|
1638
|
+
onChange,
|
|
1639
|
+
readOnly
|
|
1640
|
+
})]
|
|
299
1641
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
1642
|
+
};
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region src/admin/grey-scale-field.tsx
|
|
1645
|
+
const FAMILIES = Object.keys(presetHints.greyScale);
|
|
1646
|
+
const SWATCHES = [
|
|
1647
|
+
"50",
|
|
1648
|
+
"200",
|
|
1649
|
+
"400",
|
|
1650
|
+
"700",
|
|
1651
|
+
"950"
|
|
1652
|
+
];
|
|
1653
|
+
function GreyScaleChoices({ value, onChange, readOnly }) {
|
|
1654
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1655
|
+
className: "bl-grey",
|
|
1656
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
1657
|
+
className: "bl-grey__head",
|
|
1658
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", { children: "Gray family" }), /* @__PURE__ */ jsx("p", { children: "Choose the neutral family used for page backgrounds, cards, borders, inputs, and muted text." })] }), /* @__PURE__ */ jsx("span", {
|
|
1659
|
+
className: "bl-grey__badge",
|
|
1660
|
+
children: "Required"
|
|
1661
|
+
})]
|
|
1662
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1663
|
+
className: "bl-grey__choices",
|
|
1664
|
+
children: FAMILIES.map((id) => {
|
|
1665
|
+
const copy = presetHints.greyScale[id];
|
|
1666
|
+
const selected = value === id;
|
|
1667
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
1668
|
+
className: "bl-grey__choice",
|
|
1669
|
+
"data-selected": selected ? "" : void 0,
|
|
1670
|
+
children: [
|
|
1671
|
+
/* @__PURE__ */ jsx("input", {
|
|
1672
|
+
type: "radio",
|
|
1673
|
+
name: "greyScale",
|
|
1674
|
+
value: id,
|
|
1675
|
+
checked: selected,
|
|
1676
|
+
disabled: readOnly,
|
|
1677
|
+
onChange: () => onChange(id)
|
|
1678
|
+
}),
|
|
1679
|
+
/* @__PURE__ */ jsx("span", {
|
|
1680
|
+
className: "bl-grey__ramp",
|
|
1681
|
+
"aria-hidden": "true",
|
|
1682
|
+
children: SWATCHES.map((step) => /* @__PURE__ */ jsx("i", { style: { background: `hsl(${GREY_SCALES[id][step]})` } }, step))
|
|
1683
|
+
}),
|
|
1684
|
+
/* @__PURE__ */ jsxs("strong", { children: [copy.label, selected ? /* @__PURE__ */ jsx("em", {
|
|
1685
|
+
"aria-hidden": "true",
|
|
1686
|
+
children: "✓"
|
|
1687
|
+
}) : null] }),
|
|
1688
|
+
/* @__PURE__ */ jsx("span", { children: copy.hint })
|
|
1689
|
+
]
|
|
1690
|
+
}, id);
|
|
1691
|
+
})
|
|
1692
|
+
})]
|
|
309
1693
|
});
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Visual picker over the five grey-scale presets. Replaces the stock select
|
|
1697
|
+
* on Colors; the stored value is still `colors.greyScale`.
|
|
1698
|
+
*/
|
|
1699
|
+
const GreyScaleField = ({ path, readOnly }) => {
|
|
1700
|
+
const { value, setValue } = useField({ path });
|
|
1701
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1702
|
+
className: "field-type bl-grey-field",
|
|
1703
|
+
children: [/* @__PURE__ */ jsx("style", {
|
|
1704
|
+
href: "bl-grey",
|
|
1705
|
+
precedence: "default",
|
|
1706
|
+
children: GREY_SCALE_FIELD_CSS
|
|
1707
|
+
}), /* @__PURE__ */ jsx(GreyScaleChoices, {
|
|
1708
|
+
value: value ?? "slate",
|
|
1709
|
+
onChange: setValue,
|
|
1710
|
+
readOnly
|
|
1711
|
+
})]
|
|
1712
|
+
});
|
|
1713
|
+
};
|
|
1714
|
+
const GREY_SCALE_FIELD_CSS = `
|
|
1715
|
+
.bl-grey{margin:0 0 1.75rem}
|
|
1716
|
+
.bl-grey__head{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;margin-bottom:.85rem}
|
|
1717
|
+
.bl-grey__head h2{margin:0;font-size:1.1rem}
|
|
1718
|
+
.bl-grey__head p{margin:.35rem 0 0;max-width:40rem;font-size:13px;color:var(--theme-elevation-500,#64748b)}
|
|
1719
|
+
.bl-grey__badge{font:inherit;font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:.15rem .45rem;border-radius:999px;background:var(--theme-elevation-100,#f1f5f9);color:var(--theme-elevation-500,#64748b)}
|
|
1720
|
+
.bl-grey__choices{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:.6rem}
|
|
1721
|
+
.bl-grey__choice{display:flex;flex-direction:column;gap:.45rem;padding:.75rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:12px;background:var(--theme-elevation-0,#fff);cursor:pointer}
|
|
1722
|
+
.bl-grey__choice[data-selected]{box-shadow:inset 0 0 0 2px #0f172a;border-color:#0f172a}
|
|
1723
|
+
.bl-grey__choice input{position:absolute;opacity:0;pointer-events:none}
|
|
1724
|
+
.bl-grey__ramp{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:3px}
|
|
1725
|
+
.bl-grey__ramp i{display:block;height:2rem;border-radius:6px}
|
|
1726
|
+
.bl-grey__choice strong{display:flex;justify-content:space-between;align-items:center;gap:.4rem;font-size:14px}
|
|
1727
|
+
.bl-grey__choice em{font-style:normal}
|
|
1728
|
+
.bl-grey__choice > span{font-size:12px;color:var(--theme-elevation-500,#64748b)}
|
|
1729
|
+
[data-bl-theme-preview=dark] .bl-grey__head p,[data-bl-theme-preview=dark] .bl-grey__choice > span{color:#94a3b8}
|
|
1730
|
+
[data-bl-theme-preview=dark] .bl-grey__choice{background:#0f172a;border-color:#334155;color:#f8fafc}
|
|
1731
|
+
[data-bl-theme-preview=dark] .bl-grey__choice[data-selected]{box-shadow:inset 0 0 0 2px #f8fafc;border-color:#f8fafc}
|
|
1732
|
+
[data-bl-theme-preview=dark] .bl-grey__badge{background:#1e293b;color:#94a3b8}
|
|
1733
|
+
@media (max-width:720px){.bl-grey__choices{grid-template-columns:1fr}}
|
|
1734
|
+
`;
|
|
1735
|
+
//#endregion
|
|
1736
|
+
//#region src/admin/contrast-report.tsx
|
|
1737
|
+
/**
|
|
1738
|
+
* Contrast used to be a Colors-tab list. Readability now lives on each
|
|
1739
|
+
* color card: Needs attention opens a popup. This field stays in the
|
|
1740
|
+
* schema so `contrastTarget` is still a Theme option; it paints nothing.
|
|
1741
|
+
*/
|
|
1742
|
+
const ContrastReport = () => null;
|
|
1743
|
+
//#endregion
|
|
1744
|
+
//#region src/admin/section-heading.tsx
|
|
1745
|
+
/**
|
|
1746
|
+
* Unnamed UI heading between Theme fields. No document path.
|
|
1747
|
+
*/
|
|
1748
|
+
const SectionHeading = ({ field }) => {
|
|
1749
|
+
const custom = field.admin?.custom ?? {};
|
|
1750
|
+
if (!custom.title) return null;
|
|
1751
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
1752
|
+
className: "bl-section-heading",
|
|
1753
|
+
children: [
|
|
1754
|
+
/* @__PURE__ */ jsx("style", {
|
|
1755
|
+
href: "bl-section-heading",
|
|
1756
|
+
precedence: "default",
|
|
1757
|
+
children: CSS$1
|
|
1758
|
+
}),
|
|
1759
|
+
/* @__PURE__ */ jsx("h2", { children: custom.title }),
|
|
1760
|
+
custom.hint ? /* @__PURE__ */ jsx("p", { children: custom.hint }) : null
|
|
1761
|
+
]
|
|
1762
|
+
});
|
|
1763
|
+
};
|
|
1764
|
+
const CSS$1 = `
|
|
1765
|
+
.bl-section-heading{margin:1.25rem 0 .65rem}
|
|
1766
|
+
.bl-section-heading:first-child{margin-top:0}
|
|
1767
|
+
.bl-section-heading h2{margin:0;font-size:1.1rem}
|
|
1768
|
+
.bl-section-heading p{margin:.35rem 0 0;font-size:13px;color:var(--theme-elevation-500,#64748b);max-width:40rem}
|
|
1769
|
+
`;
|
|
1770
|
+
//#endregion
|
|
1771
|
+
//#region src/theme/changes.ts
|
|
1772
|
+
/** Payload may hand an empty array field as `{}`. `.map` on that crashes the Theme form. */
|
|
1773
|
+
function libraryRows(value) {
|
|
1774
|
+
return Array.isArray(value) ? value : [];
|
|
1775
|
+
}
|
|
1776
|
+
function colorLine(name, before, after) {
|
|
1777
|
+
const a = before && typeof before === "object" ? before : { hex: before };
|
|
1778
|
+
const b = after && typeof after === "object" ? after : { hex: after };
|
|
1779
|
+
const left = a;
|
|
1780
|
+
const right = b;
|
|
1781
|
+
if (left.hex === right.hex && String(left.sourceStep) === String(right.sourceStep)) return null;
|
|
1782
|
+
return `${name}: ${String(left.hex ?? "—")} → ${String(right.hex ?? "—")}`;
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Human lines for the Publish confirmation. Only this child's
|
|
1786
|
+
* differences; other responsibilities are not listed.
|
|
1787
|
+
*/
|
|
1788
|
+
function themeChildChanges(child, published, incoming) {
|
|
1789
|
+
if (child === "colors") {
|
|
1790
|
+
const lines = [];
|
|
1791
|
+
for (const key of SYSTEM_COLOR_KEYS) {
|
|
1792
|
+
const line = colorLine(key[0].toUpperCase() + key.slice(1), published.colors?.brand?.[key], incoming.colors?.brand?.[key]);
|
|
1793
|
+
if (line) lines.push(line);
|
|
1794
|
+
}
|
|
1795
|
+
const beforeKeys = libraryRows(published.colors?.library).map((row) => row.key).join(", ");
|
|
1796
|
+
const afterKeys = libraryRows(incoming.colors?.library).map((row) => row.key).join(", ");
|
|
1797
|
+
if (beforeKeys !== afterKeys) lines.push(`Custom colors: ${beforeKeys || "none"} → ${afterKeys || "none"}`);
|
|
1798
|
+
if ((published.colors?.greyScale ?? "") !== (incoming.colors?.greyScale ?? "")) lines.push(`Gray family: ${published.colors?.greyScale ?? "—"} → ${incoming.colors?.greyScale ?? "—"}`);
|
|
1799
|
+
return lines;
|
|
1800
|
+
}
|
|
1801
|
+
if (child === "typography") {
|
|
1802
|
+
const lines = [];
|
|
1803
|
+
if ((published.typography?.heading ?? "") !== (incoming.typography?.heading ?? "")) lines.push(`Heading: ${published.typography?.heading || "same as body"} → ${incoming.typography?.heading || "same as body"}`);
|
|
1804
|
+
if ((published.typography?.body ?? "") !== (incoming.typography?.body ?? "")) lines.push(`Body: ${published.typography?.body ?? "—"} → ${incoming.typography?.body ?? "—"}`);
|
|
1805
|
+
return lines;
|
|
1806
|
+
}
|
|
1807
|
+
if (child === "appearance") return [
|
|
1808
|
+
"defaultTheme",
|
|
1809
|
+
"radius",
|
|
1810
|
+
"shadow",
|
|
1811
|
+
"motion",
|
|
1812
|
+
"density"
|
|
1813
|
+
].flatMap((key) => {
|
|
1814
|
+
const left = published.appearance?.[key] ?? "";
|
|
1815
|
+
const right = incoming.appearance?.[key] ?? "";
|
|
1816
|
+
return left === right ? [] : [`${key}: ${left || "—"} → ${right || "—"}`];
|
|
1817
|
+
});
|
|
1818
|
+
const lines = [];
|
|
1819
|
+
if (uploadId(published.logo) !== uploadId(incoming.logo)) lines.push("Logo updated");
|
|
1820
|
+
if (uploadId(published.favicon) !== uploadId(incoming.favicon)) lines.push("Favicon updated");
|
|
1821
|
+
if (uploadId(published.logoMark) !== uploadId(incoming.logoMark)) lines.push("Mobile menu logo updated");
|
|
1822
|
+
return lines;
|
|
1823
|
+
}
|
|
1824
|
+
function uploadId(value) {
|
|
1825
|
+
if (value && typeof value === "object" && value.id != null) return String(value.id);
|
|
1826
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
1827
|
+
return "";
|
|
1828
|
+
}
|
|
1829
|
+
//#endregion
|
|
1830
|
+
//#region src/theme/publish.ts
|
|
1831
|
+
const THEME_CHILDREN = [
|
|
1832
|
+
"colors",
|
|
1833
|
+
"typography",
|
|
1834
|
+
"appearance",
|
|
1835
|
+
"identity"
|
|
1836
|
+
];
|
|
1837
|
+
function isThemeChild(value) {
|
|
1838
|
+
return typeof value === "string" && THEME_CHILDREN.includes(value);
|
|
1839
|
+
}
|
|
1840
|
+
//#endregion
|
|
1841
|
+
//#region src/admin/publish-confirm.tsx
|
|
1842
|
+
const OTHER_CHILDREN = {
|
|
1843
|
+
Colors: "Typography, Appearance, and Identity stay as they are.",
|
|
1844
|
+
Typography: "Colors, Appearance, and Identity stay as they are.",
|
|
1845
|
+
Appearance: "Colors, Typography, and Identity stay as they are.",
|
|
1846
|
+
Identity: "Colors, Typography, and Appearance stay as they are."
|
|
1847
|
+
};
|
|
1848
|
+
function PublishConfirm({ childLabel, changes, shorts, onKeepEditing, onPublish, busy }) {
|
|
1849
|
+
const remaining = shorts?.filter((item) => !item.meets) ?? [];
|
|
1850
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1851
|
+
className: "bl-publish",
|
|
1852
|
+
role: "dialog",
|
|
1853
|
+
"aria-modal": "true",
|
|
1854
|
+
"aria-labelledby": "bl-publish-title",
|
|
1855
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1856
|
+
className: "bl-publish__panel",
|
|
1857
|
+
children: [
|
|
1858
|
+
/* @__PURE__ */ jsxs("h2", {
|
|
1859
|
+
id: "bl-publish-title",
|
|
1860
|
+
children: ["Publish ", childLabel]
|
|
1861
|
+
}),
|
|
1862
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
1863
|
+
"These ",
|
|
1864
|
+
childLabel.toLowerCase(),
|
|
1865
|
+
" changes go live now.",
|
|
1866
|
+
" ",
|
|
1867
|
+
OTHER_CHILDREN[childLabel] ?? "The other Theme sections stay as they are."
|
|
1868
|
+
] }),
|
|
1869
|
+
changes.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
1870
|
+
className: "bl-publish__empty",
|
|
1871
|
+
children: "No changes to publish."
|
|
1872
|
+
}) : /* @__PURE__ */ jsx("ul", { children: changes.map((change) => /* @__PURE__ */ jsx("li", { children: change }, change)) }),
|
|
1873
|
+
remaining.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
1874
|
+
className: "bl-publish__shorts",
|
|
1875
|
+
children: [
|
|
1876
|
+
/* @__PURE__ */ jsx("strong", { children: "Some colors may be hard to read" }),
|
|
1877
|
+
/* @__PURE__ */ jsx("ul", { children: remaining.map((item) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("span", {
|
|
1878
|
+
"aria-hidden": "true",
|
|
1879
|
+
className: "bl-publish__swatch",
|
|
1880
|
+
style: {
|
|
1881
|
+
background: hslCss(item.light.fill),
|
|
1882
|
+
color: hslCss(item.light.label)
|
|
1883
|
+
},
|
|
1884
|
+
children: "Aa"
|
|
1885
|
+
}), /* @__PURE__ */ jsxs("span", { children: [
|
|
1886
|
+
readabilityHeadline(item).replace(/^Text on /, ""),
|
|
1887
|
+
" ",
|
|
1888
|
+
hardToReadModes(item),
|
|
1889
|
+
"."
|
|
1890
|
+
] })] }, item.id)) }),
|
|
1891
|
+
/* @__PURE__ */ jsx("p", { children: READABILITY_PUBLISH_NOTE })
|
|
1892
|
+
]
|
|
1893
|
+
}) : null,
|
|
1894
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1895
|
+
className: "bl-publish__actions",
|
|
1896
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
1897
|
+
type: "button",
|
|
1898
|
+
onClick: onKeepEditing,
|
|
1899
|
+
children: "Keep editing"
|
|
1900
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
1901
|
+
type: "button",
|
|
1902
|
+
disabled: busy || changes.length === 0,
|
|
1903
|
+
onClick: onPublish,
|
|
1904
|
+
children: remaining.length > 0 ? "Publish anyway" : "Publish"
|
|
1905
|
+
})]
|
|
1906
|
+
})
|
|
1907
|
+
]
|
|
318
1908
|
})
|
|
319
1909
|
});
|
|
320
1910
|
}
|
|
1911
|
+
const PUBLISH_CONFIRM_CSS = `
|
|
1912
|
+
.bl-publish{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:1rem;background:rgba(15,23,42,.45)}
|
|
1913
|
+
.bl-publish__panel{width:min(36rem,100%);padding:1.25rem 1.35rem;border-radius:16px;background:#fff;color:#0f172a;box-shadow:0 24px 60px rgba(15,23,42,.2)}
|
|
1914
|
+
.bl-publish__panel h2{margin:0}
|
|
1915
|
+
.bl-publish__panel p{margin:.5rem 0 0;color:#475569}
|
|
1916
|
+
.bl-publish__empty{font-style:italic}
|
|
1917
|
+
.bl-publish__panel > ul{margin:.75rem 0 0;padding-left:1.1rem}
|
|
1918
|
+
.bl-publish__shorts{margin-top:1rem;padding:.85rem .9rem;border-radius:10px;background:#fffbeb}
|
|
1919
|
+
.bl-publish__shorts strong{display:block}
|
|
1920
|
+
.bl-publish__shorts ul{margin:.65rem 0 0;padding:0;list-style:none}
|
|
1921
|
+
.bl-publish__shorts li{display:flex;gap:.65rem;align-items:center;margin-top:.45rem;font-size:13px}
|
|
1922
|
+
.bl-publish__swatch{flex:none;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:1.6rem;border-radius:6px;font-weight:700;font-size:12px}
|
|
1923
|
+
.bl-publish__shorts p{margin:.65rem 0 0;font-size:13px}
|
|
1924
|
+
.bl-publish__actions{display:flex;justify-content:flex-end;gap:.6rem;margin-top:1.1rem}
|
|
1925
|
+
.bl-publish__actions button{min-height:2.25rem;padding:.4rem .9rem;border-radius:8px;border:1px solid #cbd5e1;background:#fff;font:inherit;cursor:pointer}
|
|
1926
|
+
.bl-publish__actions button:last-child{border:0;background:#0f172a;color:#fff;font-weight:600}
|
|
1927
|
+
.bl-publish__actions button:disabled{opacity:.45;cursor:not-allowed}
|
|
1928
|
+
`;
|
|
1929
|
+
//#endregion
|
|
1930
|
+
//#region src/admin/theme-tab.ts
|
|
1931
|
+
const THEME_TAB_LABELS = {
|
|
1932
|
+
colors: "Colors",
|
|
1933
|
+
typography: "Typography",
|
|
1934
|
+
appearance: "Appearance",
|
|
1935
|
+
identity: "Identity"
|
|
1936
|
+
};
|
|
1937
|
+
/**
|
|
1938
|
+
* The selected Payload field tab on Theme. Defaults to Colors when the
|
|
1939
|
+
* tabs have not painted yet (SSR, first paint).
|
|
1940
|
+
*/
|
|
1941
|
+
function readActiveThemeTab(root = typeof document === "undefined" ? null : document) {
|
|
1942
|
+
if (!root) return "colors";
|
|
1943
|
+
const text = (root.querySelector(".tabs-field__tab-button[aria-selected=\"true\"]") ?? root.querySelector(".tabs-field__tab-button--active") ?? root.querySelector("[role=\"tab\"][aria-selected=\"true\"]"))?.textContent?.replace(/\s+/g, " ").trim();
|
|
1944
|
+
return THEME_CHILDREN.find((id) => THEME_TAB_LABELS[id] === text) ?? "colors";
|
|
1945
|
+
}
|
|
1946
|
+
//#endregion
|
|
1947
|
+
//#region src/admin/publish-child.tsx
|
|
1948
|
+
function asColor(value) {
|
|
1949
|
+
if (value && typeof value === "object") return value;
|
|
1950
|
+
if (typeof value === "string") return { hex: value };
|
|
1951
|
+
}
|
|
1952
|
+
function formDoc(fields) {
|
|
1953
|
+
return {
|
|
1954
|
+
colors: {
|
|
1955
|
+
brand: Object.fromEntries(SYSTEM_COLOR_KEYS.map((key) => [key, {
|
|
1956
|
+
hex: fields[`colors.brand.${key}.hex`]?.value,
|
|
1957
|
+
sourceStep: fields[`colors.brand.${key}.sourceStep`]?.value
|
|
1958
|
+
}])),
|
|
1959
|
+
library: libraryRows(fields["colors.library"]?.value),
|
|
1960
|
+
greyScale: fields["colors.greyScale"]?.value
|
|
1961
|
+
},
|
|
1962
|
+
typography: {
|
|
1963
|
+
body: fields["typography.body"]?.value,
|
|
1964
|
+
heading: fields["typography.heading"]?.value
|
|
1965
|
+
},
|
|
1966
|
+
appearance: {
|
|
1967
|
+
defaultTheme: fields["appearance.defaultTheme"]?.value,
|
|
1968
|
+
radius: fields["appearance.radius"]?.value,
|
|
1969
|
+
shadow: fields["appearance.shadow"]?.value,
|
|
1970
|
+
motion: fields["appearance.motion"]?.value,
|
|
1971
|
+
density: fields["appearance.density"]?.value
|
|
1972
|
+
},
|
|
1973
|
+
logo: fields.logo?.value,
|
|
1974
|
+
favicon: fields.favicon?.value,
|
|
1975
|
+
logoMark: fields.logoMark?.value
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
function publishedDoc(fields) {
|
|
1979
|
+
return formDoc(Object.fromEntries(Object.entries(fields).map(([path, field]) => [path, { value: field?.initialValue }])));
|
|
1980
|
+
}
|
|
1981
|
+
function shortsFrom(doc) {
|
|
1982
|
+
const brand = doc.colors?.brand ?? {};
|
|
1983
|
+
const custom = libraryRows(doc.colors?.library);
|
|
1984
|
+
return [...SYSTEM_COLOR_KEYS.flatMap((key) => {
|
|
1985
|
+
const hex = asColor(brand[key])?.hex;
|
|
1986
|
+
const warning = typeof hex === "string" ? fillLabelWarning(key, SYSTEM_COLOR_COPY[key].label, hex) : null;
|
|
1987
|
+
return warning ? [warning] : [];
|
|
1988
|
+
}), ...custom.flatMap((row, index) => {
|
|
1989
|
+
const warning = row.hex ? fillLabelWarning(`library-${index}`, row.label ?? row.key ?? "Custom", row.hex) : null;
|
|
1990
|
+
return warning ? [warning] : [];
|
|
1991
|
+
})];
|
|
1992
|
+
}
|
|
321
1993
|
/**
|
|
322
|
-
*
|
|
323
|
-
* `
|
|
324
|
-
*
|
|
1994
|
+
* Publish for the open Theme tab. Lives in Payload's document Save
|
|
1995
|
+
* slot (`ThemeSaveButton`) so the header matches a Pages document.
|
|
1996
|
+
* Sets `publishChild` then submits; `beforeChange` writes only that slice.
|
|
325
1997
|
*/
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
const
|
|
1998
|
+
function ThemePublishControls({ child }) {
|
|
1999
|
+
const label = THEME_TAB_LABELS[child];
|
|
2000
|
+
const { submit } = useForm();
|
|
2001
|
+
const publishChild = useField({ path: "publishChild" });
|
|
2002
|
+
const snapshot = useFormFields(([fields]) => {
|
|
329
2003
|
const record = fields;
|
|
330
2004
|
return {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
accent: fieldValue(record, "brand.accent"),
|
|
334
|
-
highlight: fieldValue(record, "brand.highlight"),
|
|
335
|
-
success: fieldValue(record, "brand.success"),
|
|
336
|
-
greyScale: fieldValue(record, "greyScale")
|
|
2005
|
+
incoming: formDoc(record),
|
|
2006
|
+
published: publishedDoc(record)
|
|
337
2007
|
};
|
|
338
2008
|
});
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
2009
|
+
const [open, setOpen] = useState(false);
|
|
2010
|
+
const [busy, setBusy] = useState(false);
|
|
2011
|
+
const changes = useMemo(() => themeChildChanges(child, snapshot.published, snapshot.incoming), [child, snapshot]);
|
|
2012
|
+
const shorts = child === "colors" ? shortsFrom(snapshot.incoming) : void 0;
|
|
2013
|
+
async function publish() {
|
|
2014
|
+
setBusy(true);
|
|
2015
|
+
publishChild.setValue(child);
|
|
2016
|
+
try {
|
|
2017
|
+
await submit();
|
|
2018
|
+
setOpen(false);
|
|
2019
|
+
} finally {
|
|
2020
|
+
setBusy(false);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
353
2023
|
return /* @__PURE__ */ jsxs("div", {
|
|
354
|
-
className: "
|
|
2024
|
+
className: "bl-publish-child",
|
|
355
2025
|
children: [
|
|
356
2026
|
/* @__PURE__ */ jsx("style", {
|
|
357
|
-
href: "bl-
|
|
2027
|
+
href: "bl-publish",
|
|
358
2028
|
precedence: "default",
|
|
359
|
-
children:
|
|
2029
|
+
children: PUBLISH_CONFIRM_CSS
|
|
360
2030
|
}),
|
|
361
|
-
/* @__PURE__ */ jsx("
|
|
362
|
-
|
|
363
|
-
|
|
2031
|
+
/* @__PURE__ */ jsx("style", {
|
|
2032
|
+
href: "bl-publish-child",
|
|
2033
|
+
precedence: "default",
|
|
2034
|
+
children: `${THEME_PREVIEW_TOGGLE_CSS}.bl-publish-child{display:flex;align-items:center;gap:.75rem}.bl-publish-child__button{min-height:2.25rem;padding:.4rem 1rem;border:0;border-radius:8px;background:#a80063;color:#fff;font:inherit;font-weight:600;cursor:pointer}`
|
|
364
2035
|
}),
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
className: "bl-contrast-report__swatch",
|
|
382
|
-
style: {
|
|
383
|
-
color: `hsl(${sample.foreground})`,
|
|
384
|
-
backgroundColor: `hsl(${sample.background})`
|
|
385
|
-
},
|
|
386
|
-
children: "Aa"
|
|
387
|
-
}),
|
|
388
|
-
/* @__PURE__ */ jsxs("span", {
|
|
389
|
-
className: "bl-contrast-report__ratio",
|
|
390
|
-
children: [sample.ratio.toFixed(2), ":1"]
|
|
391
|
-
}),
|
|
392
|
-
/* @__PURE__ */ jsxs("span", {
|
|
393
|
-
className: "bl-contrast-report__level",
|
|
394
|
-
children: [/* @__PURE__ */ jsx(LevelIcon, { level: sample.level }), contrastLevelLabel(sample.level)]
|
|
395
|
-
}),
|
|
396
|
-
/* @__PURE__ */ jsx("span", {
|
|
397
|
-
className: "bl-contrast-report__verdict",
|
|
398
|
-
children: verdict.text
|
|
399
|
-
}),
|
|
400
|
-
/* @__PURE__ */ jsxs("span", {
|
|
401
|
-
className: "bl-contrast-report__label",
|
|
402
|
-
children: [
|
|
403
|
-
check.label,
|
|
404
|
-
" (",
|
|
405
|
-
mode === "light" ? "Light" : "Dark",
|
|
406
|
-
")"
|
|
407
|
-
]
|
|
408
|
-
})
|
|
409
|
-
]
|
|
410
|
-
}, `${check.id}-${mode}`);
|
|
411
|
-
}))
|
|
2036
|
+
child === "colors" ? /* @__PURE__ */ jsx(ThemePreviewToggle, {}) : null,
|
|
2037
|
+
/* @__PURE__ */ jsxs("button", {
|
|
2038
|
+
type: "button",
|
|
2039
|
+
className: "bl-publish-child__button",
|
|
2040
|
+
onClick: () => setOpen(true),
|
|
2041
|
+
children: ["Publish ", label]
|
|
2042
|
+
}),
|
|
2043
|
+
open ? /* @__PURE__ */ jsx(PublishConfirm, {
|
|
2044
|
+
childLabel: label,
|
|
2045
|
+
changes,
|
|
2046
|
+
shorts,
|
|
2047
|
+
busy,
|
|
2048
|
+
onKeepEditing: () => setOpen(false),
|
|
2049
|
+
onPublish: () => {
|
|
2050
|
+
publish();
|
|
2051
|
+
}
|
|
412
2052
|
}) : null
|
|
413
2053
|
]
|
|
414
2054
|
});
|
|
2055
|
+
}
|
|
2056
|
+
/** @deprecated Theme publish is the document Save button */
|
|
2057
|
+
const PublishChild = ({ field }) => {
|
|
2058
|
+
const child = (field.admin?.custom)?.child;
|
|
2059
|
+
return /* @__PURE__ */ jsx(ThemePublishControls, { child: isThemeChild(child) ? child : "colors" });
|
|
2060
|
+
};
|
|
2061
|
+
//#endregion
|
|
2062
|
+
//#region src/admin/save-button.tsx
|
|
2063
|
+
/**
|
|
2064
|
+
* Payload's document Save slot — the same control row as Pages'
|
|
2065
|
+
* "Publish changes". Theme has no drafts; this publishes only the
|
|
2066
|
+
* open field tab. The tab is read after mount so SSR matches.
|
|
2067
|
+
*/
|
|
2068
|
+
function ThemeSaveButton() {
|
|
2069
|
+
const [child, setChild] = useState("colors");
|
|
2070
|
+
useEffect(() => {
|
|
2071
|
+
const sync = () => setChild(readActiveThemeTab());
|
|
2072
|
+
sync();
|
|
2073
|
+
const target = document.querySelector(".tabs-field") ?? document.body;
|
|
2074
|
+
const observer = new MutationObserver(sync);
|
|
2075
|
+
observer.observe(target, {
|
|
2076
|
+
subtree: true,
|
|
2077
|
+
attributes: true,
|
|
2078
|
+
attributeFilter: ["aria-selected", "class"],
|
|
2079
|
+
childList: true
|
|
2080
|
+
});
|
|
2081
|
+
target.addEventListener("click", sync);
|
|
2082
|
+
return () => {
|
|
2083
|
+
observer.disconnect();
|
|
2084
|
+
target.removeEventListener("click", sync);
|
|
2085
|
+
};
|
|
2086
|
+
}, []);
|
|
2087
|
+
return /* @__PURE__ */ jsx(ThemePublishControls, { child });
|
|
2088
|
+
}
|
|
2089
|
+
/** Import-map alias — Theme still points `SaveButton` at this name. */
|
|
2090
|
+
const HiddenSaveButton = ThemeSaveButton;
|
|
2091
|
+
//#endregion
|
|
2092
|
+
//#region src/admin/identity-fallback.tsx
|
|
2093
|
+
/**
|
|
2094
|
+
* Identity child when the site has not registered a cupboard yet.
|
|
2095
|
+
* Shows the fallback lockup and mark; uploads wait on `logo.collection`.
|
|
2096
|
+
*/
|
|
2097
|
+
const IdentityFallback = ({ field }) => {
|
|
2098
|
+
const fallback = (field.admin?.custom)?.fallback;
|
|
2099
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
2100
|
+
className: "bl-identity-fallback",
|
|
2101
|
+
children: [
|
|
2102
|
+
/* @__PURE__ */ jsx("style", {
|
|
2103
|
+
href: "bl-identity-fallback",
|
|
2104
|
+
precedence: "default",
|
|
2105
|
+
children: CSS
|
|
2106
|
+
}),
|
|
2107
|
+
/* @__PURE__ */ jsxs("header", { children: [/* @__PURE__ */ jsx("h2", { children: "Site identity" }), /* @__PURE__ */ jsx("p", { children: "Today's lockup and mark until a brand editor uploads replacements." })] }),
|
|
2108
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2109
|
+
className: "bl-identity-fallback__grid",
|
|
2110
|
+
children: [fallback?.lockup ? /* @__PURE__ */ jsxs("figure", { children: [/* @__PURE__ */ jsx("img", {
|
|
2111
|
+
src: fallback.lockup.url,
|
|
2112
|
+
alt: fallback.lockup.alt
|
|
2113
|
+
}), /* @__PURE__ */ jsx("figcaption", { children: "Lockup" })] }) : null, fallback?.mark ? /* @__PURE__ */ jsxs("figure", { children: [/* @__PURE__ */ jsx("img", {
|
|
2114
|
+
src: fallback.mark.url,
|
|
2115
|
+
alt: fallback.mark.alt
|
|
2116
|
+
}), /* @__PURE__ */ jsx("figcaption", { children: "Mark" })] }) : null]
|
|
2117
|
+
})
|
|
2118
|
+
]
|
|
2119
|
+
});
|
|
415
2120
|
};
|
|
416
2121
|
const CSS = `
|
|
417
|
-
.bl-
|
|
418
|
-
.bl-
|
|
419
|
-
.bl-
|
|
420
|
-
.bl-
|
|
421
|
-
.bl-
|
|
422
|
-
.bl-
|
|
423
|
-
.bl-contrast-report__row[data-failing]{background:var(--theme-error-50)}
|
|
424
|
-
.bl-contrast-report__swatch{flex:none;display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:1.75rem;border:1px solid var(--theme-elevation-150);border-radius:var(--style-radius-s);font-weight:600}
|
|
425
|
-
.bl-contrast-report__ratio{width:4.5rem;font-variant-numeric:tabular-nums}
|
|
426
|
-
.bl-contrast-report__level{display:inline-flex;align-items:center;gap:4px;width:5.5rem}
|
|
427
|
-
.bl-contrast-report__verdict{width:6rem;font-weight:600}
|
|
428
|
-
.bl-contrast-report__label{flex:1;min-width:12rem;color:var(--theme-elevation-500)}
|
|
2122
|
+
.bl-identity-fallback header h2{margin:0;font-size:1.1rem}
|
|
2123
|
+
.bl-identity-fallback header p{margin:.35rem 0 1rem;color:var(--theme-elevation-600,#475569)}
|
|
2124
|
+
.bl-identity-fallback__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:1rem}
|
|
2125
|
+
.bl-identity-fallback figure{margin:0;padding:1rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:10px;background:var(--theme-elevation-50,#f8fafc)}
|
|
2126
|
+
.bl-identity-fallback img{display:block;max-width:100%;height:4rem;object-fit:contain}
|
|
2127
|
+
.bl-identity-fallback figcaption{margin-top:.5rem;font-size:.8rem;color:var(--theme-elevation-600,#475569)}
|
|
429
2128
|
`;
|
|
430
2129
|
//#endregion
|
|
431
|
-
export { ColorField, ContrastReport, FontField };
|
|
2130
|
+
export { AppearanceField, ColorField, ColorScaleField, ContrastReport, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, PairingField, PublishChild, SectionHeading, ThemeSaveButton };
|
|
432
2131
|
|
|
433
2132
|
//# sourceMappingURL=admin.mjs.map
|