riverbankcms 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acceptance/matrix.mjs +3 -3
- package/dist/cli/index.mjs +354 -221
- package/dist/{entryCommands-nATGxgma.mjs → entryCommands-61TwBBCA.mjs} +11 -3
- package/dist/{eventUpsertInputSchema-XRzH6yFb.mjs → eventUpsertInputSchema-zAAQrjXE.mjs} +2 -2
- package/dist/{load-config-DwiS-Ibn.mjs → load-config-C7Ght4_r.mjs} +2 -2
- package/dist/load-config-DAdJwh-i.mjs +4 -0
- package/dist/{plan-ChNV88KF.mjs → plan-DB3FQqwH.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-4t4e1z-h.mjs → sdk-event-upsert-CH3xwzuX.mjs} +2743 -884
- package/dist/{src-LhtqTjH7.mjs → src-DsR6-gBQ.mjs} +1 -1
- package/dist/testing.mjs +4 -4
- package/package.json +1 -1
- package/dist/load-config-zu397zDX.mjs +0 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $n as ok, An as unsafeAsEntitlementId, B as parsePublicContactDetails, Bn as unsafeAsPositiveSeconds, Dn as unsafeAsDeclaredMimeType, Et as validateBookingPaymentOptionKinds, Fn as unsafeAsMediaAssetId, Gn as unsafeAsSiteId, Hn as unsafeAsProductId, Jn as unsafeAsStorageBucketName, K as locationValueSchema, Kn as unsafeAsSiteScopedStoragePath, Ln as unsafeAsOriginalFilename, N as parseFooterCompositionV1, P as footerNavigationLayouts, Pn as unsafeAsFileChecksumSha256, Q as isPresent, Qn as err, Qt as mapReadonlyNonEmptyArray, R as isCanonicalRichTextDoc, Tn as unsafeAsBookingSessionId, Tt as parseBookingTimeZone, Un as unsafeAsSafeDownloadFilename, V as parseWeeklyOpeningHours, W as stableJsonStringify, Wn as unsafeAsSiteFontId, Xn as unsafeAsUploadSessionId, Xt as fromArray, Yn as unsafeAsStoragePath, Zn as unsafeAsUserId, Zt as mapNonEmptyArray, a as NAVIGATION_LOCATIONS, bn as parseSiteId, cn as assertNever, f as EVENT_PRESET_VALUES, gn as parseMediaAssetId, gt as localScheduleTimePattern, kn as unsafeAsDetectedMimeType, mn as parseEditorSessionGeneration, n as authoredValuesEqual, p as ORDINARY_EVENT_PRESET_VALUES, u as resolveSiteVerticalBookingConfig, wn as unsafeAsBookingRequirementId, yt as parseUtcIsoDateTimeString, zn as unsafeAsPositiveByteCount } from "./src-
|
|
2
|
+
import { $n as ok, An as unsafeAsEntitlementId, B as parsePublicContactDetails, Bn as unsafeAsPositiveSeconds, Dn as unsafeAsDeclaredMimeType, Et as validateBookingPaymentOptionKinds, Fn as unsafeAsMediaAssetId, Gn as unsafeAsSiteId, Hn as unsafeAsProductId, Jn as unsafeAsStorageBucketName, K as locationValueSchema, Kn as unsafeAsSiteScopedStoragePath, Ln as unsafeAsOriginalFilename, N as parseFooterCompositionV1, P as footerNavigationLayouts, Pn as unsafeAsFileChecksumSha256, Q as isPresent, Qn as err, Qt as mapReadonlyNonEmptyArray, R as isCanonicalRichTextDoc, Tn as unsafeAsBookingSessionId, Tt as parseBookingTimeZone, Un as unsafeAsSafeDownloadFilename, V as parseWeeklyOpeningHours, W as stableJsonStringify, Wn as unsafeAsSiteFontId, Xn as unsafeAsUploadSessionId, Xt as fromArray, Yn as unsafeAsStoragePath, Zn as unsafeAsUserId, Zt as mapNonEmptyArray, a as NAVIGATION_LOCATIONS, bn as parseSiteId, cn as assertNever, f as EVENT_PRESET_VALUES, gn as parseMediaAssetId, gt as localScheduleTimePattern, kn as unsafeAsDetectedMimeType, mn as parseEditorSessionGeneration, n as authoredValuesEqual, p as ORDINARY_EVENT_PRESET_VALUES, u as resolveSiteVerticalBookingConfig, wn as unsafeAsBookingRequirementId, yt as parseUtcIsoDateTimeString, zn as unsafeAsPositiveByteCount } from "./src-DsR6-gBQ.mjs";
|
|
3
3
|
import "node:module";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -771,8 +771,8 @@ function paletteColorsFromEntries(entries) {
|
|
|
771
771
|
}
|
|
772
772
|
};
|
|
773
773
|
}
|
|
774
|
-
const hex = normalizePaletteHex(entry.hex);
|
|
775
|
-
if (!hex) {
|
|
774
|
+
const hex$1 = normalizePaletteHex(entry.hex);
|
|
775
|
+
if (!hex$1) {
|
|
776
776
|
return {
|
|
777
777
|
ok: false,
|
|
778
778
|
error: {
|
|
@@ -782,7 +782,7 @@ function paletteColorsFromEntries(entries) {
|
|
|
782
782
|
}
|
|
783
783
|
};
|
|
784
784
|
}
|
|
785
|
-
values[role] = hex;
|
|
785
|
+
values[role] = hex$1;
|
|
786
786
|
}
|
|
787
787
|
const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === undefined);
|
|
788
788
|
if (missing.length > 0) {
|
|
@@ -869,8 +869,8 @@ const clamp01$2 = (x) => Math.max(0, Math.min(1, x));
|
|
|
869
869
|
const mod = (a, n) => (a % n + n) % n;
|
|
870
870
|
const deg = (x) => mod(x, 360);
|
|
871
871
|
const mix$1 = (a, b, t) => a + (b - a) * clamp01$2(t);
|
|
872
|
-
function hexToRgb(hex) {
|
|
873
|
-
const h = hex.replace(/^#/, "");
|
|
872
|
+
function hexToRgb(hex$1) {
|
|
873
|
+
const h = hex$1.replace(/^#/, "");
|
|
874
874
|
const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
875
875
|
if (m.length !== 6) throw new Error("Bad hex");
|
|
876
876
|
const int = parseInt(m, 16);
|
|
@@ -1015,8 +1015,8 @@ function oklchToHexGamut(L, C, h) {
|
|
|
1015
1015
|
const { r, g, b: bb } = oklabToSrgb(L, a, b);
|
|
1016
1016
|
return rgbToHex(r, g, bb);
|
|
1017
1017
|
}
|
|
1018
|
-
function featuresFromHex(hex) {
|
|
1019
|
-
const { r, g, b } = hexToRgb(hex);
|
|
1018
|
+
function featuresFromHex(hex$1) {
|
|
1019
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
1020
1020
|
const { h, s, l } = rgbToHsl(r, g, b);
|
|
1021
1021
|
const { L, a, b: b2 } = srgbToOklab(r, g, b);
|
|
1022
1022
|
const { L: okL, C: okC, h: okH } = oklabToOklch(L, a, b2);
|
|
@@ -1037,7 +1037,7 @@ function featuresFromHex(hex) {
|
|
|
1037
1037
|
okL,
|
|
1038
1038
|
okC,
|
|
1039
1039
|
okH,
|
|
1040
|
-
hex
|
|
1040
|
+
hex: hex$1
|
|
1041
1041
|
};
|
|
1042
1042
|
}
|
|
1043
1043
|
function hueRotate(h, degrees) {
|
|
@@ -1130,19 +1130,19 @@ function preparePaletteColourIndex(palette) {
|
|
|
1130
1130
|
return Object.freeze(Object.fromEntries(colours.flatMap(colourIndexEntry)));
|
|
1131
1131
|
}
|
|
1132
1132
|
/** An unused unsupported colour must not turn preparation into an eager validation failure. */
|
|
1133
|
-
function colourIndexEntry(hex) {
|
|
1133
|
+
function colourIndexEntry(hex$1) {
|
|
1134
1134
|
try {
|
|
1135
|
-
return [[hex, colourLuminance(hex)]];
|
|
1135
|
+
return [[hex$1, colourLuminance(hex$1)]];
|
|
1136
1136
|
} catch {
|
|
1137
1137
|
return [];
|
|
1138
1138
|
}
|
|
1139
1139
|
}
|
|
1140
|
-
function colourLuminance(hex) {
|
|
1141
|
-
const { r, g, b } = hexToRgb(hex);
|
|
1140
|
+
function colourLuminance(hex$1) {
|
|
1141
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
1142
1142
|
return relativeLuminance(r, g, b);
|
|
1143
1143
|
}
|
|
1144
|
-
function indexedLuminance(hex, index) {
|
|
1145
|
-
return (index && Object.prototype.hasOwnProperty.call(index, hex) ? index[hex] : undefined) ?? colourLuminance(hex);
|
|
1144
|
+
function indexedLuminance(hex$1, index) {
|
|
1145
|
+
return (index && Object.prototype.hasOwnProperty.call(index, hex$1) ? index[hex$1] : undefined) ?? colourLuminance(hex$1);
|
|
1146
1146
|
}
|
|
1147
1147
|
/**
|
|
1148
1148
|
* A closed luminance range with `lo <= hi`. The private field means only
|
|
@@ -1165,12 +1165,12 @@ function luminanceInterval(lo, hi = lo) {
|
|
|
1165
1165
|
return new Interval(lo, hi);
|
|
1166
1166
|
}
|
|
1167
1167
|
/** The relative luminance of a hex colour, read from the prepared index when given. */
|
|
1168
|
-
function hexLuminance(hex, index) {
|
|
1169
|
-
return toLuminance(indexedLuminance(hex, index));
|
|
1168
|
+
function hexLuminance(hex$1, index) {
|
|
1169
|
+
return toLuminance(indexedLuminance(hex$1, index));
|
|
1170
1170
|
}
|
|
1171
1171
|
/** The paint of a solid hex ground: a single point. */
|
|
1172
|
-
function solidPaint(hex, index) {
|
|
1173
|
-
const y = hexLuminance(hex, index);
|
|
1172
|
+
function solidPaint(hex$1, index) {
|
|
1173
|
+
const y = hexLuminance(hex$1, index);
|
|
1174
1174
|
return new Interval(y, y);
|
|
1175
1175
|
}
|
|
1176
1176
|
function luminanceRatio(a, b) {
|
|
@@ -1272,8 +1272,8 @@ function compositeHex$1(topHex, alpha, bottomHex) {
|
|
|
1272
1272
|
function worstCaseScrimContrast(input) {
|
|
1273
1273
|
const overWhite = compositeHex$1(input.scrimHex, input.alpha, WHITE);
|
|
1274
1274
|
const overBlack = compositeHex$1(input.scrimHex, input.alpha, BLACK);
|
|
1275
|
-
const luminance = (hex) => {
|
|
1276
|
-
const { r, g, b } = hexToRgb(hex);
|
|
1275
|
+
const luminance = (hex$1) => {
|
|
1276
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
1277
1277
|
return relativeLuminance(r, g, b);
|
|
1278
1278
|
};
|
|
1279
1279
|
const foreground = luminance(input.foregroundHex);
|
|
@@ -1303,9 +1303,9 @@ function gradientScrimSamples(gradient, alpha, palette) {
|
|
|
1303
1303
|
alpha: alpha * (stop[4] === undefined ? 1 : Number(stop[4]))
|
|
1304
1304
|
}));
|
|
1305
1305
|
const tokens = [...gradient.matchAll(/var\(--tb-([\w-]+)\)/g)].flatMap((stop) => {
|
|
1306
|
-
const hex = stop[1] ? resolveConcreteColorHex(stop[1], palette) : null;
|
|
1307
|
-
return hex ? [{
|
|
1308
|
-
hex,
|
|
1306
|
+
const hex$1 = stop[1] ? resolveConcreteColorHex(stop[1], palette) : null;
|
|
1307
|
+
return hex$1 ? [{
|
|
1308
|
+
hex: hex$1,
|
|
1309
1309
|
alpha
|
|
1310
1310
|
}] : [];
|
|
1311
1311
|
});
|
|
@@ -1421,7 +1421,7 @@ function generateShades(hexColor, name) {
|
|
|
1421
1421
|
|
|
1422
1422
|
//#endregion
|
|
1423
1423
|
//#region ../theme-core/src/palette/neutralScale.ts
|
|
1424
|
-
function generateNeutralScale({ background, steps = 11, warm = false }) {
|
|
1424
|
+
function generateNeutralScale({ background, steps: steps$1 = 11, warm = false }) {
|
|
1425
1425
|
const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
|
|
1426
1426
|
const shadeNames = [
|
|
1427
1427
|
50,
|
|
@@ -1436,17 +1436,17 @@ function generateNeutralScale({ background, steps = 11, warm = false }) {
|
|
|
1436
1436
|
900,
|
|
1437
1437
|
950
|
|
1438
1438
|
];
|
|
1439
|
-
const count = Math.min(steps, shadeNames.length);
|
|
1439
|
+
const count = Math.min(steps$1, shadeNames.length);
|
|
1440
1440
|
const scale$1 = [];
|
|
1441
1441
|
for (let i = 0; i < count; i++) {
|
|
1442
1442
|
const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
|
|
1443
1443
|
const t = i / (count - 1);
|
|
1444
1444
|
const tone = isDark ? mix$2(.06, .92, t) : mix$2(.98, .1, t);
|
|
1445
|
-
const hex = oklchToHexGamut(tone, .02, warm ? 85 : 250);
|
|
1445
|
+
const hex$1 = oklchToHexGamut(tone, .02, warm ? 85 : 250);
|
|
1446
1446
|
const name = `neutral-${shadeName}`;
|
|
1447
1447
|
scale$1.push({
|
|
1448
1448
|
name,
|
|
1449
|
-
hex
|
|
1449
|
+
hex: hex$1
|
|
1450
1450
|
});
|
|
1451
1451
|
}
|
|
1452
1452
|
return scale$1;
|
|
@@ -1488,9 +1488,9 @@ function colorsToMap$1(palette) {
|
|
|
1488
1488
|
}
|
|
1489
1489
|
return map;
|
|
1490
1490
|
}
|
|
1491
|
-
function isBackgroundDark(hex) {
|
|
1491
|
+
function isBackgroundDark(hex$1) {
|
|
1492
1492
|
try {
|
|
1493
|
-
return featuresFromHex(hex).Y < .5;
|
|
1493
|
+
return featuresFromHex(hex$1).Y < .5;
|
|
1494
1494
|
} catch {
|
|
1495
1495
|
return false;
|
|
1496
1496
|
}
|
|
@@ -1537,10 +1537,10 @@ function expandBasePalette(palette) {
|
|
|
1537
1537
|
neutral$1.forEach((n) => {
|
|
1538
1538
|
expanded[n.name] = n.hex;
|
|
1539
1539
|
});
|
|
1540
|
-
for (const [name, hex] of Object.entries(base)) {
|
|
1540
|
+
for (const [name, hex$1] of Object.entries(base)) {
|
|
1541
1541
|
if (nonShadeablePaletteRoleNames.has(name)) continue;
|
|
1542
|
-
if (hex) {
|
|
1543
|
-
const shades = generateShades(hex, name);
|
|
1542
|
+
if (hex$1) {
|
|
1543
|
+
const shades = generateShades(hex$1, name);
|
|
1544
1544
|
shades.forEach((shade) => {
|
|
1545
1545
|
expanded[shade.name] = shade.hex;
|
|
1546
1546
|
});
|
|
@@ -1554,11 +1554,11 @@ function expandBasePalette(palette) {
|
|
|
1554
1554
|
const isDark = base.background ? isBackgroundDark(base.background) : false;
|
|
1555
1555
|
const isDarkMode = palette.meta?.mode === "dark" || isDark;
|
|
1556
1556
|
expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
|
|
1557
|
-
for (const [token, hex] of Object.entries({ ...expanded })) {
|
|
1558
|
-
if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
|
|
1557
|
+
for (const [token, hex$1] of Object.entries({ ...expanded })) {
|
|
1558
|
+
if (!hex$1 || !isOnColorBackgroundCandidateToken(token)) continue;
|
|
1559
1559
|
const onToken = onColorTokenName(token);
|
|
1560
1560
|
if (expanded[onToken]) continue;
|
|
1561
|
-
expanded[onToken] = getTextColorForBackground(hex);
|
|
1561
|
+
expanded[onToken] = getTextColorForBackground(hex$1);
|
|
1562
1562
|
}
|
|
1563
1563
|
return expanded;
|
|
1564
1564
|
}
|
|
@@ -1589,7 +1589,7 @@ function firstContrastBlend$1(from, to, ground, threshold) {
|
|
|
1589
1589
|
return Array.from({ length: 101 }, (_, index) => {
|
|
1590
1590
|
const amount = index / 100;
|
|
1591
1591
|
return rgbToHex(mix$1(start.r, end.r, amount), mix$1(start.g, end.g, amount), mix$1(start.b, end.b, amount));
|
|
1592
|
-
}).find((hex) => contrastRatio(hex, ground) >= threshold) ?? to;
|
|
1592
|
+
}).find((hex$1) => contrastRatio(hex$1, ground) >= threshold) ?? to;
|
|
1593
1593
|
}
|
|
1594
1594
|
/** `markSource` names the colour surface marks are derived from when a style marks in its brand rather than its accent. */
|
|
1595
1595
|
function arrangementPaletteTokens(base, options) {
|
|
@@ -1600,100 +1600,6 @@ function arrangementPaletteTokens(base, options) {
|
|
|
1600
1600
|
};
|
|
1601
1601
|
}
|
|
1602
1602
|
|
|
1603
|
-
//#endregion
|
|
1604
|
-
//#region ../theme-core/src/surfaces/foregroundContract.ts
|
|
1605
|
-
const SURFACE_FOREGROUND_CSS_VARS = {
|
|
1606
|
-
text: "--rb-surface-text-color",
|
|
1607
|
-
heading: "--rb-surface-heading-color",
|
|
1608
|
-
link: "--rb-surface-link-color",
|
|
1609
|
-
linkHover: "--rb-surface-link-hover-color",
|
|
1610
|
-
supporting: "--rb-surface-supporting-color",
|
|
1611
|
-
muted: "--rb-surface-muted-color",
|
|
1612
|
-
line: "--rb-surface-line-color",
|
|
1613
|
-
mark: "--rb-surface-mark-color"
|
|
1614
|
-
};
|
|
1615
|
-
const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
|
|
1616
|
-
const DEFAULT_SURFACE_LINK_COLOR = "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))";
|
|
1617
|
-
const DEFAULT_SURFACE_SUPPORTING_COLOR = "rgb(var(--tb-mutedText, 100 116 139))";
|
|
1618
|
-
const DEFAULT_ROLE_FALLBACKS = {
|
|
1619
|
-
text: DEFAULT_SURFACE_TEXT_COLOR,
|
|
1620
|
-
heading: DEFAULT_SURFACE_TEXT_COLOR,
|
|
1621
|
-
link: DEFAULT_SURFACE_LINK_COLOR,
|
|
1622
|
-
linkHover: DEFAULT_SURFACE_LINK_COLOR,
|
|
1623
|
-
supporting: DEFAULT_SURFACE_SUPPORTING_COLOR,
|
|
1624
|
-
muted: DEFAULT_SURFACE_SUPPORTING_COLOR
|
|
1625
|
-
};
|
|
1626
|
-
function requireSurfaceColorValue(role, value) {
|
|
1627
|
-
const normalized = value.trim();
|
|
1628
|
-
if (normalized.length === 0) {
|
|
1629
|
-
throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
|
|
1630
|
-
}
|
|
1631
|
-
return normalized;
|
|
1632
|
-
}
|
|
1633
|
-
function completeSurfaceForegrounds(input) {
|
|
1634
|
-
const text = requireSurfaceColorValue("text", input.text);
|
|
1635
|
-
const link = input.link === undefined ? text : requireSurfaceColorValue("link", input.link);
|
|
1636
|
-
return {
|
|
1637
|
-
text,
|
|
1638
|
-
heading: input.heading === undefined ? text : requireSurfaceColorValue("heading", input.heading),
|
|
1639
|
-
link,
|
|
1640
|
-
linkHover: input.linkHover === undefined ? link : requireSurfaceColorValue("linkHover", input.linkHover),
|
|
1641
|
-
muted: input.muted === undefined ? text : requireSurfaceColorValue("muted", input.muted),
|
|
1642
|
-
supporting: input.supporting === undefined ? text : requireSurfaceColorValue("supporting", input.supporting)
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
|
|
1646
|
-
const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
|
|
1647
|
-
const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
|
|
1648
|
-
if (role === "text") {
|
|
1649
|
-
return `var(${roleVariable}, ${fallback})`;
|
|
1650
|
-
}
|
|
1651
|
-
if (role === "linkHover") {
|
|
1652
|
-
return `var(${roleVariable}, ${surfaceForegroundValue("link", fallback)})`;
|
|
1653
|
-
}
|
|
1654
|
-
return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
|
|
1655
|
-
}
|
|
1656
|
-
function surfaceForegroundValueWithOpacity(role, opacityPercent) {
|
|
1657
|
-
if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) {
|
|
1658
|
-
throw new RangeError("Surface foreground opacity must be between 0 and 100.");
|
|
1659
|
-
}
|
|
1660
|
-
return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
|
|
1661
|
-
}
|
|
1662
|
-
function surfaceForegroundStyle(input) {
|
|
1663
|
-
const foregrounds = completeSurfaceForegrounds(input);
|
|
1664
|
-
return {
|
|
1665
|
-
color: foregrounds.text,
|
|
1666
|
-
[SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
|
|
1667
|
-
[SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
|
|
1668
|
-
[SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
|
|
1669
|
-
[SURFACE_FOREGROUND_CSS_VARS.linkHover]: foregrounds.linkHover,
|
|
1670
|
-
[SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting,
|
|
1671
|
-
...input.muted === undefined ? {} : { [SURFACE_FOREGROUND_CSS_VARS.muted]: foregrounds.muted },
|
|
1672
|
-
"--rb-surface-link-hover-decoration": hoverDecoration(input)
|
|
1673
|
-
};
|
|
1674
|
-
}
|
|
1675
|
-
function surfaceForegroundDeclarations(input) {
|
|
1676
|
-
return Object.entries(surfaceForegroundStyle(input)).map(([property, value]) => `${property}: ${value};`);
|
|
1677
|
-
}
|
|
1678
|
-
function surfaceForegroundInputFromResolved(foreground, color) {
|
|
1679
|
-
const { roles } = foreground;
|
|
1680
|
-
const value = (ink) => {
|
|
1681
|
-
const token = parseThemeColorToken(ink);
|
|
1682
|
-
return token ? color(token) : ink;
|
|
1683
|
-
};
|
|
1684
|
-
return {
|
|
1685
|
-
text: value(roles.text),
|
|
1686
|
-
heading: value(roles.heading),
|
|
1687
|
-
link: value(roles.link),
|
|
1688
|
-
linkHover: value(roles.linkHover),
|
|
1689
|
-
supporting: value(roles.supporting),
|
|
1690
|
-
hoverDecoration: foreground.hoverDecoration
|
|
1691
|
-
};
|
|
1692
|
-
}
|
|
1693
|
-
function hoverDecoration(input) {
|
|
1694
|
-
return input.hoverDecoration ?? "underline";
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
1603
|
//#endregion
|
|
1698
1604
|
//#region ../theme-core/src/tokens/tokens.ts
|
|
1699
1605
|
const colorTokens = [
|
|
@@ -1886,6 +1792,209 @@ function cloneThemeTokens(source) {
|
|
|
1886
1792
|
return JSON.parse(JSON.stringify(source));
|
|
1887
1793
|
}
|
|
1888
1794
|
|
|
1795
|
+
//#endregion
|
|
1796
|
+
//#region ../theme-core/src/tokens/resolver.ts
|
|
1797
|
+
/**
|
|
1798
|
+
* Resolves theme tokens to CSS variables
|
|
1799
|
+
*
|
|
1800
|
+
* This resolver ensures all button styles use theme tokens (not hardcoded colors)
|
|
1801
|
+
* so that changing the palette automatically updates all buttons.
|
|
1802
|
+
*/
|
|
1803
|
+
var TokenResolver = class {
|
|
1804
|
+
constructor(theme) {
|
|
1805
|
+
this.theme = theme;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Resolve a color token to a CSS variable reference wrapped in rgb()
|
|
1809
|
+
*
|
|
1810
|
+
* @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
|
|
1811
|
+
* @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
|
|
1812
|
+
*
|
|
1813
|
+
* Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
|
|
1814
|
+
* so we must wrap them with rgb() to create a valid color value.
|
|
1815
|
+
*
|
|
1816
|
+
* @example
|
|
1817
|
+
* resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
|
|
1818
|
+
* resolver.getColor('white') // => 'rgb(var(--tb-white))'
|
|
1819
|
+
*/
|
|
1820
|
+
getColor(token) {
|
|
1821
|
+
if (!this.tokenExists(token)) {
|
|
1822
|
+
console.warn(`[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`);
|
|
1823
|
+
}
|
|
1824
|
+
return rgbColorValue(token);
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Adjust a color token's shade by a given amount
|
|
1828
|
+
*
|
|
1829
|
+
* @param token - Base token (e.g., 'primary-500')
|
|
1830
|
+
* @param amount - Shade adjustment (+100 = darker, -100 = lighter)
|
|
1831
|
+
* @returns Adjusted token name (e.g., 'primary-600')
|
|
1832
|
+
*
|
|
1833
|
+
* @example
|
|
1834
|
+
* resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
|
|
1835
|
+
* resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
|
|
1836
|
+
*/
|
|
1837
|
+
adjustShade(token, amount) {
|
|
1838
|
+
const parts = token.split("-");
|
|
1839
|
+
if (parts.length < 2) {
|
|
1840
|
+
const newShade$1 = Math.max(100, Math.min(900, 500 + amount));
|
|
1841
|
+
return `${token}-${newShade$1}`;
|
|
1842
|
+
}
|
|
1843
|
+
const base = parts.slice(0, -1).join("-");
|
|
1844
|
+
const shade = parseInt(parts[parts.length - 1] ?? "", 10);
|
|
1845
|
+
if (isNaN(shade)) {
|
|
1846
|
+
console.warn(`[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`);
|
|
1847
|
+
return token;
|
|
1848
|
+
}
|
|
1849
|
+
const newShade = Math.max(100, Math.min(900, shade + amount));
|
|
1850
|
+
return `${base}-${newShade}`;
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Check if a token exists in the theme palette
|
|
1854
|
+
*
|
|
1855
|
+
* @param token - Token name to check
|
|
1856
|
+
* @returns True if token exists in theme
|
|
1857
|
+
*/
|
|
1858
|
+
tokenExists(token) {
|
|
1859
|
+
const commonTokens = [
|
|
1860
|
+
"white",
|
|
1861
|
+
"black",
|
|
1862
|
+
"transparent"
|
|
1863
|
+
];
|
|
1864
|
+
if (commonTokens.includes(token)) {
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
const parts = token.split("-");
|
|
1868
|
+
if (parts.length >= 2) {
|
|
1869
|
+
const lastPart = parts[parts.length - 1] ?? "";
|
|
1870
|
+
const shade = parseInt(lastPart, 10);
|
|
1871
|
+
if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
|
|
1879
|
+
//#endregion
|
|
1880
|
+
//#region ../theme-core/src/surfaces/foregroundInteraction.ts
|
|
1881
|
+
/**
|
|
1882
|
+
* How far a hover state softens away from its resting foreground, as a
|
|
1883
|
+
* percentage of full opacity.
|
|
1884
|
+
*
|
|
1885
|
+
* Softening — rather than brightening — is forced by the token vocabulary
|
|
1886
|
+
* rather than chosen for taste. A resolved foreground is frequently an
|
|
1887
|
+
* `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
|
|
1888
|
+
* with no shade ramp: over a primary-painted section the resolved text role
|
|
1889
|
+
* is `onPrimary`, and white has no headroom left to move further from its
|
|
1890
|
+
* background. Moving *toward* the mid-range always has headroom, whatever
|
|
1891
|
+
* the resting colour, and it keeps the hue exactly.
|
|
1892
|
+
*
|
|
1893
|
+
* 85% is the measured floor, not a round number. Softening costs contrast, and
|
|
1894
|
+
* the binding case is a scrolled overlay header: `contrast-glass` paints 60%
|
|
1895
|
+
* black or white, which composites the backdrop into a known band whatever the
|
|
1896
|
+
* page content underneath happens to be. The lightest backdrop a dark glass can
|
|
1897
|
+
* present is 60% black over white, `#666666`; a white foreground softened to
|
|
1898
|
+
* 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
|
|
1899
|
+
* 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
|
|
1900
|
+
* the large-text 3:1.
|
|
1901
|
+
*
|
|
1902
|
+
* The mirror case — a light glass over dark content, `#999999` — cannot be met
|
|
1903
|
+
* by any softening, because the *resting* colour is already only about 4.6:1
|
|
1904
|
+
* there. That is a property of the glass rather than of this derivation, and
|
|
1905
|
+
* it wants fixing where the glass tone is chosen.
|
|
1906
|
+
*/
|
|
1907
|
+
const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
|
|
1908
|
+
/**
|
|
1909
|
+
* Derive the hover foreground that belongs with a resolved resting foreground.
|
|
1910
|
+
*
|
|
1911
|
+
* A resting foreground is chosen to be legible against a known surface. A hover
|
|
1912
|
+
* state derived from it stays within a bounded distance of that colour, so it
|
|
1913
|
+
* inherits most of the resting colour's contrast margin instead of asserting
|
|
1914
|
+
* an unrelated brand colour that no one checked against the surface.
|
|
1915
|
+
*
|
|
1916
|
+
* That bounded-distance property is what lets this be used on a transparent
|
|
1917
|
+
* overlay header, where the backdrop is only partly knowable: the scrolled
|
|
1918
|
+
* state composites a 60% glass tint over arbitrary page content, so no
|
|
1919
|
+
* compile-time contrast proof is available there. Staying near a colour that
|
|
1920
|
+
* is already trusted in both states is a weaker claim than a contrast proof,
|
|
1921
|
+
* and it is one this function can actually keep.
|
|
1922
|
+
*
|
|
1923
|
+
* Returns `null` when the resting token is not a plain palette token — an
|
|
1924
|
+
* already-faded or raw value has no well-defined softening, and the caller
|
|
1925
|
+
* should leave hover alone rather than guess.
|
|
1926
|
+
*/
|
|
1927
|
+
function deriveForegroundHoverToken(restingToken) {
|
|
1928
|
+
const parts = parseColorTokenParts(restingToken);
|
|
1929
|
+
if (parts.kind !== "token") return null;
|
|
1930
|
+
if (parts.opacityPercent !== null) return null;
|
|
1931
|
+
return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* The opacity `contrast-glass` paints at. Mirrors the value in
|
|
1935
|
+
* `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
|
|
1936
|
+
* point of the bound below is that it describes what that treatment can produce.
|
|
1937
|
+
*/
|
|
1938
|
+
const CONTRAST_GLASS_ALPHA = .6;
|
|
1939
|
+
/**
|
|
1940
|
+
* The extreme backdrop a glass tone can present, whatever page content happens
|
|
1941
|
+
* to be underneath it.
|
|
1942
|
+
*
|
|
1943
|
+
* A 60% tint clamps the composite into a known band, which is what makes a
|
|
1944
|
+
* conservative check possible at compile time even though the content passing
|
|
1945
|
+
* under a sticky header is not knowable. For a dark glass the composite is
|
|
1946
|
+
* lightest over white; for a light glass it is darkest over black. Those are
|
|
1947
|
+
* the two ends that a foreground has to survive.
|
|
1948
|
+
*/
|
|
1949
|
+
function worstCaseGlassBackdrop(tone) {
|
|
1950
|
+
const [over, tint$1] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
|
|
1951
|
+
return compositeHex(tint$1, over, CONTRAST_GLASS_ALPHA);
|
|
1952
|
+
}
|
|
1953
|
+
/** Flatten `foreground` at `alpha` over `background` into an opaque hex. */
|
|
1954
|
+
function compositeHex(foreground, background, alpha) {
|
|
1955
|
+
const f = hexToRgb(foreground);
|
|
1956
|
+
const b = hexToRgb(background);
|
|
1957
|
+
return rgbToHex(Math.round(mix$1(b.r, f.r, alpha)), Math.round(mix$1(b.g, f.g, alpha)), Math.round(mix$1(b.b, f.b, alpha)));
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Prefer an authored hover that survives every measured backdrop, then a
|
|
1961
|
+
* checked softening of the resting colour. A null backdrop set represents a
|
|
1962
|
+
* trusted treatment: only an explicit authored hover may change the colour.
|
|
1963
|
+
* When neither option works, retain the resting colour and underline it.
|
|
1964
|
+
*/
|
|
1965
|
+
function planForegroundHover(input) {
|
|
1966
|
+
const { restingToken, candidate: candidate$1, palette, backdrops } = input;
|
|
1967
|
+
if (candidate$1 && String(candidate$1) !== String(restingToken)) {
|
|
1968
|
+
if (backdrops === null || survivesEveryBackdrop(candidate$1, palette, backdrops)) {
|
|
1969
|
+
return {
|
|
1970
|
+
kind: "candidate",
|
|
1971
|
+
token: candidate$1,
|
|
1972
|
+
decoration: "initial"
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
const resting = parseThemeColorToken(restingToken);
|
|
1977
|
+
const softened = resting ? deriveForegroundHoverToken(resting) : null;
|
|
1978
|
+
if (softened && backdrops && survivesEveryBackdrop(softened, palette, backdrops)) {
|
|
1979
|
+
return {
|
|
1980
|
+
kind: "softened",
|
|
1981
|
+
token: softened,
|
|
1982
|
+
decoration: "initial"
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
return {
|
|
1986
|
+
kind: "resting",
|
|
1987
|
+
token: restingToken,
|
|
1988
|
+
decoration: "underline"
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
function survivesEveryBackdrop(token, palette, backdrops) {
|
|
1992
|
+
return backdrops.length > 0 && backdrops.every((backdrop) => {
|
|
1993
|
+
const foreground = resolveOpaqueSurfaceHex(token, palette, backdrop);
|
|
1994
|
+
return foreground !== null && contrastRatio(foreground, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO;
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1889
1998
|
//#endregion
|
|
1890
1999
|
//#region ../theme-core/src/surfaces/foregroundRoles.ts
|
|
1891
2000
|
const SURFACE_FOREGROUND_ROLES = [
|
|
@@ -1897,6 +2006,425 @@ const SURFACE_FOREGROUND_ROLES = [
|
|
|
1897
2006
|
"muted"
|
|
1898
2007
|
];
|
|
1899
2008
|
|
|
2009
|
+
//#endregion
|
|
2010
|
+
//#region ../theme-core/src/palette/family.ts
|
|
2011
|
+
/**
|
|
2012
|
+
* Only `colourFamily` makes one (the class is not exported), so a caller can't
|
|
2013
|
+
* swap in its own members and still hand the solver a valid family. That
|
|
2014
|
+
* matters because `unpaintable` relies on the members running from black to
|
|
2015
|
+
* white.
|
|
2016
|
+
*/
|
|
2017
|
+
var Family = class {
|
|
2018
|
+
#members;
|
|
2019
|
+
constructor(seed, members) {
|
|
2020
|
+
this.seed = seed;
|
|
2021
|
+
this.#members = Object.freeze(members);
|
|
2022
|
+
}
|
|
2023
|
+
/** Unique by hex, ascending luminance. Includes the exact seed, black and white. */
|
|
2024
|
+
get members() {
|
|
2025
|
+
return this.#members;
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
/** Lightness steps between black and white. */
|
|
2029
|
+
const FAMILY_STEPS = 512;
|
|
2030
|
+
const GAMUT_BISECTION_STEPS = 18;
|
|
2031
|
+
/** The highest chroma at or below `c` that stays inside sRGB at this lightness and hue. */
|
|
2032
|
+
function gamutChroma(l, c, h) {
|
|
2033
|
+
if (inLinearSrgbGamut(oklchToLinearSrgb(l, c, h))) return c;
|
|
2034
|
+
return Array.from({ length: GAMUT_BISECTION_STEPS }).reduce(({ lo, hi }) => {
|
|
2035
|
+
const mid = (lo + hi) / 2;
|
|
2036
|
+
return inLinearSrgbGamut(oklchToLinearSrgb(l, mid, h)) ? {
|
|
2037
|
+
lo: mid,
|
|
2038
|
+
hi
|
|
2039
|
+
} : {
|
|
2040
|
+
lo,
|
|
2041
|
+
hi: mid
|
|
2042
|
+
};
|
|
2043
|
+
}, {
|
|
2044
|
+
lo: 0,
|
|
2045
|
+
hi: c
|
|
2046
|
+
}).lo;
|
|
2047
|
+
}
|
|
2048
|
+
function oklabDistance(a, b) {
|
|
2049
|
+
return Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b);
|
|
2050
|
+
}
|
|
2051
|
+
function colourFamily(seedHex) {
|
|
2052
|
+
if (!/^#[0-9a-f]{6}$/i.test(seedHex)) throw new Error(`Expected an opaque #rrggbb colour: ${seedHex}`);
|
|
2053
|
+
const features = featuresFromHex(seedHex.toLowerCase());
|
|
2054
|
+
const seed = member(seedHex.toLowerCase(), {
|
|
2055
|
+
l: features.okL,
|
|
2056
|
+
c: features.okC,
|
|
2057
|
+
h: features.okH
|
|
2058
|
+
});
|
|
2059
|
+
const seedTaper = Math.max(.001, Math.sin(Math.PI * seed.sample.l));
|
|
2060
|
+
const samples = Array.from({ length: FAMILY_STEPS + 1 }, (_, i) => {
|
|
2061
|
+
const l = i / FAMILY_STEPS;
|
|
2062
|
+
const c = gamutChroma(l, seed.sample.c * Math.min(1, Math.sin(Math.PI * l) / seedTaper), seed.sample.h);
|
|
2063
|
+
return sampleMember({
|
|
2064
|
+
l,
|
|
2065
|
+
c,
|
|
2066
|
+
h: seed.sample.h
|
|
2067
|
+
});
|
|
2068
|
+
});
|
|
2069
|
+
const unique = new Map([seed, ...samples].map((candidate$1) => [candidate$1.hex, candidate$1]).reverse());
|
|
2070
|
+
const members = [...unique.values()].sort((a, b) => a.y - b.y || (a.hex < b.hex ? -1 : a.hex > b.hex ? 1 : 0));
|
|
2071
|
+
return new Family(seed, members);
|
|
2072
|
+
}
|
|
2073
|
+
function sampleMember(sample) {
|
|
2074
|
+
const { r, g, b } = oklchToLinearSrgb(sample.l, sample.c, sample.h);
|
|
2075
|
+
return member(rgbToHex(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b)), sample);
|
|
2076
|
+
}
|
|
2077
|
+
function member(hex$1, sample) {
|
|
2078
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
2079
|
+
return {
|
|
2080
|
+
hex: hex$1,
|
|
2081
|
+
y: hexLuminance(hex$1),
|
|
2082
|
+
oklab: srgbToOklab(r, g, b),
|
|
2083
|
+
sample
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
//#endregion
|
|
2088
|
+
//#region ../theme-core/src/palette/utils/inkSolver.ts
|
|
2089
|
+
/**
|
|
2090
|
+
* How far (OKLab ΔE) a brand colour drawn as ink may be nudged along its own
|
|
2091
|
+
* family before it stops reading as that colour. Past this the caller uses
|
|
2092
|
+
* neutral or palette text instead (Will, 2026-10-14).
|
|
2093
|
+
*/
|
|
2094
|
+
const BRAND_INK_MAX_DELTA_E = .08;
|
|
2095
|
+
/**
|
|
2096
|
+
* The seed itself when it clears `paint`, otherwise the family member nearest
|
|
2097
|
+
* the seed (OKLab ΔE) that clears. On a light ground that is darker, on a dark
|
|
2098
|
+
* one lighter. A passing member further than `maxDistance` from the seed is
|
|
2099
|
+
* refused as `too-far`, which is distinct from `unpaintable`: the family runs
|
|
2100
|
+
* from black to white, so if no member clears, neither does black or white.
|
|
2101
|
+
*/
|
|
2102
|
+
function inkFrom(family, paint, ratio$1, maxDistance = BRAND_INK_MAX_DELTA_E) {
|
|
2103
|
+
assertContrastRatio(ratio$1);
|
|
2104
|
+
if (!Number.isFinite(maxDistance) || maxDistance < 0) {
|
|
2105
|
+
throw new RangeError(`Maximum ink distance must be finite and not negative: ${maxDistance}`);
|
|
2106
|
+
}
|
|
2107
|
+
const seedRatio = worstContrast(family.seed.y, paint);
|
|
2108
|
+
if (seedRatio >= ratio$1) return {
|
|
2109
|
+
kind: "ink",
|
|
2110
|
+
hex: family.seed.hex,
|
|
2111
|
+
ratio: seedRatio,
|
|
2112
|
+
source: "exact"
|
|
2113
|
+
};
|
|
2114
|
+
const nearest = family.members.reduce((best, member$1) => {
|
|
2115
|
+
const achieved = worstContrast(member$1.y, paint);
|
|
2116
|
+
if (achieved < ratio$1) return best;
|
|
2117
|
+
const distance$1 = oklabDistance(member$1.oklab, family.seed.oklab);
|
|
2118
|
+
return best === null || distance$1 < best.distance ? {
|
|
2119
|
+
hex: member$1.hex,
|
|
2120
|
+
distance: distance$1,
|
|
2121
|
+
ratio: achieved
|
|
2122
|
+
} : best;
|
|
2123
|
+
}, null);
|
|
2124
|
+
if (nearest === null) return {
|
|
2125
|
+
kind: "unpaintable",
|
|
2126
|
+
interval: paint,
|
|
2127
|
+
ratio: ratio$1
|
|
2128
|
+
};
|
|
2129
|
+
if (nearest.distance > maxDistance) {
|
|
2130
|
+
return {
|
|
2131
|
+
kind: "too-far",
|
|
2132
|
+
nearest: {
|
|
2133
|
+
hex: nearest.hex,
|
|
2134
|
+
distance: nearest.distance
|
|
2135
|
+
},
|
|
2136
|
+
maxDistance
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
kind: "ink",
|
|
2141
|
+
hex: nearest.hex,
|
|
2142
|
+
ratio: nearest.ratio,
|
|
2143
|
+
source: "family"
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* Neutral text for a paint. The style's preferred neutral wins when one of its
|
|
2148
|
+
* pair clears (the higher-contrast one, dark on a tie); otherwise literal black
|
|
2149
|
+
* or white by `maxContrastBlackOrWhite`; otherwise `unpaintable`.
|
|
2150
|
+
*/
|
|
2151
|
+
function neutralInk(paint, ratio$1, preferred) {
|
|
2152
|
+
assertContrastRatio(ratio$1);
|
|
2153
|
+
if (preferred) {
|
|
2154
|
+
const dark = worstContrast(hexLuminance(preferred.dark), paint);
|
|
2155
|
+
const light = worstContrast(hexLuminance(preferred.light), paint);
|
|
2156
|
+
const [hex$1, achieved] = dark >= light ? [preferred.dark, dark] : [preferred.light, light];
|
|
2157
|
+
if (achieved >= ratio$1) return {
|
|
2158
|
+
kind: "ink",
|
|
2159
|
+
hex: hex$1,
|
|
2160
|
+
ratio: achieved,
|
|
2161
|
+
source: "neutral"
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
const literal = maxContrastBlackOrWhite(paint);
|
|
2165
|
+
return literal.ratio >= ratio$1 ? {
|
|
2166
|
+
kind: "ink",
|
|
2167
|
+
hex: literal.ink === "black" ? "#000000" : "#ffffff",
|
|
2168
|
+
ratio: literal.ratio,
|
|
2169
|
+
source: "neutral"
|
|
2170
|
+
} : {
|
|
2171
|
+
kind: "unpaintable",
|
|
2172
|
+
interval: paint,
|
|
2173
|
+
ratio: ratio$1
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
//#endregion
|
|
2178
|
+
//#region ../theme-core/src/surfaces/surfaceInks.ts
|
|
2179
|
+
const SURFACE_INK_ROLES = [
|
|
2180
|
+
"brand",
|
|
2181
|
+
"accent",
|
|
2182
|
+
"focus"
|
|
2183
|
+
];
|
|
2184
|
+
/** Lines and focus rings are graphics: WCAG non-text contrast. */
|
|
2185
|
+
const GRAPHIC_MIN_CONTRAST_RATIO = 3;
|
|
2186
|
+
const INK_SEEDS = {
|
|
2187
|
+
brand: {
|
|
2188
|
+
token: "primary",
|
|
2189
|
+
ratio: ORDINARY_TEXT_MIN_CONTRAST_RATIO
|
|
2190
|
+
},
|
|
2191
|
+
accent: {
|
|
2192
|
+
token: "accent",
|
|
2193
|
+
ratio: ORDINARY_TEXT_MIN_CONTRAST_RATIO
|
|
2194
|
+
},
|
|
2195
|
+
focus: {
|
|
2196
|
+
token: "primary",
|
|
2197
|
+
ratio: GRAPHIC_MIN_CONTRAST_RATIO
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
/**
|
|
2201
|
+
* Solve the three inks for a measured solid paint. `textInk` is the surface's
|
|
2202
|
+
* own (already certified) text ink: it stands in when the owner's colour is
|
|
2203
|
+
* missing, or can only clear the paint by drifting too far from itself.
|
|
2204
|
+
* A block owner can supply an authored shade candidate for a local ink.
|
|
2205
|
+
*/
|
|
2206
|
+
function solveSurfaceInks(backdropHex, textInk, palette, candidates = {}) {
|
|
2207
|
+
const paint = solidPaint(backdropHex);
|
|
2208
|
+
const fallback = literalInk(resolveConcreteColorHex(textInk, palette) ?? pickBlackOrWhiteTextColor(backdropHex));
|
|
2209
|
+
return {
|
|
2210
|
+
brand: solveInk({
|
|
2211
|
+
...INK_SEEDS.brand,
|
|
2212
|
+
token: candidates.brand ?? INK_SEEDS.brand.token
|
|
2213
|
+
}, paint, palette, fallback),
|
|
2214
|
+
accent: solveInk({
|
|
2215
|
+
...INK_SEEDS.accent,
|
|
2216
|
+
token: candidates.accent ?? INK_SEEDS.accent.token
|
|
2217
|
+
}, paint, palette, fallback),
|
|
2218
|
+
focus: solveInk({
|
|
2219
|
+
...INK_SEEDS.focus,
|
|
2220
|
+
token: candidates.focus ?? INK_SEEDS.focus.token
|
|
2221
|
+
}, paint, palette, fallback)
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
function solveInk(seed, paint, palette, fallback) {
|
|
2225
|
+
const token = parseThemeColorToken(seed.token);
|
|
2226
|
+
const seedHex = hexColorSchema.safeParse(resolveConcreteColorHex(seed.token, palette));
|
|
2227
|
+
if (!token || !seedHex.success) return fallback;
|
|
2228
|
+
const solved = inkFrom(familyOf(seedHex.data), paint, seed.ratio);
|
|
2229
|
+
switch (solved.kind) {
|
|
2230
|
+
case "ink": return solved.source === "exact" ? token : literalInk(solved.hex);
|
|
2231
|
+
case "too-far":
|
|
2232
|
+
case "unpaintable": return fallback;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
function literalInk(hex$1) {
|
|
2236
|
+
return hexColorSchema.parse(hex$1.toLowerCase());
|
|
2237
|
+
}
|
|
2238
|
+
const FAMILY_CACHE_LIMIT = 64;
|
|
2239
|
+
const families = new Map();
|
|
2240
|
+
function familyOf(seed) {
|
|
2241
|
+
const cached = families.get(seed);
|
|
2242
|
+
if (cached) return cached;
|
|
2243
|
+
if (families.size >= FAMILY_CACHE_LIMIT) families.clear();
|
|
2244
|
+
const family = colourFamily(seed);
|
|
2245
|
+
families.set(seed, family);
|
|
2246
|
+
return family;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
//#endregion
|
|
2250
|
+
//#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
|
|
2251
|
+
const FALLBACK_TEXT_TOKENS = [
|
|
2252
|
+
"text",
|
|
2253
|
+
"primaryForeground",
|
|
2254
|
+
"secondaryForeground",
|
|
2255
|
+
"accentForeground",
|
|
2256
|
+
"neutral-950",
|
|
2257
|
+
"neutral-50"
|
|
2258
|
+
];
|
|
2259
|
+
const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
|
|
2260
|
+
"text",
|
|
2261
|
+
"primaryForeground",
|
|
2262
|
+
"secondaryForeground",
|
|
2263
|
+
"accentForeground",
|
|
2264
|
+
"onPrimary",
|
|
2265
|
+
"onSecondary",
|
|
2266
|
+
"onAccent",
|
|
2267
|
+
"onSurface",
|
|
2268
|
+
"neutral-950",
|
|
2269
|
+
"neutral-50"
|
|
2270
|
+
];
|
|
2271
|
+
function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
|
|
2272
|
+
const authored = foregroundCandidates(background, candidates);
|
|
2273
|
+
if ("measuredBackdrop" in options) return resolveMeasuredForeground(background?.color ?? "background", options.measuredBackdrop, authored, palette);
|
|
2274
|
+
return resolveAuthoredBackground(background, palette, authored, options);
|
|
2275
|
+
}
|
|
2276
|
+
/** Legacy references may be measurable, inherited, or a trusted media treatment. */
|
|
2277
|
+
function resolveAuthoredBackground(background, palette, authored, options) {
|
|
2278
|
+
if (background === null) {
|
|
2279
|
+
return resolveDeterministicForeground("background", {
|
|
2280
|
+
...authored,
|
|
2281
|
+
text: authored.text ?? "text"
|
|
2282
|
+
}, palette);
|
|
2283
|
+
}
|
|
2284
|
+
if (background.type === "color") {
|
|
2285
|
+
const backgroundReference = normalizedReference(background.color);
|
|
2286
|
+
if (!backgroundReference || backgroundReference === "transparent") {
|
|
2287
|
+
return { kind: "inherits" };
|
|
2288
|
+
}
|
|
2289
|
+
return resolveDeterministicForeground(backgroundReference, authored, palette, options.compositeBase);
|
|
2290
|
+
}
|
|
2291
|
+
return resolveTrustedTreatmentForeground(authored, palette);
|
|
2292
|
+
}
|
|
2293
|
+
function foregroundCandidates(background, overrides) {
|
|
2294
|
+
return {
|
|
2295
|
+
text: candidateOrBackground(overrides.text, background?.textColor),
|
|
2296
|
+
heading: candidateOrBackground(overrides.heading, background?.headingColor),
|
|
2297
|
+
link: candidateOrBackground(overrides.link, background?.linkColor),
|
|
2298
|
+
linkHover: candidateOrBackground(overrides.linkHover),
|
|
2299
|
+
supporting: candidateOrBackground(overrides.supporting, background?.supportingColor)
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
function candidateOrBackground(candidate$1, backgroundValue) {
|
|
2303
|
+
return candidate$1 ?? backgroundValue ?? null;
|
|
2304
|
+
}
|
|
2305
|
+
function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
|
|
2306
|
+
const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
|
|
2307
|
+
if (!backgroundHex) {
|
|
2308
|
+
return resolveTrustedTreatmentForeground(candidates, palette);
|
|
2309
|
+
}
|
|
2310
|
+
return resolveMeasuredForeground(backgroundReference, backgroundHex, candidates, palette);
|
|
2311
|
+
}
|
|
2312
|
+
function resolveMeasuredForeground(backgroundReference, backgroundHex, candidates, palette) {
|
|
2313
|
+
const backgroundToken$1 = paletteToken(backgroundReference);
|
|
2314
|
+
const derivedOnToken = backgroundToken$1 ? onColorTokenName(backgroundToken$1) : null;
|
|
2315
|
+
const text = firstSafeToken([
|
|
2316
|
+
candidates.text,
|
|
2317
|
+
derivedOnToken,
|
|
2318
|
+
...FALLBACK_TEXT_TOKENS
|
|
2319
|
+
], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []) ?? pickBlackOrWhiteTextColor(backgroundHex);
|
|
2320
|
+
return knownForeground({
|
|
2321
|
+
text,
|
|
2322
|
+
heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
|
|
2323
|
+
link: safeTokenOr(candidates.link, text, backgroundHex, palette),
|
|
2324
|
+
supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
|
|
2325
|
+
}, {
|
|
2326
|
+
kind: "measured",
|
|
2327
|
+
backdropHex: backgroundHex,
|
|
2328
|
+
inks: solveSurfaceInks(backgroundHex, text, palette)
|
|
2329
|
+
}, candidates.linkHover, palette);
|
|
2330
|
+
}
|
|
2331
|
+
function resolveTrustedTreatmentForeground(candidates, palette) {
|
|
2332
|
+
const text = canonicalToken(candidates.text, palette);
|
|
2333
|
+
if (!text) return { kind: "unmeasured-paint" };
|
|
2334
|
+
return knownForeground({
|
|
2335
|
+
text,
|
|
2336
|
+
heading: canonicalToken(candidates.heading, palette) ?? text,
|
|
2337
|
+
link: canonicalToken(candidates.link, palette) ?? text,
|
|
2338
|
+
supporting: canonicalToken(candidates.supporting, palette) ?? text
|
|
2339
|
+
}, { kind: "trusted-treatment" }, candidates.linkHover, palette);
|
|
2340
|
+
}
|
|
2341
|
+
function knownForeground(roles, provenance, hoverCandidate, palette) {
|
|
2342
|
+
const hover = planForegroundHover({
|
|
2343
|
+
restingToken: roles.link,
|
|
2344
|
+
candidate: canonicalHoverToken(hoverCandidate, palette),
|
|
2345
|
+
palette,
|
|
2346
|
+
backdrops: provenance.kind === "measured" ? [provenance.backdropHex] : null
|
|
2347
|
+
});
|
|
2348
|
+
return {
|
|
2349
|
+
kind: "known",
|
|
2350
|
+
provenance,
|
|
2351
|
+
roles: {
|
|
2352
|
+
...roles,
|
|
2353
|
+
linkHover: hover.token,
|
|
2354
|
+
muted: provenance.kind === "measured" ? deriveSurfaceMutedColor(provenance.backdropHex, resolveConcreteColorHex(roles.text, palette) ?? provenance.backdropHex) : roles.text
|
|
2355
|
+
},
|
|
2356
|
+
hoverDecoration: hover.decoration
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
function canonicalHoverToken(reference, palette) {
|
|
2360
|
+
const token = parseThemeColorToken(reference);
|
|
2361
|
+
if (token) {
|
|
2362
|
+
const parts = parseColorTokenParts(token);
|
|
2363
|
+
if (parts.kind === "token" && resolveConcreteColorHex(parts.token, palette)) {
|
|
2364
|
+
return token;
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
return canonicalToken(reference, palette);
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* The one rule for what a resolved foreground publishes for the solved inks.
|
|
2371
|
+
* Only a measured paint under a publishing runtime publishes inks; any other
|
|
2372
|
+
* paint it owns resets them, and a paint it only inherits publishes nothing.
|
|
2373
|
+
*/
|
|
2374
|
+
function surfaceInkPublication(foreground, policy) {
|
|
2375
|
+
switch (foreground.kind) {
|
|
2376
|
+
case "inherits": return { kind: "none" };
|
|
2377
|
+
case "unmeasured-paint": return { kind: "reset" };
|
|
2378
|
+
case "known": return foreground.provenance.kind === "measured" && policy === "publish" ? {
|
|
2379
|
+
kind: "solved",
|
|
2380
|
+
inks: foreground.provenance.inks
|
|
2381
|
+
} : { kind: "reset" };
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
/** Keep a candidate only when it meets ordinary-text contrast on the ground. */
|
|
2385
|
+
function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
|
|
2386
|
+
return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
|
|
2387
|
+
}
|
|
2388
|
+
function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
|
|
2389
|
+
const seen = new Set();
|
|
2390
|
+
for (const candidate$1 of candidates) {
|
|
2391
|
+
const token = canonicalToken(candidate$1, palette, preferredEquivalentTokens);
|
|
2392
|
+
if (!token || seen.has(token)) continue;
|
|
2393
|
+
seen.add(token);
|
|
2394
|
+
const foregroundHex = resolveConcreteColorHex(token, palette);
|
|
2395
|
+
if (foregroundHex && contrastRatio(backgroundHex, foregroundHex) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO) {
|
|
2396
|
+
return token;
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
return null;
|
|
2400
|
+
}
|
|
2401
|
+
function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
|
|
2402
|
+
const normalized = normalizedReference(reference);
|
|
2403
|
+
if (!normalized || normalized === "transparent") return null;
|
|
2404
|
+
const direct = parseThemeColorToken(normalized);
|
|
2405
|
+
if (direct && resolveConcreteColorHex(direct, palette)) return direct;
|
|
2406
|
+
const referenceHex = resolveConcreteColorHex(normalized, palette);
|
|
2407
|
+
if (!referenceHex) return null;
|
|
2408
|
+
const equivalent = [
|
|
2409
|
+
...preferredEquivalentTokens,
|
|
2410
|
+
...CANONICAL_EQUIVALENT_TOKEN_ORDER,
|
|
2411
|
+
...Object.keys(palette).sort()
|
|
2412
|
+
].find((token) => {
|
|
2413
|
+
const parsed = parseThemeColorToken(token);
|
|
2414
|
+
return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
|
|
2415
|
+
});
|
|
2416
|
+
return equivalent ? parseThemeColorToken(equivalent) : null;
|
|
2417
|
+
}
|
|
2418
|
+
function paletteToken(reference) {
|
|
2419
|
+
const parts = parseColorTokenParts(reference);
|
|
2420
|
+
return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
|
|
2421
|
+
}
|
|
2422
|
+
function normalizedReference(value) {
|
|
2423
|
+
if (typeof value !== "string") return null;
|
|
2424
|
+
const normalized = value.trim();
|
|
2425
|
+
return normalized.length > 0 ? normalized : null;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
1900
2428
|
//#endregion
|
|
1901
2429
|
//#region ../theme-core/src/surfaces/logoVariant.ts
|
|
1902
2430
|
/** Concrete hex behind a plain, opaque palette token, or null otherwise. */
|
|
@@ -1914,9 +2442,9 @@ function opaquePaletteHexForToken(expandedPalette, token) {
|
|
|
1914
2442
|
* a guess.
|
|
1915
2443
|
*/
|
|
1916
2444
|
function surfaceToneFromForegroundToken(expandedPalette, foregroundToken) {
|
|
1917
|
-
const hex = opaquePaletteHexForToken(expandedPalette, foregroundToken);
|
|
1918
|
-
if (!hex) return "unknown";
|
|
1919
|
-
return featuresFromHex(hex).Y >= .5 ? "dark" : "light";
|
|
2445
|
+
const hex$1 = opaquePaletteHexForToken(expandedPalette, foregroundToken);
|
|
2446
|
+
if (!hex$1) return "unknown";
|
|
2447
|
+
return featuresFromHex(hex$1).Y >= .5 ? "dark" : "light";
|
|
1920
2448
|
}
|
|
1921
2449
|
/**
|
|
1922
2450
|
* The logo to draw on a surface of the given tone. A missing variant falls
|
|
@@ -3220,14 +3748,14 @@ function resolveSolid(input, backgroundToken$1, edge) {
|
|
|
3220
3748
|
function resolveSolidInk(input, backgroundToken$1, backgroundHex) {
|
|
3221
3749
|
const foregroundToken = unsafeAsOpaqueButtonColourToken(solidForegroundToken(backgroundToken$1));
|
|
3222
3750
|
const candidate$1 = input.palette[foregroundToken];
|
|
3223
|
-
const contrast = typeof candidate$1 === "string" ? validateButtonTextContrast({
|
|
3751
|
+
const contrast$1 = typeof candidate$1 === "string" ? validateButtonTextContrast({
|
|
3224
3752
|
...input,
|
|
3225
3753
|
background: backgroundToken$1,
|
|
3226
3754
|
backgroundHex,
|
|
3227
3755
|
foreground: foregroundToken,
|
|
3228
3756
|
foregroundHex: candidate$1
|
|
3229
3757
|
}) : null;
|
|
3230
|
-
if (contrast?.ok === true && typeof candidate$1 === "string") return {
|
|
3758
|
+
if (contrast$1?.ok === true && typeof candidate$1 === "string") return {
|
|
3231
3759
|
foregroundHex: candidate$1,
|
|
3232
3760
|
colour: {
|
|
3233
3761
|
kind: "solid",
|
|
@@ -3287,14 +3815,14 @@ function resolveGradient(input, treatment) {
|
|
|
3287
3815
|
for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
|
|
3288
3816
|
const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
|
|
3289
3817
|
const sampleHex = interpolateHex(start, end, position);
|
|
3290
|
-
const contrast = validateButtonTextContrast({
|
|
3818
|
+
const contrast$1 = validateButtonTextContrast({
|
|
3291
3819
|
...input,
|
|
3292
3820
|
background: `${startToken}→${endToken}@${position.toFixed(2)}`,
|
|
3293
3821
|
backgroundHex: sampleHex,
|
|
3294
3822
|
foreground: treatment.foregroundToken,
|
|
3295
3823
|
foregroundHex: foregroundHex.hex
|
|
3296
3824
|
});
|
|
3297
|
-
if (!contrast.ok) return contrast;
|
|
3825
|
+
if (!contrast$1.ok) return contrast$1;
|
|
3298
3826
|
if (input.surface) {
|
|
3299
3827
|
const boundary = validateButtonSurfaceContrast({
|
|
3300
3828
|
...input,
|
|
@@ -3318,10 +3846,10 @@ function resolveGradient(input, treatment) {
|
|
|
3318
3846
|
};
|
|
3319
3847
|
}
|
|
3320
3848
|
function paletteHex(input, token) {
|
|
3321
|
-
const hex = input.palette[token];
|
|
3322
|
-
return typeof hex === "string" ? {
|
|
3849
|
+
const hex$1 = input.palette[token];
|
|
3850
|
+
return typeof hex$1 === "string" ? {
|
|
3323
3851
|
ok: true,
|
|
3324
|
-
hex
|
|
3852
|
+
hex: hex$1
|
|
3325
3853
|
} : {
|
|
3326
3854
|
ok: false,
|
|
3327
3855
|
error: {
|
|
@@ -3418,11 +3946,11 @@ function deriveKnownBackground(input, palette, fallbackToken) {
|
|
|
3418
3946
|
};
|
|
3419
3947
|
}
|
|
3420
3948
|
function resolveKnownSurfaceColour(value, palette, underlayReference) {
|
|
3421
|
-
const hex = resolveOpaqueSurfaceHex(value, palette, underlayReference);
|
|
3422
|
-
if (!hex) return resolveKnownColour(value, palette);
|
|
3949
|
+
const hex$1 = resolveOpaqueSurfaceHex(value, palette, underlayReference);
|
|
3950
|
+
if (!hex$1) return resolveKnownColour(value, palette);
|
|
3423
3951
|
const reference = parseThemeColorReference(value, { palette });
|
|
3424
3952
|
return {
|
|
3425
|
-
hex,
|
|
3953
|
+
hex: hex$1,
|
|
3426
3954
|
css: resolveThemeColorReferenceValue(value, { palette }),
|
|
3427
3955
|
source: reference.kind === "literal-css" ? "literal-css" : "palette-token"
|
|
3428
3956
|
};
|
|
@@ -3706,10 +4234,10 @@ function resolveKnownColour(value, palette) {
|
|
|
3706
4234
|
if (reference.kind !== "literal-css") {
|
|
3707
4235
|
const token = String(reference.token);
|
|
3708
4236
|
const paletteHex$1 = palette[token];
|
|
3709
|
-
const hex$
|
|
3710
|
-
if (hex$
|
|
4237
|
+
const hex$2 = paletteHex$1 ? normalizeOpaqueHex(paletteHex$1) : null;
|
|
4238
|
+
if (hex$2) {
|
|
3711
4239
|
return {
|
|
3712
|
-
hex: hex$
|
|
4240
|
+
hex: hex$2,
|
|
3713
4241
|
css: resolveThemeColorReferenceValue(value, { palette }),
|
|
3714
4242
|
source: "palette-token"
|
|
3715
4243
|
};
|
|
@@ -3721,9 +4249,9 @@ function resolveKnownColour(value, palette) {
|
|
|
3721
4249
|
source: "literal-css"
|
|
3722
4250
|
} : null;
|
|
3723
4251
|
}
|
|
3724
|
-
const hex = normalizeOpaqueCssColour(reference.value);
|
|
3725
|
-
return hex ? {
|
|
3726
|
-
hex,
|
|
4252
|
+
const hex$1 = normalizeOpaqueCssColour(reference.value);
|
|
4253
|
+
return hex$1 ? {
|
|
4254
|
+
hex: hex$1,
|
|
3727
4255
|
css: reference.value,
|
|
3728
4256
|
source: "literal-css"
|
|
3729
4257
|
} : null;
|
|
@@ -3743,8 +4271,8 @@ const NAMED_OPAQUE_COLOURS = {
|
|
|
3743
4271
|
};
|
|
3744
4272
|
function normalizeOpaqueCssColour(value) {
|
|
3745
4273
|
const trimmed = value.trim();
|
|
3746
|
-
const hex = normalizeOpaqueHex(trimmed);
|
|
3747
|
-
if (hex) return hex;
|
|
4274
|
+
const hex$1 = normalizeOpaqueHex(trimmed);
|
|
4275
|
+
if (hex$1) return hex$1;
|
|
3748
4276
|
const named = NAMED_OPAQUE_COLOURS[trimmed.toLowerCase()];
|
|
3749
4277
|
return named ?? normalizeOpaqueRgb(trimmed) ?? normalizeOpaqueOklch(trimmed);
|
|
3750
4278
|
}
|
|
@@ -3803,7 +4331,8 @@ function opaqueSurfaceContextFromTheme(theme) {
|
|
|
3803
4331
|
palette,
|
|
3804
4332
|
colourIndex: preparePaletteColourIndex(palette),
|
|
3805
4333
|
buttonSystem: theme.buttons,
|
|
3806
|
-
linkHoverToken: theme.typography.proseLinkStyle?.hoverColorToken
|
|
4334
|
+
linkHoverToken: theme.typography.proseLinkStyle?.hoverColorToken,
|
|
4335
|
+
solvedInks: "publish"
|
|
3807
4336
|
};
|
|
3808
4337
|
}
|
|
3809
4338
|
/**
|
|
@@ -3826,6 +4355,7 @@ function opaqueSurfaceDeclarations(surface$1, arrangement = false) {
|
|
|
3826
4355
|
...arrangement ? { muted: colorValue(foreground.muted) } : {},
|
|
3827
4356
|
hoverDecoration: surface$1.hoverDecoration
|
|
3828
4357
|
}),
|
|
4358
|
+
...surfaceInkDeclarations(surface$1.inks),
|
|
3829
4359
|
...arrangement && surface$1.decorations !== undefined ? [`${SURFACE_FOREGROUND_CSS_VARS.line}: ${surface$1.decorations.line};`, `${SURFACE_FOREGROUND_CSS_VARS.mark}: ${surface$1.decorations.mark};`] : [],
|
|
3830
4360
|
...Object.entries(surface$1.buttonContext.styles).map(([property, value]) => `${property}: ${value};`)
|
|
3831
4361
|
];
|
|
@@ -3839,6 +4369,7 @@ function inheritedSurfaceDeclarations(arrangement) {
|
|
|
3839
4369
|
return [
|
|
3840
4370
|
"color",
|
|
3841
4371
|
...Object.entries(SURFACE_FOREGROUND_CSS_VARS).filter(([role]) => arrangement || role !== "line" && role !== "mark" && role !== "muted").map(([, variable]) => variable),
|
|
4372
|
+
...Object.values(SURFACE_INK_CSS_VARS),
|
|
3842
4373
|
"--rb-surface-link-hover-decoration",
|
|
3843
4374
|
...Object.keys(contextualButtonSurfaceResetStyles())
|
|
3844
4375
|
].map((property) => `${property}: inherit;`);
|
|
@@ -4063,8 +4594,8 @@ const mapDrawingTreatments = {
|
|
|
4063
4594
|
function mapDrawingTreatmentFor(skin) {
|
|
4064
4595
|
return (skin === undefined ? undefined : mapDrawingTreatments[skin]) ?? defaultMapDrawingTreatment;
|
|
4065
4596
|
}
|
|
4066
|
-
const toLab = (hex) => {
|
|
4067
|
-
const { r, g, b } = hexToRgb(hex);
|
|
4597
|
+
const toLab = (hex$1) => {
|
|
4598
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
4068
4599
|
return srgbToOklab(r, g, b);
|
|
4069
4600
|
};
|
|
4070
4601
|
const toHex = ({ L, a, b }) => {
|
|
@@ -4084,14 +4615,14 @@ function pole(ground, L) {
|
|
|
4084
4615
|
}
|
|
4085
4616
|
/** The first blend from `from` toward `to` that reaches the contrast floor against every backdrop. */
|
|
4086
4617
|
function firstContrastBlend(from, to, backdrops, floor) {
|
|
4087
|
-
const steps = Array.from({ length: 41 }, (_, index) => toHex(blend(from, to, index / 40)));
|
|
4088
|
-
return steps.find((hex) => backdrops.every((backdrop) => contrastRatio(hex, backdrop) >= floor)) ?? toHex(to);
|
|
4618
|
+
const steps$1 = Array.from({ length: 41 }, (_, index) => toHex(blend(from, to, index / 40)));
|
|
4619
|
+
return steps$1.find((hex$1) => backdrops.every((backdrop) => contrastRatio(hex$1, backdrop) >= floor)) ?? toHex(to);
|
|
4089
4620
|
}
|
|
4090
4621
|
/** A tint must stand apart from what it is drawn on (a brand river on a brand band would vanish); else a step toward the ink. */
|
|
4091
4622
|
function tint(base, spec, palette, ink) {
|
|
4092
4623
|
const candidates = spec.roles.flatMap((role) => {
|
|
4093
|
-
const hex = resolveConcreteColorHex(role, palette);
|
|
4094
|
-
return hex ? [blend(base, toLab(hex), spec.amount)] : [];
|
|
4624
|
+
const hex$1 = resolveConcreteColorHex(role, palette);
|
|
4625
|
+
return hex$1 ? [blend(base, toLab(hex$1), spec.amount)] : [];
|
|
4095
4626
|
});
|
|
4096
4627
|
return candidates.find((candidate$1) => distance(candidate$1, base) >= .025) ?? blend(base, ink, Math.max(spec.amount * .6, .06));
|
|
4097
4628
|
}
|
|
@@ -4116,15 +4647,15 @@ function resolveMapDrawingColours(input) {
|
|
|
4116
4647
|
toHex(building)
|
|
4117
4648
|
];
|
|
4118
4649
|
const pinRoles = t.pin.flatMap((role) => {
|
|
4119
|
-
const hex = resolveConcreteColorHex(role, palette);
|
|
4120
|
-
return hex ? [hex] : [];
|
|
4650
|
+
const hex$1 = resolveConcreteColorHex(role, palette);
|
|
4651
|
+
return hex$1 ? [hex$1] : [];
|
|
4121
4652
|
});
|
|
4122
|
-
const pin = [...pinRoles, input.ink].find((hex) => pinBackdrops.every((backdrop) => contrastRatio(hex, backdrop) >= 3)) ?? firstContrastBlend(toLab(pinRoles[0] ?? input.ink), ink, pinBackdrops, 3);
|
|
4653
|
+
const pin = [...pinRoles, input.ink].find((hex$1) => pinBackdrops.every((backdrop) => contrastRatio(hex$1, backdrop) >= 3)) ?? firstContrastBlend(toLab(pinRoles[0] ?? input.ink), ink, pinBackdrops, 3);
|
|
4123
4654
|
const pinEdge = [
|
|
4124
4655
|
toHex(light),
|
|
4125
4656
|
toHex(dark),
|
|
4126
4657
|
landHex
|
|
4127
|
-
].reduce((best, hex) => contrastRatio(hex, pin) > contrastRatio(best, pin) ? hex : best);
|
|
4658
|
+
].reduce((best, hex$1) => contrastRatio(hex$1, pin) > contrastRatio(best, pin) ? hex$1 : best);
|
|
4128
4659
|
return {
|
|
4129
4660
|
land: landHex,
|
|
4130
4661
|
water: toHex(tint(land, t.water, palette, ink)),
|
|
@@ -4330,369 +4861,138 @@ function mergeStyles(...styles) {
|
|
|
4330
4861
|
}
|
|
4331
4862
|
|
|
4332
4863
|
//#endregion
|
|
4333
|
-
//#region ../theme-core/src/
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
console.warn(`[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`);
|
|
4360
|
-
}
|
|
4361
|
-
return rgbColorValue(token);
|
|
4864
|
+
//#region ../theme-core/src/surfaces/foregroundContract.ts
|
|
4865
|
+
const SURFACE_FOREGROUND_CSS_VARS = {
|
|
4866
|
+
text: "--rb-surface-text-color",
|
|
4867
|
+
heading: "--rb-surface-heading-color",
|
|
4868
|
+
link: "--rb-surface-link-color",
|
|
4869
|
+
linkHover: "--rb-surface-link-hover-color",
|
|
4870
|
+
supporting: "--rb-surface-supporting-color",
|
|
4871
|
+
muted: "--rb-surface-muted-color",
|
|
4872
|
+
line: "--rb-surface-line-color",
|
|
4873
|
+
mark: "--rb-surface-mark-color"
|
|
4874
|
+
};
|
|
4875
|
+
const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
|
|
4876
|
+
const DEFAULT_SURFACE_LINK_COLOR = "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))";
|
|
4877
|
+
const DEFAULT_SURFACE_SUPPORTING_COLOR = "rgb(var(--tb-mutedText, 100 116 139))";
|
|
4878
|
+
const DEFAULT_ROLE_FALLBACKS = {
|
|
4879
|
+
text: DEFAULT_SURFACE_TEXT_COLOR,
|
|
4880
|
+
heading: DEFAULT_SURFACE_TEXT_COLOR,
|
|
4881
|
+
link: DEFAULT_SURFACE_LINK_COLOR,
|
|
4882
|
+
linkHover: DEFAULT_SURFACE_LINK_COLOR,
|
|
4883
|
+
supporting: DEFAULT_SURFACE_SUPPORTING_COLOR,
|
|
4884
|
+
muted: DEFAULT_SURFACE_SUPPORTING_COLOR
|
|
4885
|
+
};
|
|
4886
|
+
function requireSurfaceColorValue(role, value) {
|
|
4887
|
+
const normalized = value.trim();
|
|
4888
|
+
if (normalized.length === 0) {
|
|
4889
|
+
throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
|
|
4362
4890
|
}
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
if (isNaN(shade)) {
|
|
4383
|
-
console.warn(`[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`);
|
|
4384
|
-
return token;
|
|
4385
|
-
}
|
|
4386
|
-
const newShade = Math.max(100, Math.min(900, shade + amount));
|
|
4387
|
-
return `${base}-${newShade}`;
|
|
4891
|
+
return normalized;
|
|
4892
|
+
}
|
|
4893
|
+
function completeSurfaceForegrounds(input) {
|
|
4894
|
+
const text = requireSurfaceColorValue("text", input.text);
|
|
4895
|
+
const link = input.link === undefined ? text : requireSurfaceColorValue("link", input.link);
|
|
4896
|
+
return {
|
|
4897
|
+
text,
|
|
4898
|
+
heading: input.heading === undefined ? text : requireSurfaceColorValue("heading", input.heading),
|
|
4899
|
+
link,
|
|
4900
|
+
linkHover: input.linkHover === undefined ? link : requireSurfaceColorValue("linkHover", input.linkHover),
|
|
4901
|
+
muted: input.muted === undefined ? text : requireSurfaceColorValue("muted", input.muted),
|
|
4902
|
+
supporting: input.supporting === undefined ? text : requireSurfaceColorValue("supporting", input.supporting)
|
|
4903
|
+
};
|
|
4904
|
+
}
|
|
4905
|
+
function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
|
|
4906
|
+
const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
|
|
4907
|
+
const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
|
|
4908
|
+
if (role === "text") {
|
|
4909
|
+
return `var(${roleVariable}, ${fallback})`;
|
|
4388
4910
|
}
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
*
|
|
4392
|
-
* @param token - Token name to check
|
|
4393
|
-
* @returns True if token exists in theme
|
|
4394
|
-
*/
|
|
4395
|
-
tokenExists(token) {
|
|
4396
|
-
const commonTokens = [
|
|
4397
|
-
"white",
|
|
4398
|
-
"black",
|
|
4399
|
-
"transparent"
|
|
4400
|
-
];
|
|
4401
|
-
if (commonTokens.includes(token)) {
|
|
4402
|
-
return true;
|
|
4403
|
-
}
|
|
4404
|
-
const parts = token.split("-");
|
|
4405
|
-
if (parts.length >= 2) {
|
|
4406
|
-
const lastPart = parts[parts.length - 1] ?? "";
|
|
4407
|
-
const shade = parseInt(lastPart, 10);
|
|
4408
|
-
if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
|
|
4409
|
-
return true;
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
return true;
|
|
4911
|
+
if (role === "linkHover") {
|
|
4912
|
+
return `var(${roleVariable}, ${surfaceForegroundValue("link", fallback)})`;
|
|
4413
4913
|
}
|
|
4914
|
+
return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
|
|
4915
|
+
}
|
|
4916
|
+
/** The three solved inks, published beside the foreground roles by the same surfaces. */
|
|
4917
|
+
const SURFACE_INK_CSS_VARS = {
|
|
4918
|
+
brand: "--rb-surface-brand-color",
|
|
4919
|
+
accent: "--rb-surface-accent-color",
|
|
4920
|
+
focus: "--rb-surface-focus-color"
|
|
4414
4921
|
};
|
|
4415
|
-
|
|
4416
|
-
//#endregion
|
|
4417
|
-
//#region ../theme-core/src/surfaces/foregroundInteraction.ts
|
|
4418
4922
|
/**
|
|
4419
|
-
*
|
|
4420
|
-
*
|
|
4421
|
-
*
|
|
4422
|
-
*
|
|
4423
|
-
* rather than chosen for taste. A resolved foreground is frequently an
|
|
4424
|
-
* `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
|
|
4425
|
-
* with no shade ramp: over a primary-painted section the resolved text role
|
|
4426
|
-
* is `onPrimary`, and white has no headroom left to move further from its
|
|
4427
|
-
* background. Moving *toward* the mid-range always has headroom, whatever
|
|
4428
|
-
* the resting colour, and it keeps the hue exactly.
|
|
4429
|
-
*
|
|
4430
|
-
* 85% is the measured floor, not a round number. Softening costs contrast, and
|
|
4431
|
-
* the binding case is a scrolled overlay header: `contrast-glass` paints 60%
|
|
4432
|
-
* black or white, which composites the backdrop into a known band whatever the
|
|
4433
|
-
* page content underneath happens to be. The lightest backdrop a dark glass can
|
|
4434
|
-
* present is 60% black over white, `#666666`; a white foreground softened to
|
|
4435
|
-
* 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
|
|
4436
|
-
* 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
|
|
4437
|
-
* the large-text 3:1.
|
|
4438
|
-
*
|
|
4439
|
-
* The mirror case — a light glass over dark content, `#999999` — cannot be met
|
|
4440
|
-
* by any softening, because the *resting* colour is already only about 4.6:1
|
|
4441
|
-
* there. That is a property of the glass rather than of this derivation, and
|
|
4442
|
-
* it wants fixing where the glass tone is chosen.
|
|
4923
|
+
* Read a solved ink, falling back straight to the declaration's own previous
|
|
4924
|
+
* expression. Unlike `surfaceForegroundValue` this never falls through to
|
|
4925
|
+
* `--rb-surface-text-color`: where no ink is published (bridge pages, unmeasured
|
|
4926
|
+
* paints) brand text must keep its brand colour, not turn into the text ink.
|
|
4443
4927
|
*/
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
* Derive the hover foreground that belongs with a resolved resting foreground.
|
|
4447
|
-
*
|
|
4448
|
-
* A resting foreground is chosen to be legible against a known surface. A hover
|
|
4449
|
-
* state derived from it stays within a bounded distance of that colour, so it
|
|
4450
|
-
* inherits most of the resting colour's contrast margin instead of asserting
|
|
4451
|
-
* an unrelated brand colour that no one checked against the surface.
|
|
4452
|
-
*
|
|
4453
|
-
* That bounded-distance property is what lets this be used on a transparent
|
|
4454
|
-
* overlay header, where the backdrop is only partly knowable: the scrolled
|
|
4455
|
-
* state composites a 60% glass tint over arbitrary page content, so no
|
|
4456
|
-
* compile-time contrast proof is available there. Staying near a colour that
|
|
4457
|
-
* is already trusted in both states is a weaker claim than a contrast proof,
|
|
4458
|
-
* and it is one this function can actually keep.
|
|
4459
|
-
*
|
|
4460
|
-
* Returns `null` when the resting token is not a plain palette token — an
|
|
4461
|
-
* already-faded or raw value has no well-defined softening, and the caller
|
|
4462
|
-
* should leave hover alone rather than guess.
|
|
4463
|
-
*/
|
|
4464
|
-
function deriveForegroundHoverToken(restingToken) {
|
|
4465
|
-
const parts = parseColorTokenParts(restingToken);
|
|
4466
|
-
if (parts.kind !== "token") return null;
|
|
4467
|
-
if (parts.opacityPercent !== null) return null;
|
|
4468
|
-
return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
|
|
4928
|
+
function surfaceInkValue(role, previousExpression) {
|
|
4929
|
+
return `var(${SURFACE_INK_CSS_VARS[role]}, ${requireSurfaceColorValue("fallback", previousExpression)})`;
|
|
4469
4930
|
}
|
|
4470
4931
|
/**
|
|
4471
|
-
* The
|
|
4472
|
-
* `
|
|
4473
|
-
*
|
|
4932
|
+
* The declarations for a surface's ink publication. An exact ink is a token
|
|
4933
|
+
* reference (`rgb(var(--tb-primary))`), so a site's own token value wins; a
|
|
4934
|
+
* nudged or replaced ink is a literal colour.
|
|
4474
4935
|
*/
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
*/
|
|
4486
|
-
function worstCaseGlassBackdrop(tone) {
|
|
4487
|
-
const [over, tint$1] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
|
|
4488
|
-
return compositeHex(tint$1, over, CONTRAST_GLASS_ALPHA);
|
|
4936
|
+
function surfaceInkStyle(publication) {
|
|
4937
|
+
switch (publication.kind) {
|
|
4938
|
+
case "none": return {};
|
|
4939
|
+
case "reset": return inkVariables(() => "initial");
|
|
4940
|
+
case "solved": return inkVariables((role) => {
|
|
4941
|
+
const ink = publication.inks[role];
|
|
4942
|
+
const token = parseThemeColorToken(ink);
|
|
4943
|
+
return token ? rgbColorValue(token) : ink;
|
|
4944
|
+
});
|
|
4945
|
+
}
|
|
4489
4946
|
}
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
const f = hexToRgb(foreground);
|
|
4493
|
-
const b = hexToRgb(background);
|
|
4494
|
-
return rgbToHex(Math.round(mix$1(b.r, f.r, alpha)), Math.round(mix$1(b.g, f.g, alpha)), Math.round(mix$1(b.b, f.b, alpha)));
|
|
4947
|
+
function surfaceInkDeclarations(publication) {
|
|
4948
|
+
return Object.entries(surfaceInkStyle(publication)).map(([property, value]) => `${property}: ${value};`);
|
|
4495
4949
|
}
|
|
4496
|
-
|
|
4497
|
-
* Prefer an authored hover that survives every measured backdrop, then a
|
|
4498
|
-
* checked softening of the resting colour. A null backdrop set represents a
|
|
4499
|
-
* trusted treatment: only an explicit authored hover may change the colour.
|
|
4500
|
-
* When neither option works, retain the resting colour and underline it.
|
|
4501
|
-
*/
|
|
4502
|
-
function planForegroundHover(input) {
|
|
4503
|
-
const { restingToken, candidate: candidate$1, palette, backdrops } = input;
|
|
4504
|
-
if (candidate$1 && String(candidate$1) !== String(restingToken)) {
|
|
4505
|
-
if (backdrops === null || survivesEveryBackdrop(candidate$1, palette, backdrops)) {
|
|
4506
|
-
return {
|
|
4507
|
-
kind: "candidate",
|
|
4508
|
-
token: candidate$1,
|
|
4509
|
-
decoration: "initial"
|
|
4510
|
-
};
|
|
4511
|
-
}
|
|
4512
|
-
}
|
|
4513
|
-
const resting = parseThemeColorToken(restingToken);
|
|
4514
|
-
const softened = resting ? deriveForegroundHoverToken(resting) : null;
|
|
4515
|
-
if (softened && backdrops && survivesEveryBackdrop(softened, palette, backdrops)) {
|
|
4516
|
-
return {
|
|
4517
|
-
kind: "softened",
|
|
4518
|
-
token: softened,
|
|
4519
|
-
decoration: "initial"
|
|
4520
|
-
};
|
|
4521
|
-
}
|
|
4950
|
+
function inkVariables(value) {
|
|
4522
4951
|
return {
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4952
|
+
[SURFACE_INK_CSS_VARS.brand]: value("brand"),
|
|
4953
|
+
[SURFACE_INK_CSS_VARS.accent]: value("accent"),
|
|
4954
|
+
[SURFACE_INK_CSS_VARS.focus]: value("focus")
|
|
4526
4955
|
};
|
|
4527
4956
|
}
|
|
4528
|
-
function
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
return foreground !== null && contrastRatio(foreground, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO;
|
|
4532
|
-
});
|
|
4533
|
-
}
|
|
4534
|
-
|
|
4535
|
-
//#endregion
|
|
4536
|
-
//#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
|
|
4537
|
-
const FALLBACK_TEXT_TOKENS = [
|
|
4538
|
-
"text",
|
|
4539
|
-
"primaryForeground",
|
|
4540
|
-
"secondaryForeground",
|
|
4541
|
-
"accentForeground",
|
|
4542
|
-
"neutral-950",
|
|
4543
|
-
"neutral-50"
|
|
4544
|
-
];
|
|
4545
|
-
const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
|
|
4546
|
-
"text",
|
|
4547
|
-
"primaryForeground",
|
|
4548
|
-
"secondaryForeground",
|
|
4549
|
-
"accentForeground",
|
|
4550
|
-
"onPrimary",
|
|
4551
|
-
"onSecondary",
|
|
4552
|
-
"onAccent",
|
|
4553
|
-
"onSurface",
|
|
4554
|
-
"neutral-950",
|
|
4555
|
-
"neutral-50"
|
|
4556
|
-
];
|
|
4557
|
-
function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
|
|
4558
|
-
const authored = foregroundCandidates(background, candidates);
|
|
4559
|
-
if ("measuredBackdrop" in options) return resolveMeasuredForeground(background?.color ?? "background", options.measuredBackdrop, authored, palette);
|
|
4560
|
-
return resolveAuthoredBackground(background, palette, authored, options);
|
|
4561
|
-
}
|
|
4562
|
-
/** Legacy references may be measurable, inherited, or a trusted media treatment. */
|
|
4563
|
-
function resolveAuthoredBackground(background, palette, authored, options) {
|
|
4564
|
-
if (background === null) {
|
|
4565
|
-
return resolveDeterministicForeground("background", {
|
|
4566
|
-
...authored,
|
|
4567
|
-
text: authored.text ?? "text"
|
|
4568
|
-
}, palette);
|
|
4569
|
-
}
|
|
4570
|
-
if (background.type === "color") {
|
|
4571
|
-
const backgroundReference = normalizedReference(background.color);
|
|
4572
|
-
if (!backgroundReference || backgroundReference === "transparent") {
|
|
4573
|
-
return { kind: "unknown" };
|
|
4574
|
-
}
|
|
4575
|
-
return resolveDeterministicForeground(backgroundReference, authored, palette, options.compositeBase);
|
|
4957
|
+
function surfaceForegroundValueWithOpacity(role, opacityPercent) {
|
|
4958
|
+
if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) {
|
|
4959
|
+
throw new RangeError("Surface foreground opacity must be between 0 and 100.");
|
|
4576
4960
|
}
|
|
4577
|
-
return
|
|
4961
|
+
return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
|
|
4578
4962
|
}
|
|
4579
|
-
function
|
|
4963
|
+
function surfaceForegroundStyle(input) {
|
|
4964
|
+
const foregrounds = completeSurfaceForegrounds(input);
|
|
4580
4965
|
return {
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4966
|
+
color: foregrounds.text,
|
|
4967
|
+
[SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
|
|
4968
|
+
[SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
|
|
4969
|
+
[SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
|
|
4970
|
+
[SURFACE_FOREGROUND_CSS_VARS.linkHover]: foregrounds.linkHover,
|
|
4971
|
+
[SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting,
|
|
4972
|
+
...input.muted === undefined ? {} : { [SURFACE_FOREGROUND_CSS_VARS.muted]: foregrounds.muted },
|
|
4973
|
+
"--rb-surface-link-hover-decoration": hoverDecoration(input)
|
|
4586
4974
|
};
|
|
4587
4975
|
}
|
|
4588
|
-
function
|
|
4589
|
-
return
|
|
4590
|
-
}
|
|
4591
|
-
function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
|
|
4592
|
-
const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
|
|
4593
|
-
if (!backgroundHex) {
|
|
4594
|
-
return resolveTrustedTreatmentForeground(candidates, palette);
|
|
4595
|
-
}
|
|
4596
|
-
return resolveMeasuredForeground(backgroundReference, backgroundHex, candidates, palette);
|
|
4597
|
-
}
|
|
4598
|
-
function resolveMeasuredForeground(backgroundReference, backgroundHex, candidates, palette) {
|
|
4599
|
-
const backgroundToken$1 = paletteToken(backgroundReference);
|
|
4600
|
-
const derivedOnToken = backgroundToken$1 ? onColorTokenName(backgroundToken$1) : null;
|
|
4601
|
-
const text = firstSafeToken([
|
|
4602
|
-
candidates.text,
|
|
4603
|
-
derivedOnToken,
|
|
4604
|
-
...FALLBACK_TEXT_TOKENS
|
|
4605
|
-
], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []) ?? pickBlackOrWhiteTextColor(backgroundHex);
|
|
4606
|
-
return knownForeground({
|
|
4607
|
-
text,
|
|
4608
|
-
heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
|
|
4609
|
-
link: safeTokenOr(candidates.link, text, backgroundHex, palette),
|
|
4610
|
-
supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
|
|
4611
|
-
}, {
|
|
4612
|
-
kind: "measured",
|
|
4613
|
-
backdropHex: backgroundHex
|
|
4614
|
-
}, candidates.linkHover, palette);
|
|
4615
|
-
}
|
|
4616
|
-
function resolveTrustedTreatmentForeground(candidates, palette) {
|
|
4617
|
-
const text = canonicalToken(candidates.text, palette);
|
|
4618
|
-
if (!text) return { kind: "unknown" };
|
|
4619
|
-
return knownForeground({
|
|
4620
|
-
text,
|
|
4621
|
-
heading: canonicalToken(candidates.heading, palette) ?? text,
|
|
4622
|
-
link: canonicalToken(candidates.link, palette) ?? text,
|
|
4623
|
-
supporting: canonicalToken(candidates.supporting, palette) ?? text
|
|
4624
|
-
}, { kind: "trusted-treatment" }, candidates.linkHover, palette);
|
|
4976
|
+
function surfaceForegroundDeclarations(input) {
|
|
4977
|
+
return Object.entries(surfaceForegroundStyle(input)).map(([property, value]) => `${property}: ${value};`);
|
|
4625
4978
|
}
|
|
4626
|
-
function
|
|
4627
|
-
const
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
});
|
|
4979
|
+
function surfaceForegroundInputFromResolved(foreground, color) {
|
|
4980
|
+
const { roles } = foreground;
|
|
4981
|
+
const value = (ink) => {
|
|
4982
|
+
const token = parseThemeColorToken(ink);
|
|
4983
|
+
return token ? color(token) : ink;
|
|
4984
|
+
};
|
|
4633
4985
|
return {
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
},
|
|
4641
|
-
hoverDecoration: hover.decoration
|
|
4986
|
+
text: value(roles.text),
|
|
4987
|
+
heading: value(roles.heading),
|
|
4988
|
+
link: value(roles.link),
|
|
4989
|
+
linkHover: value(roles.linkHover),
|
|
4990
|
+
supporting: value(roles.supporting),
|
|
4991
|
+
hoverDecoration: foreground.hoverDecoration
|
|
4642
4992
|
};
|
|
4643
4993
|
}
|
|
4644
|
-
function
|
|
4645
|
-
|
|
4646
|
-
if (token) {
|
|
4647
|
-
const parts = parseColorTokenParts(token);
|
|
4648
|
-
if (parts.kind === "token" && resolveConcreteColorHex(parts.token, palette)) {
|
|
4649
|
-
return token;
|
|
4650
|
-
}
|
|
4651
|
-
}
|
|
4652
|
-
return canonicalToken(reference, palette);
|
|
4653
|
-
}
|
|
4654
|
-
/** Keep a candidate only when it meets ordinary-text contrast on the ground. */
|
|
4655
|
-
function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
|
|
4656
|
-
return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
|
|
4657
|
-
}
|
|
4658
|
-
function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
|
|
4659
|
-
const seen = new Set();
|
|
4660
|
-
for (const candidate$1 of candidates) {
|
|
4661
|
-
const token = canonicalToken(candidate$1, palette, preferredEquivalentTokens);
|
|
4662
|
-
if (!token || seen.has(token)) continue;
|
|
4663
|
-
seen.add(token);
|
|
4664
|
-
const foregroundHex = resolveConcreteColorHex(token, palette);
|
|
4665
|
-
if (foregroundHex && contrastRatio(backgroundHex, foregroundHex) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO) {
|
|
4666
|
-
return token;
|
|
4667
|
-
}
|
|
4668
|
-
}
|
|
4669
|
-
return null;
|
|
4670
|
-
}
|
|
4671
|
-
function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
|
|
4672
|
-
const normalized = normalizedReference(reference);
|
|
4673
|
-
if (!normalized || normalized === "transparent") return null;
|
|
4674
|
-
const direct = parseThemeColorToken(normalized);
|
|
4675
|
-
if (direct && resolveConcreteColorHex(direct, palette)) return direct;
|
|
4676
|
-
const referenceHex = resolveConcreteColorHex(normalized, palette);
|
|
4677
|
-
if (!referenceHex) return null;
|
|
4678
|
-
const equivalent = [
|
|
4679
|
-
...preferredEquivalentTokens,
|
|
4680
|
-
...CANONICAL_EQUIVALENT_TOKEN_ORDER,
|
|
4681
|
-
...Object.keys(palette).sort()
|
|
4682
|
-
].find((token) => {
|
|
4683
|
-
const parsed = parseThemeColorToken(token);
|
|
4684
|
-
return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
|
|
4685
|
-
});
|
|
4686
|
-
return equivalent ? parseThemeColorToken(equivalent) : null;
|
|
4687
|
-
}
|
|
4688
|
-
function paletteToken(reference) {
|
|
4689
|
-
const parts = parseColorTokenParts(reference);
|
|
4690
|
-
return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
|
|
4691
|
-
}
|
|
4692
|
-
function normalizedReference(value) {
|
|
4693
|
-
if (typeof value !== "string") return null;
|
|
4694
|
-
const normalized = value.trim();
|
|
4695
|
-
return normalized.length > 0 ? normalized : null;
|
|
4994
|
+
function hoverDecoration(input) {
|
|
4995
|
+
return input.hoverDecoration ?? "underline";
|
|
4696
4996
|
}
|
|
4697
4997
|
|
|
4698
4998
|
//#endregion
|
|
@@ -4862,22 +5162,22 @@ function resolveAccessibleSurfaceRecipe(input) {
|
|
|
4862
5162
|
if (result.kind === "resolved-accessible-surface") return result;
|
|
4863
5163
|
const recipe = surfaceRecipes[input.surface];
|
|
4864
5164
|
const token = recipe.backgroundCandidates.find((candidate$1) => resolveConcreteColorHex(candidate$1, input.palette) !== null);
|
|
4865
|
-
const hex = token ? resolveConcreteColorHex(token, input.palette) : null;
|
|
4866
|
-
if (!token || !hex) return result;
|
|
4867
|
-
const ink = pickBlackOrWhiteTextColor(hex);
|
|
5165
|
+
const hex$1 = token ? resolveConcreteColorHex(token, input.palette) : null;
|
|
5166
|
+
if (!token || !hex$1) return result;
|
|
5167
|
+
const ink = pickBlackOrWhiteTextColor(hex$1);
|
|
4868
5168
|
const foreground = (role) => ({
|
|
4869
5169
|
kind: "safe-token",
|
|
4870
5170
|
role,
|
|
4871
5171
|
token: asAccessibleSurfaceToken(ink),
|
|
4872
5172
|
hex: ink,
|
|
4873
|
-
contrastRatio: contrastRatio(ink, hex)
|
|
5173
|
+
contrastRatio: contrastRatio(ink, hex$1)
|
|
4874
5174
|
});
|
|
4875
5175
|
return {
|
|
4876
5176
|
kind: "resolved-accessible-surface",
|
|
4877
5177
|
surface: input.surface,
|
|
4878
5178
|
background: {
|
|
4879
5179
|
token: asAccessibleSurfaceToken(token),
|
|
4880
|
-
hex,
|
|
5180
|
+
hex: hex$1,
|
|
4881
5181
|
inherited: recipe.inheritedBackground
|
|
4882
5182
|
},
|
|
4883
5183
|
heading: {
|
|
@@ -5066,12 +5366,12 @@ function surfaceResolutionFailureReason(rejectedCandidates) {
|
|
|
5066
5366
|
return rejectedCandidates.some((candidate$1) => candidate$1.kind === "foreground-candidate") ? "no-safe-foreground-candidate" : "no-background-candidate";
|
|
5067
5367
|
}
|
|
5068
5368
|
function missingBackgroundSurface(input) {
|
|
5069
|
-
const contrast = contrastRatio(missingBackgroundTokens.foreground, missingBackgroundTokens.background);
|
|
5369
|
+
const contrast$1 = contrastRatio(missingBackgroundTokens.foreground, missingBackgroundTokens.background);
|
|
5070
5370
|
const foreground = {
|
|
5071
5371
|
kind: "safe-token",
|
|
5072
5372
|
token: asAccessibleSurfaceToken(missingBackgroundTokens.foreground),
|
|
5073
5373
|
hex: missingBackgroundTokens.foreground,
|
|
5074
|
-
contrastRatio: contrast
|
|
5374
|
+
contrastRatio: contrast$1
|
|
5075
5375
|
};
|
|
5076
5376
|
return {
|
|
5077
5377
|
kind: "resolved-accessible-surface",
|
|
@@ -5146,7 +5446,8 @@ function surfaceTransportContext(context) {
|
|
|
5146
5446
|
const revision = ownSurfaceRevision(context);
|
|
5147
5447
|
const result = Object.freeze({
|
|
5148
5448
|
...revision,
|
|
5149
|
-
colourIndex: context.colourIndex
|
|
5449
|
+
colourIndex: context.colourIndex,
|
|
5450
|
+
solvedInks: context.solvedInks
|
|
5150
5451
|
});
|
|
5151
5452
|
surfaceRevisionOwners.set(result, revision);
|
|
5152
5453
|
return result;
|
|
@@ -5172,7 +5473,7 @@ function resolveSurface(spec, context) {
|
|
|
5172
5473
|
return spec.kind === "section" ? resolveSectionSurface(spec.background, context) : preparedSurfaceValue(spec, context) ?? certifySurface(spec, context);
|
|
5173
5474
|
}
|
|
5174
5475
|
/** Own one theme revision; derive its surface decisions without copying its inputs again. */
|
|
5175
|
-
function prepareThemeSurfaceRevision(inputTheme, selectSurfaces = () => []) {
|
|
5476
|
+
function prepareThemeSurfaceRevision(inputTheme, selectSurfaces = () => [], solvedInks) {
|
|
5176
5477
|
const theme = structuredClone(inputTheme);
|
|
5177
5478
|
const snapshot = freezeOwnedThemeValue(theme);
|
|
5178
5479
|
const declarations = ownedThemeSnapshot(selectSurfaces(snapshot));
|
|
@@ -5184,7 +5485,8 @@ function prepareThemeSurfaceRevision(inputTheme, selectSurfaces = () => []) {
|
|
|
5184
5485
|
kind: "concrete-palette",
|
|
5185
5486
|
palette,
|
|
5186
5487
|
colourIndex: preparePaletteColourIndex(palette),
|
|
5187
|
-
buttonSystem: snapshot.buttons
|
|
5488
|
+
buttonSystem: snapshot.buttons,
|
|
5489
|
+
solvedInks
|
|
5188
5490
|
});
|
|
5189
5491
|
registerOwnedRevisionInputs(context);
|
|
5190
5492
|
const preparedSurfaces = declarations.length === 0 ? undefined : surfaceIndexFromOwnedInputs(declarations, context);
|
|
@@ -5204,7 +5506,8 @@ function prepareSurfaceIndex(specs, context) {
|
|
|
5204
5506
|
linkHoverToken: context.linkHoverToken,
|
|
5205
5507
|
foreground: context.foreground,
|
|
5206
5508
|
compositeBase: context.compositeBase,
|
|
5207
|
-
buttonContext: context.buttonContext
|
|
5509
|
+
buttonContext: context.buttonContext,
|
|
5510
|
+
solvedInks: context.solvedInks
|
|
5208
5511
|
});
|
|
5209
5512
|
return surfaceIndexFromOwnedInputs(ownedThemeSnapshot(specs), snapshot);
|
|
5210
5513
|
}
|
|
@@ -5244,7 +5547,8 @@ function surfaceResolutionKey(spec, context) {
|
|
|
5244
5547
|
context.linkHoverToken,
|
|
5245
5548
|
context.foreground,
|
|
5246
5549
|
context.compositeBase,
|
|
5247
|
-
context.buttonContext
|
|
5550
|
+
context.buttonContext,
|
|
5551
|
+
context.solvedInks
|
|
5248
5552
|
]);
|
|
5249
5553
|
}
|
|
5250
5554
|
function resolveSurfaceSpec(spec, context) {
|
|
@@ -5318,6 +5622,7 @@ function resolveOwnedSurface(paint, context, measuredBackdrop) {
|
|
|
5318
5622
|
const decision = {
|
|
5319
5623
|
kind: "owned",
|
|
5320
5624
|
foreground,
|
|
5625
|
+
inks: ownedInkPublication(resolved, context),
|
|
5321
5626
|
decorations: surfaceDecorationColors(resolved, context.palette),
|
|
5322
5627
|
hoverDecoration: resolved.kind === "known" ? resolved.hoverDecoration : "underline",
|
|
5323
5628
|
buttonContext: resolveSurfaceButtons(paint, resolved, context)
|
|
@@ -5327,11 +5632,11 @@ function resolveOwnedSurface(paint, context, measuredBackdrop) {
|
|
|
5327
5632
|
paint,
|
|
5328
5633
|
contrast: { kind: "unmeasured" }
|
|
5329
5634
|
};
|
|
5330
|
-
const contrast = surfaceContrast(resolved, context.palette, context.colourIndex);
|
|
5331
|
-
return contrast.kind === "measured" ? {
|
|
5635
|
+
const contrast$1 = surfaceContrast(resolved, context.palette, context.colourIndex);
|
|
5636
|
+
return contrast$1.kind === "measured" ? {
|
|
5332
5637
|
...decision,
|
|
5333
5638
|
paint,
|
|
5334
|
-
contrast
|
|
5639
|
+
contrast: contrast$1
|
|
5335
5640
|
} : {
|
|
5336
5641
|
...decision,
|
|
5337
5642
|
paint,
|
|
@@ -5342,6 +5647,11 @@ function resolveOwnedSurface(paint, context, measuredBackdrop) {
|
|
|
5342
5647
|
}
|
|
5343
5648
|
};
|
|
5344
5649
|
}
|
|
5650
|
+
/** A paint the element owns but that resolves as inherited (an empty token) still blocks outer inks. */
|
|
5651
|
+
function ownedInkPublication(resolved, context) {
|
|
5652
|
+
const publication = surfaceInkPublication(resolved, context.solvedInks ?? "withhold");
|
|
5653
|
+
return publication.kind === "none" ? { kind: "reset" } : publication;
|
|
5654
|
+
}
|
|
5345
5655
|
/** Unprovable legacy paint retains its declared foreground instead of inventing one. */
|
|
5346
5656
|
function ownedForegroundRoles(resolved, text, candidates) {
|
|
5347
5657
|
if (resolved.kind === "known") return resolved.roles;
|
|
@@ -5390,7 +5700,7 @@ function surfaceContrast(foreground, palette, colourIndex) {
|
|
|
5390
5700
|
}
|
|
5391
5701
|
/** Runtime backgrounds retain authored media, compositing and unknown foregrounds. */
|
|
5392
5702
|
function resolveSectionSurface(background, context) {
|
|
5393
|
-
const foreground = background === null ? { kind: "
|
|
5703
|
+
const foreground = background === null ? { kind: "inherits" } : resolveSurfaceForeground(background, context.palette, {
|
|
5394
5704
|
linkHover: context.linkHoverToken,
|
|
5395
5705
|
...context.foreground
|
|
5396
5706
|
}, { compositeBase: context.compositeBase });
|
|
@@ -5404,6 +5714,7 @@ function resolveSectionSurface(background, context) {
|
|
|
5404
5714
|
const result = {
|
|
5405
5715
|
kind: "section",
|
|
5406
5716
|
foreground,
|
|
5717
|
+
inks: surfaceInkPublication(foreground, context.solvedInks ?? "withhold"),
|
|
5407
5718
|
buttonStyles: resolveSurfaceButtonContext({
|
|
5408
5719
|
surface: surface$1,
|
|
5409
5720
|
palette: revision.palette,
|
|
@@ -5442,8 +5753,8 @@ function resolveTintSurface(spec, context) {
|
|
|
5442
5753
|
}, context);
|
|
5443
5754
|
const source = spec.source === "text" ? base.foreground.text : base.decorations?.mark;
|
|
5444
5755
|
const token = source ? resolveOpaqueSurfaceHex(`${source}/${spec.opacity}`, context.palette, spec.base) : null;
|
|
5445
|
-
const hex = source ? resolveConcreteColorHex(source, context.palette) : null;
|
|
5446
|
-
const tinted = token ?? (hex ? resolveOpaqueSurfaceHex(hexToRgba(hex, spec.opacity / 100), context.palette, spec.base) : null);
|
|
5756
|
+
const hex$1 = source ? resolveConcreteColorHex(source, context.palette) : null;
|
|
5757
|
+
const tinted = token ?? (hex$1 ? resolveOpaqueSurfaceHex(hexToRgba(hex$1, spec.opacity / 100), context.palette, spec.base) : null);
|
|
5447
5758
|
if (!tinted) throw new Error(`Cannot derive tint on ${spec.base}`);
|
|
5448
5759
|
const solid = resolveDeclaredSolidSurface({
|
|
5449
5760
|
kind: "solid",
|
|
@@ -5456,6 +5767,7 @@ function resolveTintSurface(spec, context) {
|
|
|
5456
5767
|
assertTintSamples(solid, spec.base, context.palette);
|
|
5457
5768
|
return {
|
|
5458
5769
|
...solid,
|
|
5770
|
+
inks: { kind: "reset" },
|
|
5459
5771
|
tintBase: spec.base
|
|
5460
5772
|
};
|
|
5461
5773
|
}
|
|
@@ -5479,8 +5791,8 @@ function assertTintSamples(surface$1, base, palette) {
|
|
|
5479
5791
|
if (!safe) throw new RangeError(`No checked tint gradient for ${role} on ${base}`);
|
|
5480
5792
|
});
|
|
5481
5793
|
}
|
|
5482
|
-
function hexToRgba(hex, alpha) {
|
|
5483
|
-
const { r, g, b } = hexToRgb(hex);
|
|
5794
|
+
function hexToRgba(hex$1, alpha) {
|
|
5795
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
5484
5796
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
5485
5797
|
}
|
|
5486
5798
|
const foregroundInputSchema = z.object({
|
|
@@ -5503,6 +5815,7 @@ const paintInputSchema = z.discriminatedUnion("kind", [
|
|
|
5503
5815
|
z.object({ kind: z.literal("transparent") })
|
|
5504
5816
|
]);
|
|
5505
5817
|
const buttonInputSchema = z.object({ primary: opaqueButtonColourTokenSchema });
|
|
5818
|
+
const solvedInksSchema = z.enum(["publish", "withhold"]);
|
|
5506
5819
|
const surfaceSpecSchema = z.discriminatedUnion("kind", [
|
|
5507
5820
|
z.object({
|
|
5508
5821
|
kind: z.literal("recipe"),
|
|
@@ -5539,16 +5852,21 @@ const surfaceTransportSchema = z.object({ $surface: z.object({
|
|
|
5539
5852
|
foreground: foregroundInputSchema.optional(),
|
|
5540
5853
|
linkHoverToken: z.string().optional(),
|
|
5541
5854
|
compositeBase: z.string().optional(),
|
|
5542
|
-
buttonContext: buttonInputSchema.optional()
|
|
5855
|
+
buttonContext: buttonInputSchema.optional(),
|
|
5856
|
+
solvedInks: solvedInksSchema.optional()
|
|
5543
5857
|
})
|
|
5544
5858
|
}) });
|
|
5545
5859
|
/** Deserialised output is never evidence: parse the inputs and rerun their owner. */
|
|
5546
5860
|
function parseResolvedSurface(value) {
|
|
5547
|
-
|
|
5548
|
-
|
|
5861
|
+
return resolveTransportedSurface(value);
|
|
5862
|
+
}
|
|
5863
|
+
function resolveTransportedSurface(value, solvedInks) {
|
|
5864
|
+
if (isResolvedSurface(value) && solvedInks === undefined) return value;
|
|
5865
|
+
const { spec, context } = isResolvedSurface(value) ? serializeResolvedSurface(value).$surface : surfaceTransportSchema.parse(value).$surface;
|
|
5549
5866
|
return resolveSurface(spec, {
|
|
5550
5867
|
...context,
|
|
5551
|
-
buttonSystem: context.buttonSystem
|
|
5868
|
+
buttonSystem: context.buttonSystem,
|
|
5869
|
+
solvedInks: solvedInks ?? context.solvedInks
|
|
5552
5870
|
});
|
|
5553
5871
|
}
|
|
5554
5872
|
/** Wire only the owner inputs; parsed CSS/results are recomputed, never trusted. */
|
|
@@ -5568,7 +5886,8 @@ function certifySurface(spec, context) {
|
|
|
5568
5886
|
foreground: context.foreground,
|
|
5569
5887
|
linkHoverToken: context.linkHoverToken,
|
|
5570
5888
|
compositeBase: context.compositeBase,
|
|
5571
|
-
buttonContext: context.buttonContext
|
|
5889
|
+
buttonContext: context.buttonContext,
|
|
5890
|
+
solvedInks: context.solvedInks
|
|
5572
5891
|
})
|
|
5573
5892
|
})
|
|
5574
5893
|
};
|
|
@@ -5614,7 +5933,10 @@ function serializeSurfaceTree(value) {
|
|
|
5614
5933
|
};
|
|
5615
5934
|
const encode = (item) => {
|
|
5616
5935
|
const revision = isSurfaceTreeRecord(item) ? surfaceRevisionOwners.get(item) : undefined;
|
|
5617
|
-
if (revision) return {
|
|
5936
|
+
if (revision) return {
|
|
5937
|
+
$surfaceRevision: revisionIndex(revision),
|
|
5938
|
+
solvedInks: isSurfaceTreeRecord(item) ? item.solvedInks : undefined
|
|
5939
|
+
};
|
|
5618
5940
|
if (isResolvedSurface(item)) {
|
|
5619
5941
|
const input = serializeResolvedSurface(item).$surface;
|
|
5620
5942
|
const index = revisionIndex(item.revision);
|
|
@@ -5647,19 +5969,28 @@ const surfaceReferenceSchema = z.object({ $surface: z.object({
|
|
|
5647
5969
|
foreground: foregroundInputSchema.optional(),
|
|
5648
5970
|
linkHoverToken: z.string().optional(),
|
|
5649
5971
|
compositeBase: z.string().optional(),
|
|
5650
|
-
buttonContext: buttonInputSchema.optional()
|
|
5972
|
+
buttonContext: buttonInputSchema.optional(),
|
|
5973
|
+
solvedInks: solvedInksSchema.optional()
|
|
5651
5974
|
})
|
|
5652
5975
|
}) });
|
|
5653
|
-
const surfaceRevisionReferenceSchema = z.object({
|
|
5976
|
+
const surfaceRevisionReferenceSchema = z.object({
|
|
5977
|
+
$surfaceRevision: z.number().int().nonnegative(),
|
|
5978
|
+
solvedInks: solvedInksSchema.optional()
|
|
5979
|
+
});
|
|
5654
5980
|
/** Recreate owner results before any island's ordinary schema consumes them. */
|
|
5655
|
-
function parseSurfaceTree(value) {
|
|
5656
|
-
if (
|
|
5657
|
-
|
|
5981
|
+
function parseSurfaceTree(value, solvedInks) {
|
|
5982
|
+
if (isResolvedSurface(value)) return resolveTransportedSurface(value, solvedInks);
|
|
5983
|
+
const revision = isSurfaceTreeRecord(value) ? surfaceRevisionOwners.get(value) : undefined;
|
|
5984
|
+
if (revision) return solvedInks === undefined ? value : surfaceTransportContext({
|
|
5985
|
+
...revision,
|
|
5986
|
+
solvedInks
|
|
5987
|
+
});
|
|
5988
|
+
if (!isSurfaceTreeRecord(value) || !("$surfaceTransport" in value)) return mapSurfaceTreeChildren(value, (item) => parseSurfaceTree(item, solvedInks));
|
|
5658
5989
|
const envelope = surfaceEnvelopeSchema.parse(value).$surfaceTransport;
|
|
5659
|
-
const revisions = envelope.revisions.map((revision) => {
|
|
5990
|
+
const revisions = envelope.revisions.map((revision$1) => {
|
|
5660
5991
|
const owned = ownedThemeSnapshot({
|
|
5661
|
-
palette: revision.palette,
|
|
5662
|
-
buttonSystem: revision.buttonSystem
|
|
5992
|
+
palette: revision$1.palette,
|
|
5993
|
+
buttonSystem: revision$1.buttonSystem
|
|
5663
5994
|
});
|
|
5664
5995
|
registerOwnedRevisionInputs(owned);
|
|
5665
5996
|
return ownSurfaceRevision(owned);
|
|
@@ -5667,18 +5998,23 @@ function parseSurfaceTree(value) {
|
|
|
5667
5998
|
const decode = (item) => {
|
|
5668
5999
|
if (isSurfaceTreeRecord(item) && "$surfaceRevision" in item) {
|
|
5669
6000
|
const reference = surfaceRevisionReferenceSchema.parse(item);
|
|
5670
|
-
const revision = revisions[reference.$surfaceRevision];
|
|
5671
|
-
if (!revision) throw new Error("Unknown surface revision");
|
|
5672
|
-
|
|
6001
|
+
const revision$1 = revisions[reference.$surfaceRevision];
|
|
6002
|
+
if (!revision$1) throw new Error("Unknown surface revision");
|
|
6003
|
+
const policy = solvedInks ?? reference.solvedInks;
|
|
6004
|
+
return policy === undefined ? revision$1 : surfaceTransportContext({
|
|
6005
|
+
...revision$1,
|
|
6006
|
+
solvedInks: policy
|
|
6007
|
+
});
|
|
5673
6008
|
}
|
|
5674
6009
|
if (isSurfaceTreeRecord(item) && "$surface" in item) {
|
|
5675
6010
|
const request = surfaceReferenceSchema.parse(item).$surface;
|
|
5676
|
-
const revision = revisions[request.revision];
|
|
5677
|
-
if (!revision) throw new Error("Unknown surface revision");
|
|
6011
|
+
const revision$1 = revisions[request.revision];
|
|
6012
|
+
if (!revision$1) throw new Error("Unknown surface revision");
|
|
5678
6013
|
return resolveSurface(request.spec, {
|
|
5679
|
-
...revision,
|
|
6014
|
+
...revision$1,
|
|
5680
6015
|
...request.context,
|
|
5681
|
-
buttonSystem: revision.buttonSystem
|
|
6016
|
+
buttonSystem: revision$1.buttonSystem,
|
|
6017
|
+
solvedInks: solvedInks ?? request.context.solvedInks
|
|
5682
6018
|
});
|
|
5683
6019
|
}
|
|
5684
6020
|
return mapSurfaceTreeChildren(item, decode);
|
|
@@ -5783,9 +6119,9 @@ function makeBoundaryId(previousBlockId, nextBlockId) {
|
|
|
5783
6119
|
function assertNever$1(value, message = "Unhandled Theme V2 variant") {
|
|
5784
6120
|
throw new Error(`${message}: ${String(value)}`);
|
|
5785
6121
|
}
|
|
5786
|
-
function brandNonEmptyString(value, label) {
|
|
6122
|
+
function brandNonEmptyString(value, label$1) {
|
|
5787
6123
|
if (value.length === 0) {
|
|
5788
|
-
throw new Error(`${label} must be a non-empty string.`);
|
|
6124
|
+
throw new Error(`${label$1} must be a non-empty string.`);
|
|
5789
6125
|
}
|
|
5790
6126
|
return value;
|
|
5791
6127
|
}
|
|
@@ -7229,6 +7565,14 @@ const blockAppearancePresets = [
|
|
|
7229
7565
|
slotRoles: { copy: "card" },
|
|
7230
7566
|
styleFallback: [id$1("hero-photo-beside-text")]
|
|
7231
7567
|
}),
|
|
7568
|
+
defineArrangementLook("hero-text-over-photo", "hero", "Text over photo", "classic", [], {
|
|
7569
|
+
photoTreatment: "text-overlay",
|
|
7570
|
+
whenMissing: [{
|
|
7571
|
+
fact: "photo",
|
|
7572
|
+
look: id$1("hero-page-head")
|
|
7573
|
+
}],
|
|
7574
|
+
styleFallback: [id$1("hero-photo-behind-text")]
|
|
7575
|
+
}),
|
|
7232
7576
|
defineArrangementLook("hero-short-photo-banner", "hero", "Short photo banner", "classic", [], {
|
|
7233
7577
|
slotRoles: { copy: "card" },
|
|
7234
7578
|
styleFallback: [id$1("hero-page-head")]
|
|
@@ -11897,8 +12241,8 @@ function getRasterAssetIds(assets) {
|
|
|
11897
12241
|
function getMissingAssetUsageIds(input) {
|
|
11898
12242
|
return input.rasterAssetIds.filter((assetId) => {
|
|
11899
12243
|
if (input.inFlightAssetIds.has(assetId)) return false;
|
|
11900
|
-
const status = input.storedStatusByAssetId[assetId];
|
|
11901
|
-
return status === undefined || status.kind === "unknown";
|
|
12244
|
+
const status$1 = input.storedStatusByAssetId[assetId];
|
|
12245
|
+
return status$1 === undefined || status$1.kind === "unknown";
|
|
11902
12246
|
});
|
|
11903
12247
|
}
|
|
11904
12248
|
function markAssetUsageChecking(statusByAssetId, assetIds) {
|
|
@@ -13953,12 +14297,31 @@ function parseCanonicalRuntimeBackground(content) {
|
|
|
13953
14297
|
if (!background) return { kind: "none" };
|
|
13954
14298
|
const parsed = runtimeBoxBackgroundSchema.safeParse(background);
|
|
13955
14299
|
if (!parsed.success) return { kind: "none" };
|
|
13956
|
-
|
|
14300
|
+
const painted = applyLookPhotoTreatment(content, parsed.data);
|
|
14301
|
+
return painted.contentOwner === pageDesignEditorBackgroundOverrideOwner ? {
|
|
13957
14302
|
kind: "page-design-override",
|
|
13958
|
-
background:
|
|
14303
|
+
background: painted
|
|
13959
14304
|
} : {
|
|
13960
14305
|
kind: "block-authored",
|
|
13961
|
-
background:
|
|
14306
|
+
background: painted
|
|
14307
|
+
};
|
|
14308
|
+
}
|
|
14309
|
+
/** The Look owns shading and ink together; authored image/crop stay intact. */
|
|
14310
|
+
function applyLookPhotoTreatment(content, background) {
|
|
14311
|
+
const look$1 = readRecord(readRecord(content._effectiveLook)?.look);
|
|
14312
|
+
const structure = readRecord(look$1?.structure);
|
|
14313
|
+
if (background.type !== "image" || structure?.photoTreatment !== "text-overlay") return background;
|
|
14314
|
+
return {
|
|
14315
|
+
...background,
|
|
14316
|
+
overlay: {
|
|
14317
|
+
type: "gradient",
|
|
14318
|
+
opacity: 1,
|
|
14319
|
+
gradient: "var(--rb-opening-photo-shade, linear-gradient(transparent, transparent)), linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.76) 25%, rgba(0,0,0,0.4) 46%, transparent 72%)"
|
|
14320
|
+
},
|
|
14321
|
+
textColor: "neutral-50",
|
|
14322
|
+
headingColor: "neutral-50",
|
|
14323
|
+
linkColor: "neutral-50",
|
|
14324
|
+
supportingColor: "neutral-50"
|
|
13962
14325
|
};
|
|
13963
14326
|
}
|
|
13964
14327
|
function resolveSectionBackground(source, lookDefault, rhythm, arrangementHeroPhoto) {
|
|
@@ -14015,8 +14378,15 @@ function resolveSectionForeground(background, palette) {
|
|
|
14015
14378
|
* `resolveSectionBackground` instead.
|
|
14016
14379
|
*/
|
|
14017
14380
|
function resolveCompiledSectionForegroundFromContent(content, palette, photo) {
|
|
14018
|
-
|
|
14019
|
-
|
|
14381
|
+
return resolveCompiledSectionContextFromContent(content, palette, photo).openingForeground;
|
|
14382
|
+
}
|
|
14383
|
+
/** Resolve the compiled ground once for every opening appearance consumer. */
|
|
14384
|
+
function resolveCompiledSectionContextFromContent(content, palette, photo) {
|
|
14385
|
+
const background = content ? resolveSectionBackground(parseCanonicalRuntimeBackground(content), null, compiledOpeningRhythm(content), photo) : { kind: "base" };
|
|
14386
|
+
return {
|
|
14387
|
+
openingForeground: resolveSectionForeground(background, palette),
|
|
14388
|
+
openingPhotoBehindText: resolveSectionPhotoPlacement(background, photo) === "behind-text"
|
|
14389
|
+
};
|
|
14020
14390
|
}
|
|
14021
14391
|
/** Legacy detached photos keep already-legible authored ink and its exact CSS treatment. */
|
|
14022
14392
|
function sectionForegroundBackground(resolved, palette) {
|
|
@@ -14024,7 +14394,7 @@ function sectionForegroundBackground(resolved, palette) {
|
|
|
14024
14394
|
if (resolved.kind !== "content-media") return resolved.background;
|
|
14025
14395
|
const original = resolveSurfaceForeground(resolved.background, palette);
|
|
14026
14396
|
const measured = resolveSurfaceForeground(resolved.ground, palette);
|
|
14027
|
-
if (original.kind
|
|
14397
|
+
if (original.kind !== "known" || measured.kind !== "known") return resolved.background;
|
|
14028
14398
|
const roles = [
|
|
14029
14399
|
"text",
|
|
14030
14400
|
"heading",
|
|
@@ -14129,9 +14499,9 @@ function sectionScrimSamples(background, palette) {
|
|
|
14129
14499
|
const overlay = sectionVeil(background);
|
|
14130
14500
|
if (overlay?.type === "gradient") return gradientScrimSamples(overlay.gradient ?? "", overlay.opacity ?? 1, palette);
|
|
14131
14501
|
if (overlay?.type !== "color") return [];
|
|
14132
|
-
const hex = resolveConcreteColorHex(overlay.color ?? "", palette);
|
|
14133
|
-
return hex ? [{
|
|
14134
|
-
hex,
|
|
14502
|
+
const hex$1 = resolveConcreteColorHex(overlay.color ?? "", palette);
|
|
14503
|
+
return hex$1 ? [{
|
|
14504
|
+
hex: hex$1,
|
|
14135
14505
|
alpha: overlay.opacity ?? 1
|
|
14136
14506
|
}] : [];
|
|
14137
14507
|
}
|
|
@@ -14860,6 +15230,10 @@ function isRecord$1(value) {
|
|
|
14860
15230
|
|
|
14861
15231
|
//#endregion
|
|
14862
15232
|
//#region ../theme-core/src/header/rootPaint.ts
|
|
15233
|
+
/** Detached branding needs protection even when the navigation has its own rail. */
|
|
15234
|
+
function headerNeedsPhotoShade(header) {
|
|
15235
|
+
return header.variant === "floating" && header.background?.type === "color" && header.background.color === "transparent" && (!header.container || (header.container.opacity ?? 1) < 1);
|
|
15236
|
+
}
|
|
14863
15237
|
/** The nav container takes precedence when a floating header also has one. */
|
|
14864
15238
|
function headerRootPaint(header) {
|
|
14865
15239
|
const navContainerType = header?.navContainer?.type ?? "none";
|
|
@@ -14992,7 +15366,7 @@ function applyFooterBackgroundOverride(theme, footer, override) {
|
|
|
14992
15366
|
}
|
|
14993
15367
|
function assertChromeBackgroundHasForeground(theme, background) {
|
|
14994
15368
|
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
14995
|
-
if (resolved.kind
|
|
15369
|
+
if (resolved.kind !== "known") {
|
|
14996
15370
|
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
14997
15371
|
}
|
|
14998
15372
|
}
|
|
@@ -15803,6 +16177,730 @@ function resolveCardRole(theme, cardSystem, role) {
|
|
|
15803
16177
|
}
|
|
15804
16178
|
}
|
|
15805
16179
|
|
|
16180
|
+
//#endregion
|
|
16181
|
+
//#region ../theme-core/src/site-styles/headerDropdownRecipe.ts
|
|
16182
|
+
const neutralHeaderDropdown = {
|
|
16183
|
+
surface: {
|
|
16184
|
+
kind: "solid",
|
|
16185
|
+
background: "surface"
|
|
16186
|
+
},
|
|
16187
|
+
textColor: "text",
|
|
16188
|
+
borderColor: "border",
|
|
16189
|
+
shadow: "md",
|
|
16190
|
+
borderRadius: "md",
|
|
16191
|
+
textTransform: "none",
|
|
16192
|
+
letterSpacing: "normal"
|
|
16193
|
+
};
|
|
16194
|
+
const brandHeaderDropdown = {
|
|
16195
|
+
surface: {
|
|
16196
|
+
kind: "solid",
|
|
16197
|
+
background: "primary-700"
|
|
16198
|
+
},
|
|
16199
|
+
textColor: "primaryForeground",
|
|
16200
|
+
borderColor: null,
|
|
16201
|
+
shadow: "md",
|
|
16202
|
+
borderRadius: "md",
|
|
16203
|
+
hoverBackground: "primary-900",
|
|
16204
|
+
hoverTextColor: "secondary",
|
|
16205
|
+
textTransform: "uppercase",
|
|
16206
|
+
letterSpacing: "wider",
|
|
16207
|
+
fontWeight: "bold",
|
|
16208
|
+
textSize: "sm"
|
|
16209
|
+
};
|
|
16210
|
+
const clearSplitHeaderDropdown = {
|
|
16211
|
+
...neutralHeaderDropdown,
|
|
16212
|
+
surface: {
|
|
16213
|
+
kind: "frosted",
|
|
16214
|
+
background: "surface",
|
|
16215
|
+
tone: "opening",
|
|
16216
|
+
opacity: .45,
|
|
16217
|
+
blur: "md"
|
|
16218
|
+
},
|
|
16219
|
+
textColor: "text",
|
|
16220
|
+
shadow: "sm",
|
|
16221
|
+
borderRadius: "sm",
|
|
16222
|
+
textSize: "base",
|
|
16223
|
+
fontWeight: "medium",
|
|
16224
|
+
hoverBackground: "text"
|
|
16225
|
+
};
|
|
16226
|
+
/** Solid menus retain their existing compiled bytes; no second runtime authority. */
|
|
16227
|
+
function compileHeaderDropdownRecipe(recipe) {
|
|
16228
|
+
const { surface: surface$1, ...style } = recipe;
|
|
16229
|
+
return {
|
|
16230
|
+
...style,
|
|
16231
|
+
background: surface$1.background,
|
|
16232
|
+
...surface$1.kind === "frosted" ? { surfaceTreatment: {
|
|
16233
|
+
kind: surface$1.kind,
|
|
16234
|
+
tone: surface$1.tone,
|
|
16235
|
+
opacity: surface$1.opacity,
|
|
16236
|
+
blur: surface$1.blur
|
|
16237
|
+
} } : {}
|
|
16238
|
+
};
|
|
16239
|
+
}
|
|
16240
|
+
|
|
16241
|
+
//#endregion
|
|
16242
|
+
//#region ../theme-core/src/site-styles/bookingPageLookIds.ts
|
|
16243
|
+
/** Deterministic fallback order; Fulcrum is the default. */
|
|
16244
|
+
const bookingPageLookIdValues = [
|
|
16245
|
+
"site-style:fulcrum",
|
|
16246
|
+
"site-style:folio",
|
|
16247
|
+
"site-style:measure",
|
|
16248
|
+
"site-style:grounded-practice",
|
|
16249
|
+
"site-style:lantern",
|
|
16250
|
+
"site-style:poster"
|
|
16251
|
+
];
|
|
16252
|
+
/** Boundary admission for draft and suggestion inputs. */
|
|
16253
|
+
function parseBookingPageLookId(value) {
|
|
16254
|
+
const id$2 = bookingPageLookIdValues.find((candidate$1) => candidate$1 === value);
|
|
16255
|
+
return id$2 ? asSiteStyleId(id$2) : null;
|
|
16256
|
+
}
|
|
16257
|
+
|
|
16258
|
+
//#endregion
|
|
16259
|
+
//#region ../theme-core/src/site-styles/ownColours/bookingLookColourSpecs.ts
|
|
16260
|
+
const bookingLookGroundLimits = {
|
|
16261
|
+
"site-style:poster": {
|
|
16262
|
+
lightness: [.9, 1],
|
|
16263
|
+
maxChroma: .04
|
|
16264
|
+
},
|
|
16265
|
+
"site-style:fulcrum": {
|
|
16266
|
+
lightness: [.9, 1],
|
|
16267
|
+
maxChroma: .03
|
|
16268
|
+
},
|
|
16269
|
+
"site-style:folio": {
|
|
16270
|
+
lightness: [.88, 1],
|
|
16271
|
+
maxChroma: .05
|
|
16272
|
+
},
|
|
16273
|
+
"site-style:measure": {
|
|
16274
|
+
lightness: [.9, 1],
|
|
16275
|
+
maxChroma: .02
|
|
16276
|
+
},
|
|
16277
|
+
"site-style:grounded-practice": {
|
|
16278
|
+
lightness: [.85, 1],
|
|
16279
|
+
maxChroma: .06
|
|
16280
|
+
},
|
|
16281
|
+
"site-style:lantern": {
|
|
16282
|
+
lightness: [.12, .34],
|
|
16283
|
+
maxChroma: .07
|
|
16284
|
+
}
|
|
16285
|
+
};
|
|
16286
|
+
const contrast = (against, ratio$1 = 3) => ({
|
|
16287
|
+
kind: "contrast",
|
|
16288
|
+
against,
|
|
16289
|
+
ratio: ratio$1
|
|
16290
|
+
});
|
|
16291
|
+
const distinct = (from, deltaE) => ({
|
|
16292
|
+
kind: "distinct",
|
|
16293
|
+
from,
|
|
16294
|
+
deltaE
|
|
16295
|
+
});
|
|
16296
|
+
const label = { kind: "label" };
|
|
16297
|
+
/** The quiet light grounds the five light looks share: faint tints of the leading colour, inside the look's limit. */
|
|
16298
|
+
function lightGrounds(limits, tint$1) {
|
|
16299
|
+
const page = Math.min(tint$1.page, limits.maxChroma);
|
|
16300
|
+
const band = Math.min(tint$1.band, limits.maxChroma);
|
|
16301
|
+
return {
|
|
16302
|
+
background: {
|
|
16303
|
+
l: [.955, .985],
|
|
16304
|
+
c: [0, page],
|
|
16305
|
+
chromaShare: .12,
|
|
16306
|
+
target: .972,
|
|
16307
|
+
name: "the page"
|
|
16308
|
+
},
|
|
16309
|
+
surface: {
|
|
16310
|
+
l: [.99, 1],
|
|
16311
|
+
c: [0, Math.min(page, .004)],
|
|
16312
|
+
chromaShare: .04,
|
|
16313
|
+
target: .997,
|
|
16314
|
+
name: "the cards"
|
|
16315
|
+
},
|
|
16316
|
+
surfaceAlt: {
|
|
16317
|
+
l: [.93, .965],
|
|
16318
|
+
c: [0, band],
|
|
16319
|
+
chromaShare: .25,
|
|
16320
|
+
target: .948,
|
|
16321
|
+
name: "the quieter panels",
|
|
16322
|
+
needs: [distinct("background", .015)]
|
|
16323
|
+
},
|
|
16324
|
+
muted: {
|
|
16325
|
+
l: tint$1.bandL,
|
|
16326
|
+
c: [0, band],
|
|
16327
|
+
chromaShare: .3,
|
|
16328
|
+
target: (tint$1.bandL[0] + tint$1.bandL[1]) / 2,
|
|
16329
|
+
name: "the band",
|
|
16330
|
+
needs: [distinct("background", .03)]
|
|
16331
|
+
},
|
|
16332
|
+
border: {
|
|
16333
|
+
l: [.8, .88],
|
|
16334
|
+
c: [0, band],
|
|
16335
|
+
chromaShare: .25,
|
|
16336
|
+
target: .85,
|
|
16337
|
+
name: "the rules"
|
|
16338
|
+
}
|
|
16339
|
+
};
|
|
16340
|
+
}
|
|
16341
|
+
const groundOrder = [
|
|
16342
|
+
"background",
|
|
16343
|
+
"surface",
|
|
16344
|
+
"muted",
|
|
16345
|
+
"surfaceAlt",
|
|
16346
|
+
"border"
|
|
16347
|
+
];
|
|
16348
|
+
const lightReading = [
|
|
16349
|
+
"background",
|
|
16350
|
+
"surface",
|
|
16351
|
+
"surfaceAlt",
|
|
16352
|
+
"muted"
|
|
16353
|
+
];
|
|
16354
|
+
/** Fulcrum: a quiet stone page, white sheets, a strong mid-tone on the buttons, a deep plate behind the opening panel. */
|
|
16355
|
+
const fulcrum = {
|
|
16356
|
+
roles: {
|
|
16357
|
+
...lightGrounds(bookingLookGroundLimits["site-style:fulcrum"], {
|
|
16358
|
+
page: .008,
|
|
16359
|
+
band: .022,
|
|
16360
|
+
bandL: [.9, .94]
|
|
16361
|
+
}),
|
|
16362
|
+
primary: {
|
|
16363
|
+
l: [.3, .6],
|
|
16364
|
+
c: [.04, .16],
|
|
16365
|
+
target: .45,
|
|
16366
|
+
name: "the buttons",
|
|
16367
|
+
mention: true,
|
|
16368
|
+
needs: [
|
|
16369
|
+
contrast("background"),
|
|
16370
|
+
contrast("surface"),
|
|
16371
|
+
contrast("muted"),
|
|
16372
|
+
label
|
|
16373
|
+
]
|
|
16374
|
+
},
|
|
16375
|
+
secondary: {
|
|
16376
|
+
l: [.18, .36],
|
|
16377
|
+
c: [.02, .11],
|
|
16378
|
+
target: .28,
|
|
16379
|
+
name: "the plate",
|
|
16380
|
+
mention: true,
|
|
16381
|
+
needs: [contrast("surface"), distinct("primary", .08)]
|
|
16382
|
+
},
|
|
16383
|
+
accent: {
|
|
16384
|
+
l: [.38, .72],
|
|
16385
|
+
c: [.06, .17],
|
|
16386
|
+
target: .62,
|
|
16387
|
+
name: "the small marks",
|
|
16388
|
+
analogous: true,
|
|
16389
|
+
needs: [
|
|
16390
|
+
contrast("background"),
|
|
16391
|
+
contrast("surface"),
|
|
16392
|
+
contrast("muted"),
|
|
16393
|
+
distinct("primary", .1)
|
|
16394
|
+
]
|
|
16395
|
+
}
|
|
16396
|
+
},
|
|
16397
|
+
slots: [
|
|
16398
|
+
{
|
|
16399
|
+
roles: ["muted"],
|
|
16400
|
+
name: "the band"
|
|
16401
|
+
},
|
|
16402
|
+
{
|
|
16403
|
+
roles: ["secondary"],
|
|
16404
|
+
name: "the plate"
|
|
16405
|
+
},
|
|
16406
|
+
{
|
|
16407
|
+
roles: ["primary"],
|
|
16408
|
+
name: "the buttons"
|
|
16409
|
+
},
|
|
16410
|
+
{
|
|
16411
|
+
roles: ["accent"],
|
|
16412
|
+
name: "the small marks"
|
|
16413
|
+
}
|
|
16414
|
+
],
|
|
16415
|
+
order: [
|
|
16416
|
+
...groundOrder,
|
|
16417
|
+
"primary",
|
|
16418
|
+
"secondary",
|
|
16419
|
+
"accent"
|
|
16420
|
+
],
|
|
16421
|
+
neutrals: {
|
|
16422
|
+
dark: [.24, .02],
|
|
16423
|
+
light: [.995, .005]
|
|
16424
|
+
},
|
|
16425
|
+
reading: lightReading,
|
|
16426
|
+
groundLimit: bookingLookGroundLimits["site-style:fulcrum"].maxChroma,
|
|
16427
|
+
contrast: [{
|
|
16428
|
+
label: "a muted complement on the plate",
|
|
16429
|
+
hue: "complement",
|
|
16430
|
+
second: "secondary",
|
|
16431
|
+
lead: "primary",
|
|
16432
|
+
l: .3,
|
|
16433
|
+
c: [.04, .07]
|
|
16434
|
+
}, {
|
|
16435
|
+
label: "a warm colour on the buttons",
|
|
16436
|
+
hue: "split-warm",
|
|
16437
|
+
second: "primary",
|
|
16438
|
+
lead: "secondary",
|
|
16439
|
+
l: .45,
|
|
16440
|
+
c: [.08, .13]
|
|
16441
|
+
}]
|
|
16442
|
+
};
|
|
16443
|
+
/** Folio: warm paper, sheets and a band; buttons and links share the primary (hovering to its 800 shade); small rules and marks. */
|
|
16444
|
+
const folio = {
|
|
16445
|
+
roles: {
|
|
16446
|
+
...lightGrounds(bookingLookGroundLimits["site-style:folio"], {
|
|
16447
|
+
page: .012,
|
|
16448
|
+
band: .03,
|
|
16449
|
+
bandL: [.9, .94]
|
|
16450
|
+
}),
|
|
16451
|
+
primary: {
|
|
16452
|
+
l: [.3, .55],
|
|
16453
|
+
c: [.03, .14],
|
|
16454
|
+
target: .42,
|
|
16455
|
+
name: "the buttons and links",
|
|
16456
|
+
mention: true,
|
|
16457
|
+
link: true,
|
|
16458
|
+
needs: [
|
|
16459
|
+
contrast("background"),
|
|
16460
|
+
contrast("surface"),
|
|
16461
|
+
contrast("muted"),
|
|
16462
|
+
label
|
|
16463
|
+
]
|
|
16464
|
+
},
|
|
16465
|
+
secondary: {
|
|
16466
|
+
l: [.45, .62],
|
|
16467
|
+
c: [.01, .04],
|
|
16468
|
+
target: .54,
|
|
16469
|
+
name: "the quiet details"
|
|
16470
|
+
},
|
|
16471
|
+
accent: {
|
|
16472
|
+
l: [.38, .68],
|
|
16473
|
+
c: [.05, .15],
|
|
16474
|
+
target: .6,
|
|
16475
|
+
name: "the marks",
|
|
16476
|
+
mention: true,
|
|
16477
|
+
analogous: true,
|
|
16478
|
+
needs: [
|
|
16479
|
+
contrast("background"),
|
|
16480
|
+
contrast("surface"),
|
|
16481
|
+
contrast("muted"),
|
|
16482
|
+
distinct("primary", .1)
|
|
16483
|
+
]
|
|
16484
|
+
}
|
|
16485
|
+
},
|
|
16486
|
+
slots: [
|
|
16487
|
+
{
|
|
16488
|
+
roles: ["muted"],
|
|
16489
|
+
name: "the band"
|
|
16490
|
+
},
|
|
16491
|
+
{
|
|
16492
|
+
roles: ["primary"],
|
|
16493
|
+
name: "the buttons and links"
|
|
16494
|
+
},
|
|
16495
|
+
{
|
|
16496
|
+
roles: ["accent"],
|
|
16497
|
+
name: "the marks"
|
|
16498
|
+
}
|
|
16499
|
+
],
|
|
16500
|
+
order: [
|
|
16501
|
+
...groundOrder,
|
|
16502
|
+
"primary",
|
|
16503
|
+
"accent",
|
|
16504
|
+
"secondary"
|
|
16505
|
+
],
|
|
16506
|
+
neutrals: {
|
|
16507
|
+
dark: [.27, .015],
|
|
16508
|
+
light: [.985, .01]
|
|
16509
|
+
},
|
|
16510
|
+
reading: lightReading,
|
|
16511
|
+
groundLimit: bookingLookGroundLimits["site-style:folio"].maxChroma,
|
|
16512
|
+
contrast: [{
|
|
16513
|
+
label: "the complement on the buttons and links",
|
|
16514
|
+
hue: "complement",
|
|
16515
|
+
second: "primary",
|
|
16516
|
+
lead: "accent",
|
|
16517
|
+
l: .42,
|
|
16518
|
+
c: [.08, .13]
|
|
16519
|
+
}, {
|
|
16520
|
+
label: "a warm colour on the buttons and links",
|
|
16521
|
+
hue: "split-warm",
|
|
16522
|
+
second: "primary",
|
|
16523
|
+
lead: "accent",
|
|
16524
|
+
l: .42,
|
|
16525
|
+
c: [.08, .13]
|
|
16526
|
+
}]
|
|
16527
|
+
};
|
|
16528
|
+
/** Measure: one plain page. Flat buttons that fill with the secondary on hover, and a small square. */
|
|
16529
|
+
const measure = {
|
|
16530
|
+
roles: {
|
|
16531
|
+
...lightGrounds(bookingLookGroundLimits["site-style:measure"], {
|
|
16532
|
+
page: .008,
|
|
16533
|
+
band: .012,
|
|
16534
|
+
bandL: [.91, .94]
|
|
16535
|
+
}),
|
|
16536
|
+
primary: {
|
|
16537
|
+
l: [.18, .55],
|
|
16538
|
+
c: [0, .16],
|
|
16539
|
+
target: .26,
|
|
16540
|
+
derivedChroma: .035,
|
|
16541
|
+
name: "the buttons",
|
|
16542
|
+
needs: [
|
|
16543
|
+
contrast("background"),
|
|
16544
|
+
contrast("surface"),
|
|
16545
|
+
label
|
|
16546
|
+
]
|
|
16547
|
+
},
|
|
16548
|
+
secondary: {
|
|
16549
|
+
l: [.12, .5],
|
|
16550
|
+
c: [0, .16],
|
|
16551
|
+
target: {
|
|
16552
|
+
from: "primary",
|
|
16553
|
+
dl: -.06
|
|
16554
|
+
},
|
|
16555
|
+
hueFrom: "primary",
|
|
16556
|
+
name: "the button hover",
|
|
16557
|
+
needs: [contrast("background"), label]
|
|
16558
|
+
},
|
|
16559
|
+
accent: {
|
|
16560
|
+
l: [.4, .7],
|
|
16561
|
+
c: [.08, .2],
|
|
16562
|
+
target: .55,
|
|
16563
|
+
name: "the small square",
|
|
16564
|
+
mention: true,
|
|
16565
|
+
analogous: true,
|
|
16566
|
+
needs: [
|
|
16567
|
+
contrast("background"),
|
|
16568
|
+
contrast("surface"),
|
|
16569
|
+
distinct("primary", .1)
|
|
16570
|
+
]
|
|
16571
|
+
}
|
|
16572
|
+
},
|
|
16573
|
+
slots: [{
|
|
16574
|
+
roles: ["primary"],
|
|
16575
|
+
name: "the buttons"
|
|
16576
|
+
}, {
|
|
16577
|
+
roles: ["accent"],
|
|
16578
|
+
name: "the small square"
|
|
16579
|
+
}],
|
|
16580
|
+
order: [
|
|
16581
|
+
...groundOrder,
|
|
16582
|
+
"primary",
|
|
16583
|
+
"secondary",
|
|
16584
|
+
"accent"
|
|
16585
|
+
],
|
|
16586
|
+
neutrals: {
|
|
16587
|
+
dark: [.22, .012],
|
|
16588
|
+
light: [.995, .004]
|
|
16589
|
+
},
|
|
16590
|
+
reading: lightReading,
|
|
16591
|
+
groundLimit: bookingLookGroundLimits["site-style:measure"].maxChroma,
|
|
16592
|
+
contrast: [{
|
|
16593
|
+
label: "the complement on the square and the button hover",
|
|
16594
|
+
hue: "complement",
|
|
16595
|
+
second: "accent",
|
|
16596
|
+
lead: "primary",
|
|
16597
|
+
alsoTints: {
|
|
16598
|
+
roles: ["secondary"],
|
|
16599
|
+
name: "the button hover"
|
|
16600
|
+
},
|
|
16601
|
+
l: .55,
|
|
16602
|
+
c: [.1, .17]
|
|
16603
|
+
}, {
|
|
16604
|
+
label: "a warm colour on the buttons",
|
|
16605
|
+
hue: "split-warm",
|
|
16606
|
+
second: "primary",
|
|
16607
|
+
lead: "accent",
|
|
16608
|
+
l: .38,
|
|
16609
|
+
c: [.08, .13]
|
|
16610
|
+
}]
|
|
16611
|
+
};
|
|
16612
|
+
/** Grounded Practice: an inset panel on the muted colour opens the page; links and buttons share the primary. */
|
|
16613
|
+
const grounded = {
|
|
16614
|
+
roles: {
|
|
16615
|
+
...lightGrounds(bookingLookGroundLimits["site-style:grounded-practice"], {
|
|
16616
|
+
page: .012,
|
|
16617
|
+
band: .04,
|
|
16618
|
+
bandL: [.9, .945]
|
|
16619
|
+
}),
|
|
16620
|
+
primary: {
|
|
16621
|
+
l: [.3, .55],
|
|
16622
|
+
c: [.03, .14],
|
|
16623
|
+
target: .42,
|
|
16624
|
+
name: "the buttons and links",
|
|
16625
|
+
mention: true,
|
|
16626
|
+
link: true,
|
|
16627
|
+
needs: [
|
|
16628
|
+
contrast("background"),
|
|
16629
|
+
contrast("muted"),
|
|
16630
|
+
label,
|
|
16631
|
+
{
|
|
16632
|
+
kind: "ladder",
|
|
16633
|
+
step: 600,
|
|
16634
|
+
against: "muted",
|
|
16635
|
+
ratio: 3
|
|
16636
|
+
}
|
|
16637
|
+
]
|
|
16638
|
+
},
|
|
16639
|
+
secondary: {
|
|
16640
|
+
l: [.45, .62],
|
|
16641
|
+
c: [.01, .04],
|
|
16642
|
+
target: .54,
|
|
16643
|
+
name: "the quiet details"
|
|
16644
|
+
},
|
|
16645
|
+
accent: {
|
|
16646
|
+
l: [.38, .68],
|
|
16647
|
+
c: [.05, .15],
|
|
16648
|
+
target: .6,
|
|
16649
|
+
name: "the small marks",
|
|
16650
|
+
analogous: true,
|
|
16651
|
+
needs: [contrast("background"), distinct("primary", .1)]
|
|
16652
|
+
}
|
|
16653
|
+
},
|
|
16654
|
+
slots: [{
|
|
16655
|
+
roles: ["muted"],
|
|
16656
|
+
name: "the opening panel"
|
|
16657
|
+
}, {
|
|
16658
|
+
roles: ["primary"],
|
|
16659
|
+
name: "the buttons and links"
|
|
16660
|
+
}],
|
|
16661
|
+
order: [
|
|
16662
|
+
...groundOrder,
|
|
16663
|
+
"primary",
|
|
16664
|
+
"accent",
|
|
16665
|
+
"secondary"
|
|
16666
|
+
],
|
|
16667
|
+
neutrals: {
|
|
16668
|
+
dark: [.27, .015],
|
|
16669
|
+
light: [.985, .01]
|
|
16670
|
+
},
|
|
16671
|
+
reading: lightReading,
|
|
16672
|
+
groundLimit: bookingLookGroundLimits["site-style:grounded-practice"].maxChroma,
|
|
16673
|
+
contrast: [{
|
|
16674
|
+
label: "the complement on the buttons and links",
|
|
16675
|
+
hue: "complement",
|
|
16676
|
+
second: "primary",
|
|
16677
|
+
lead: "muted",
|
|
16678
|
+
l: .42,
|
|
16679
|
+
c: [.08, .13]
|
|
16680
|
+
}, {
|
|
16681
|
+
label: "a warm colour on the buttons and links",
|
|
16682
|
+
hue: "split-warm",
|
|
16683
|
+
second: "primary",
|
|
16684
|
+
lead: "muted",
|
|
16685
|
+
l: .42,
|
|
16686
|
+
c: [.08, .13]
|
|
16687
|
+
}]
|
|
16688
|
+
};
|
|
16689
|
+
/**
|
|
16690
|
+
* Lantern: always dark. A very dark page, a deep band, gilt buttons running
|
|
16691
|
+
* from the accent into the primary (one colour family, light into deeper),
|
|
16692
|
+
* and the accent on every ornament and the candle glow.
|
|
16693
|
+
*/
|
|
16694
|
+
const lanternLimits = bookingLookGroundLimits["site-style:lantern"];
|
|
16695
|
+
const lantern = {
|
|
16696
|
+
roles: {
|
|
16697
|
+
background: {
|
|
16698
|
+
l: [.14, .22],
|
|
16699
|
+
c: [0, Math.min(.03, lanternLimits.maxChroma)],
|
|
16700
|
+
chromaShare: .3,
|
|
16701
|
+
target: .18,
|
|
16702
|
+
name: "the page"
|
|
16703
|
+
},
|
|
16704
|
+
surface: {
|
|
16705
|
+
l: [.24, .3],
|
|
16706
|
+
c: [0, Math.min(.035, lanternLimits.maxChroma)],
|
|
16707
|
+
chromaShare: .3,
|
|
16708
|
+
target: .27,
|
|
16709
|
+
name: "the cards",
|
|
16710
|
+
needs: [distinct("background", .05)]
|
|
16711
|
+
},
|
|
16712
|
+
surfaceAlt: {
|
|
16713
|
+
l: [.28, .34],
|
|
16714
|
+
c: [0, Math.min(.035, lanternLimits.maxChroma)],
|
|
16715
|
+
chromaShare: .3,
|
|
16716
|
+
target: .31,
|
|
16717
|
+
name: "the quieter panels"
|
|
16718
|
+
},
|
|
16719
|
+
muted: {
|
|
16720
|
+
l: [.2, .32],
|
|
16721
|
+
c: [0, lanternLimits.maxChroma],
|
|
16722
|
+
chromaShare: .45,
|
|
16723
|
+
target: .235,
|
|
16724
|
+
name: "the band",
|
|
16725
|
+
needs: [distinct("background", .04)]
|
|
16726
|
+
},
|
|
16727
|
+
border: {
|
|
16728
|
+
l: [.35, .42],
|
|
16729
|
+
c: [0, .035],
|
|
16730
|
+
chromaShare: .3,
|
|
16731
|
+
target: .38,
|
|
16732
|
+
name: "the rules"
|
|
16733
|
+
},
|
|
16734
|
+
accent: {
|
|
16735
|
+
l: [.76, .9],
|
|
16736
|
+
c: [.03, .13],
|
|
16737
|
+
target: .86,
|
|
16738
|
+
name: "the ornaments and the light end of the gilt",
|
|
16739
|
+
mention: true,
|
|
16740
|
+
giltEnd: true,
|
|
16741
|
+
hueFrom: "primary",
|
|
16742
|
+
needs: [
|
|
16743
|
+
contrast("background"),
|
|
16744
|
+
contrast("muted"),
|
|
16745
|
+
label
|
|
16746
|
+
]
|
|
16747
|
+
},
|
|
16748
|
+
primary: {
|
|
16749
|
+
l: [.62, .82],
|
|
16750
|
+
c: [.03, .15],
|
|
16751
|
+
target: {
|
|
16752
|
+
from: "accent",
|
|
16753
|
+
dl: -.11
|
|
16754
|
+
},
|
|
16755
|
+
hueFrom: "accent",
|
|
16756
|
+
name: "the deep end of the gilt",
|
|
16757
|
+
giltEnd: true,
|
|
16758
|
+
needs: [
|
|
16759
|
+
contrast("background"),
|
|
16760
|
+
contrast("muted"),
|
|
16761
|
+
label,
|
|
16762
|
+
distinct("accent", .06)
|
|
16763
|
+
]
|
|
16764
|
+
},
|
|
16765
|
+
secondary: {
|
|
16766
|
+
l: [.24, .38],
|
|
16767
|
+
c: [.04, .11],
|
|
16768
|
+
target: .3,
|
|
16769
|
+
name: "the deep details"
|
|
16770
|
+
}
|
|
16771
|
+
},
|
|
16772
|
+
slots: [{
|
|
16773
|
+
roles: ["muted"],
|
|
16774
|
+
name: "the band"
|
|
16775
|
+
}, {
|
|
16776
|
+
roles: ["accent", "primary"],
|
|
16777
|
+
name: "the gilt buttons"
|
|
16778
|
+
}],
|
|
16779
|
+
order: [
|
|
16780
|
+
...groundOrder,
|
|
16781
|
+
"accent",
|
|
16782
|
+
"primary",
|
|
16783
|
+
"secondary"
|
|
16784
|
+
],
|
|
16785
|
+
neutrals: {
|
|
16786
|
+
dark: [.2, .03],
|
|
16787
|
+
light: [.95, .02]
|
|
16788
|
+
},
|
|
16789
|
+
reading: [
|
|
16790
|
+
"background",
|
|
16791
|
+
"surface",
|
|
16792
|
+
"surfaceAlt",
|
|
16793
|
+
"muted"
|
|
16794
|
+
],
|
|
16795
|
+
groundLimit: lanternLimits.maxChroma,
|
|
16796
|
+
contrast: [{
|
|
16797
|
+
label: "a warm complement on the gilt",
|
|
16798
|
+
hue: "warm-complement",
|
|
16799
|
+
second: "accent",
|
|
16800
|
+
lead: "muted",
|
|
16801
|
+
l: .84,
|
|
16802
|
+
c: [.08, .12]
|
|
16803
|
+
}, {
|
|
16804
|
+
label: "a second warm colour on the gilt",
|
|
16805
|
+
hue: "warm-other",
|
|
16806
|
+
second: "accent",
|
|
16807
|
+
lead: "muted",
|
|
16808
|
+
l: .84,
|
|
16809
|
+
c: [.08, .12]
|
|
16810
|
+
}]
|
|
16811
|
+
};
|
|
16812
|
+
/** Poster: the brand block is the one large area meant to be loud. Its buttons are the pop colour; elsewhere buttons are the block colour. */
|
|
16813
|
+
const posterBand = {
|
|
16814
|
+
roles: ["muted", "surfaceAlt"],
|
|
16815
|
+
name: "a faint tint of it on the band"
|
|
16816
|
+
};
|
|
16817
|
+
const poster = {
|
|
16818
|
+
roles: {
|
|
16819
|
+
...lightGrounds(bookingLookGroundLimits["site-style:poster"], {
|
|
16820
|
+
page: .008,
|
|
16821
|
+
band: .04,
|
|
16822
|
+
bandL: [.92, .96]
|
|
16823
|
+
}),
|
|
16824
|
+
primary: {
|
|
16825
|
+
l: [.3, .62],
|
|
16826
|
+
c: [.08, .25],
|
|
16827
|
+
target: .48,
|
|
16828
|
+
name: "the big block",
|
|
16829
|
+
mention: true,
|
|
16830
|
+
needs: [
|
|
16831
|
+
contrast("surface", 3.6),
|
|
16832
|
+
contrast("background"),
|
|
16833
|
+
label
|
|
16834
|
+
]
|
|
16835
|
+
},
|
|
16836
|
+
accent: {
|
|
16837
|
+
l: [.62, .95],
|
|
16838
|
+
c: [.08, .25],
|
|
16839
|
+
target: .86,
|
|
16840
|
+
name: "the pop buttons and stickers",
|
|
16841
|
+
mention: true,
|
|
16842
|
+
analogous: true,
|
|
16843
|
+
needs: [
|
|
16844
|
+
contrast("primary"),
|
|
16845
|
+
label,
|
|
16846
|
+
distinct("primary", .15)
|
|
16847
|
+
]
|
|
16848
|
+
},
|
|
16849
|
+
secondary: {
|
|
16850
|
+
l: [.15, .32],
|
|
16851
|
+
c: [.03, .15],
|
|
16852
|
+
target: .24,
|
|
16853
|
+
name: "the hard shadows",
|
|
16854
|
+
needs: [distinct("primary", .1)]
|
|
16855
|
+
}
|
|
16856
|
+
},
|
|
16857
|
+
slots: [{
|
|
16858
|
+
roles: ["primary"],
|
|
16859
|
+
name: "the big block"
|
|
16860
|
+
}, {
|
|
16861
|
+
roles: ["accent"],
|
|
16862
|
+
name: "the pop buttons and stickers"
|
|
16863
|
+
}],
|
|
16864
|
+
order: [
|
|
16865
|
+
...groundOrder,
|
|
16866
|
+
"primary",
|
|
16867
|
+
"accent",
|
|
16868
|
+
"secondary"
|
|
16869
|
+
],
|
|
16870
|
+
neutrals: {
|
|
16871
|
+
dark: [.2, .04],
|
|
16872
|
+
light: [1, 0]
|
|
16873
|
+
},
|
|
16874
|
+
reading: lightReading,
|
|
16875
|
+
groundLimit: bookingLookGroundLimits["site-style:poster"].maxChroma,
|
|
16876
|
+
contrast: [{
|
|
16877
|
+
label: "the complement on the pop buttons",
|
|
16878
|
+
hue: "complement",
|
|
16879
|
+
second: "accent",
|
|
16880
|
+
lead: "primary",
|
|
16881
|
+
alsoTints: posterBand,
|
|
16882
|
+
l: .82,
|
|
16883
|
+
c: [.12, .15]
|
|
16884
|
+
}, {
|
|
16885
|
+
label: "a warm triadic partner on the pop buttons",
|
|
16886
|
+
hue: "triadic-warm",
|
|
16887
|
+
second: "accent",
|
|
16888
|
+
lead: "primary",
|
|
16889
|
+
alsoTints: posterBand,
|
|
16890
|
+
l: .82,
|
|
16891
|
+
c: [.12, .15]
|
|
16892
|
+
}]
|
|
16893
|
+
};
|
|
16894
|
+
/** Keyed by the offered looks, so a booking look without a colour spec doesn't compile. */
|
|
16895
|
+
const bookingLookColourSpecs = {
|
|
16896
|
+
"site-style:fulcrum": fulcrum,
|
|
16897
|
+
"site-style:folio": folio,
|
|
16898
|
+
"site-style:measure": measure,
|
|
16899
|
+
"site-style:grounded-practice": grounded,
|
|
16900
|
+
"site-style:lantern": lantern,
|
|
16901
|
+
"site-style:poster": poster
|
|
16902
|
+
};
|
|
16903
|
+
|
|
15806
16904
|
//#endregion
|
|
15807
16905
|
//#region ../theme-core/src/site-styles/footerLookDefaults.ts
|
|
15808
16906
|
const defaultFooterLookRecipes = {
|
|
@@ -17932,10 +19030,10 @@ function buildTextContrastShadow(input) {
|
|
|
17932
19030
|
return isLikelyLightColorToken(input.theme, input.colorToken) ? "0 1px 3px rgb(0 0 0 / 0.55), 0 0 18px rgb(0 0 0 / 0.38)" : "0 1px 3px rgb(255 255 255 / 0.72), 0 0 18px rgb(255 255 255 / 0.5)";
|
|
17933
19031
|
}
|
|
17934
19032
|
function isLikelyLightColorToken(theme, colorToken) {
|
|
17935
|
-
const hex = resolveColorTokenHex(theme, colorToken);
|
|
17936
|
-
if (hex) {
|
|
19033
|
+
const hex$1 = resolveColorTokenHex(theme, colorToken);
|
|
19034
|
+
if (hex$1) {
|
|
17937
19035
|
try {
|
|
17938
|
-
return featuresFromHex(hex).Y >= .5;
|
|
19036
|
+
return featuresFromHex(hex$1).Y >= .5;
|
|
17939
19037
|
} catch {}
|
|
17940
19038
|
}
|
|
17941
19039
|
const normalizedToken = colorToken?.toLowerCase() ?? "";
|
|
@@ -27434,6 +28532,7 @@ function resolveHeaderStyleRecipe(siteStyle, styleId) {
|
|
|
27434
28532
|
const defaultHeaderStyleRecipes = {
|
|
27435
28533
|
minimal: {
|
|
27436
28534
|
tone: "neutral",
|
|
28535
|
+
dropdown: neutralHeaderDropdown,
|
|
27437
28536
|
background: {
|
|
27438
28537
|
kind: "token",
|
|
27439
28538
|
token: "surface"
|
|
@@ -27452,6 +28551,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27452
28551
|
},
|
|
27453
28552
|
"fine-line": {
|
|
27454
28553
|
tone: "neutral",
|
|
28554
|
+
dropdown: neutralHeaderDropdown,
|
|
27455
28555
|
background: {
|
|
27456
28556
|
kind: "token",
|
|
27457
28557
|
token: "surface"
|
|
@@ -27470,6 +28570,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27470
28570
|
},
|
|
27471
28571
|
"soft-bar": {
|
|
27472
28572
|
tone: "neutral",
|
|
28573
|
+
dropdown: neutralHeaderDropdown,
|
|
27473
28574
|
background: {
|
|
27474
28575
|
kind: "token",
|
|
27475
28576
|
token: "surfaceAlt"
|
|
@@ -27490,6 +28591,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27490
28591
|
},
|
|
27491
28592
|
"pill-nav": {
|
|
27492
28593
|
tone: "neutral",
|
|
28594
|
+
dropdown: neutralHeaderDropdown,
|
|
27493
28595
|
background: {
|
|
27494
28596
|
kind: "token",
|
|
27495
28597
|
token: "surface"
|
|
@@ -27515,6 +28617,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27515
28617
|
},
|
|
27516
28618
|
"brand-bar": {
|
|
27517
28619
|
tone: "brand",
|
|
28620
|
+
dropdown: brandHeaderDropdown,
|
|
27518
28621
|
background: {
|
|
27519
28622
|
kind: "token",
|
|
27520
28623
|
token: "primary"
|
|
@@ -27533,6 +28636,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27533
28636
|
},
|
|
27534
28637
|
"brand-underline": {
|
|
27535
28638
|
tone: "brand",
|
|
28639
|
+
dropdown: brandHeaderDropdown,
|
|
27536
28640
|
background: {
|
|
27537
28641
|
kind: "token",
|
|
27538
28642
|
token: "primary"
|
|
@@ -27556,6 +28660,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27556
28660
|
},
|
|
27557
28661
|
"brand-pill": {
|
|
27558
28662
|
tone: "brand",
|
|
28663
|
+
dropdown: brandHeaderDropdown,
|
|
27559
28664
|
background: {
|
|
27560
28665
|
kind: "token",
|
|
27561
28666
|
token: "primary"
|
|
@@ -27586,6 +28691,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27586
28691
|
},
|
|
27587
28692
|
"brand-capsule": {
|
|
27588
28693
|
tone: "brand",
|
|
28694
|
+
dropdown: brandHeaderDropdown,
|
|
27589
28695
|
background: {
|
|
27590
28696
|
kind: "token",
|
|
27591
28697
|
token: "primary"
|
|
@@ -27609,6 +28715,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27609
28715
|
},
|
|
27610
28716
|
"serif-line": {
|
|
27611
28717
|
tone: "neutral",
|
|
28718
|
+
dropdown: neutralHeaderDropdown,
|
|
27612
28719
|
background: {
|
|
27613
28720
|
kind: "token",
|
|
27614
28721
|
token: "background"
|
|
@@ -27635,6 +28742,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27635
28742
|
},
|
|
27636
28743
|
"flat-tabs": {
|
|
27637
28744
|
tone: "neutral",
|
|
28745
|
+
dropdown: neutralHeaderDropdown,
|
|
27638
28746
|
background: {
|
|
27639
28747
|
kind: "token",
|
|
27640
28748
|
token: "surface"
|
|
@@ -27653,6 +28761,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27653
28761
|
},
|
|
27654
28762
|
glass: {
|
|
27655
28763
|
tone: "neutral",
|
|
28764
|
+
dropdown: neutralHeaderDropdown,
|
|
27656
28765
|
background: { kind: "transparent" },
|
|
27657
28766
|
nav: {
|
|
27658
28767
|
kind: "pill",
|
|
@@ -27676,6 +28785,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27676
28785
|
},
|
|
27677
28786
|
"minimal-surface": {
|
|
27678
28787
|
tone: "neutral",
|
|
28788
|
+
dropdown: clearSplitHeaderDropdown,
|
|
27679
28789
|
background: { kind: "transparent" },
|
|
27680
28790
|
nav: {
|
|
27681
28791
|
kind: "minimal",
|
|
@@ -27693,11 +28803,13 @@ const defaultHeaderStyleRecipes = {
|
|
|
27693
28803
|
contrastTreatment: "soft-glow"
|
|
27694
28804
|
},
|
|
27695
28805
|
openingForeground: "all",
|
|
28806
|
+
openingPlacement: "photo-behind-text",
|
|
27696
28807
|
scrollTreatment: "contrast-glass",
|
|
27697
28808
|
interactionPalette: clearSplitInteractionPalette
|
|
27698
28809
|
},
|
|
27699
28810
|
"soft-shell": {
|
|
27700
28811
|
tone: "neutral",
|
|
28812
|
+
dropdown: neutralHeaderDropdown,
|
|
27701
28813
|
background: { kind: "transparent" },
|
|
27702
28814
|
nav: {
|
|
27703
28815
|
kind: "pill",
|
|
@@ -27721,6 +28833,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27721
28833
|
},
|
|
27722
28834
|
"split-glass": {
|
|
27723
28835
|
tone: "neutral",
|
|
28836
|
+
dropdown: neutralHeaderDropdown,
|
|
27724
28837
|
background: { kind: "transparent" },
|
|
27725
28838
|
nav: {
|
|
27726
28839
|
kind: "pill",
|
|
@@ -27744,6 +28857,7 @@ const defaultHeaderStyleRecipes = {
|
|
|
27744
28857
|
},
|
|
27745
28858
|
"split-pill": {
|
|
27746
28859
|
tone: "neutral",
|
|
28860
|
+
dropdown: neutralHeaderDropdown,
|
|
27747
28861
|
background: { kind: "transparent" },
|
|
27748
28862
|
nav: {
|
|
27749
28863
|
kind: "pill",
|
|
@@ -27908,33 +29022,6 @@ function toneColorToken(tone) {
|
|
|
27908
29022
|
default: return assertNever$1(tone, "Unhandled header tone recipe");
|
|
27909
29023
|
}
|
|
27910
29024
|
}
|
|
27911
|
-
function toneDropdownStyle(tone) {
|
|
27912
|
-
switch (tone) {
|
|
27913
|
-
case "neutral": return {
|
|
27914
|
-
background: "surface",
|
|
27915
|
-
textColor: "text",
|
|
27916
|
-
borderColor: "border",
|
|
27917
|
-
shadow: "md",
|
|
27918
|
-
borderRadius: "md",
|
|
27919
|
-
textTransform: "none",
|
|
27920
|
-
letterSpacing: "normal"
|
|
27921
|
-
};
|
|
27922
|
-
case "brand": return {
|
|
27923
|
-
background: "primary-700",
|
|
27924
|
-
textColor: "primaryForeground",
|
|
27925
|
-
borderColor: null,
|
|
27926
|
-
shadow: "md",
|
|
27927
|
-
borderRadius: "md",
|
|
27928
|
-
hoverBackground: "primary-900",
|
|
27929
|
-
hoverTextColor: "secondary",
|
|
27930
|
-
textTransform: "uppercase",
|
|
27931
|
-
letterSpacing: "wider",
|
|
27932
|
-
fontWeight: "bold",
|
|
27933
|
-
textSize: "sm"
|
|
27934
|
-
};
|
|
27935
|
-
default: return assertNever$1(tone, "Unhandled header tone recipe");
|
|
27936
|
-
}
|
|
27937
|
-
}
|
|
27938
29025
|
function recipeBackground(background) {
|
|
27939
29026
|
switch (background.kind) {
|
|
27940
29027
|
case "token": return {
|
|
@@ -28138,7 +29225,7 @@ function compileHeaderRecipe(theme, layout, recipe) {
|
|
|
28138
29225
|
...headerForLayout(layout),
|
|
28139
29226
|
textColor: toneColorToken(effective.tone),
|
|
28140
29227
|
navColor: toneColorToken(effective.tone),
|
|
28141
|
-
dropdownStyle:
|
|
29228
|
+
dropdownStyle: compileHeaderDropdownRecipe(effective.dropdown),
|
|
28142
29229
|
background: recipeBackground(effective.background),
|
|
28143
29230
|
border: recipeBorder(effective.border),
|
|
28144
29231
|
navStyle: recipeNavStyle(effective.nav),
|
|
@@ -28670,7 +29757,7 @@ function resolveCornerRadiiCss$1(cornerRadii, fallback) {
|
|
|
28670
29757
|
return `${at(cornerRadii.topLeft)} ${at(cornerRadii.topRight)} ${at(cornerRadii.bottomRight)} ${at(cornerRadii.bottomLeft)}`;
|
|
28671
29758
|
}
|
|
28672
29759
|
function generateCardCss(options) {
|
|
28673
|
-
const { themeId, theme, cardSystem: providedCardSystem } = options;
|
|
29760
|
+
const { themeId, theme, cardSystem: providedCardSystem, solvedInks } = options;
|
|
28674
29761
|
const tokens = new TokenResolver(theme);
|
|
28675
29762
|
const palette = expandPalette(theme.palette);
|
|
28676
29763
|
const cardSystem = providedCardSystem ?? generateDefaultCardSystem(theme);
|
|
@@ -28683,11 +29770,11 @@ function generateCardCss(options) {
|
|
|
28683
29770
|
const groupKey = roleAliasGroupKey(source, variant.id);
|
|
28684
29771
|
emittedGroupKeys.add(groupKey);
|
|
28685
29772
|
const aliasClasses = aliasGroups.get(groupKey)?.classes ?? [];
|
|
28686
|
-
cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme, palette));
|
|
29773
|
+
cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme, palette, solvedInks));
|
|
28687
29774
|
}
|
|
28688
29775
|
for (const [groupKey, group] of aliasGroups) {
|
|
28689
29776
|
if (emittedGroupKeys.has(groupKey)) continue;
|
|
28690
|
-
cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme, palette));
|
|
29777
|
+
cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme, palette, solvedInks));
|
|
28691
29778
|
}
|
|
28692
29779
|
return cssChunks.filter(Boolean).join("\n\n");
|
|
28693
29780
|
}
|
|
@@ -28750,7 +29837,7 @@ const MEDIA_CORNER_MAP = {
|
|
|
28750
29837
|
* This CSS-first approach eliminates runtime transforms for styling,
|
|
28751
29838
|
* keeping transforms only for data normalization and dynamic layout.
|
|
28752
29839
|
*/
|
|
28753
|
-
function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palette) {
|
|
29840
|
+
function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palette, solvedInks) {
|
|
28754
29841
|
const selectorsFor = (suffix) => cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}${suffix}`).join(",\n");
|
|
28755
29842
|
const containerSelectors = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}`);
|
|
28756
29843
|
const containerSelector = containerSelectors.join(",\n");
|
|
@@ -28758,7 +29845,7 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palett
|
|
|
28758
29845
|
const contentSelector = selectorsFor(" .card-content");
|
|
28759
29846
|
const fallbackBackgroundSelector = selectorsFor(" > .rb-bg-surface");
|
|
28760
29847
|
const rules = [];
|
|
28761
|
-
rules.push(...cardSurfaceRules(variant, tokens, theme, palette));
|
|
29848
|
+
rules.push(...cardSurfaceRules(variant, tokens, theme, palette, solvedInks));
|
|
28762
29849
|
const resolveColor = (token) => tokens.getColor(token);
|
|
28763
29850
|
rules.push(...cardBorderDeclarations(variant.border ? borderDeclaration(variant.border, tokens) : null, variant.decorations, resolveColor));
|
|
28764
29851
|
const corners$1 = variant.corners ?? theme.corners;
|
|
@@ -28782,7 +29869,7 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palett
|
|
|
28782
29869
|
`${contentSelector} {\n ${contentRules.join(";\n ")};\n}`
|
|
28783
29870
|
].join("\n\n");
|
|
28784
29871
|
}
|
|
28785
|
-
function cardSurfaceRules(variant, tokens, theme, palette) {
|
|
29872
|
+
function cardSurfaceRules(variant, tokens, theme, palette, solvedInks) {
|
|
28786
29873
|
if (variant.background.type === "transparent") return ["background: transparent"];
|
|
28787
29874
|
const surface$1 = resolveSurface({
|
|
28788
29875
|
kind: "paint",
|
|
@@ -28790,7 +29877,8 @@ function cardSurfaceRules(variant, tokens, theme, palette) {
|
|
|
28790
29877
|
}, {
|
|
28791
29878
|
palette,
|
|
28792
29879
|
buttonSystem: theme.buttons,
|
|
28793
|
-
linkHoverToken: theme.typography.proseLinkStyle?.hoverColorToken
|
|
29880
|
+
linkHoverToken: theme.typography.proseLinkStyle?.hoverColorToken,
|
|
29881
|
+
solvedInks
|
|
28794
29882
|
});
|
|
28795
29883
|
return [cardBackgroundRule(variant.background, tokens), ...opaqueSurfaceDeclarations(surface$1)];
|
|
28796
29884
|
}
|
|
@@ -30215,9 +31303,9 @@ const paletteSchema = z.object({
|
|
|
30215
31303
|
return z.NEVER;
|
|
30216
31304
|
}
|
|
30217
31305
|
return {
|
|
30218
|
-
colors: paletteColorEntries(colors.colors).map(({ name, hex }) => ({
|
|
31306
|
+
colors: paletteColorEntries(colors.colors).map(({ name, hex: hex$1 }) => ({
|
|
30219
31307
|
name: String(name),
|
|
30220
|
-
hex: String(hex)
|
|
31308
|
+
hex: String(hex$1)
|
|
30221
31309
|
})),
|
|
30222
31310
|
meta: palette.meta
|
|
30223
31311
|
};
|
|
@@ -31877,38 +32965,49 @@ function renderHeaderPaintTargetRule(target, rules) {
|
|
|
31877
32965
|
"border-radius: inherit;",
|
|
31878
32966
|
"z-index: -1;"
|
|
31879
32967
|
] : [];
|
|
31880
|
-
|
|
31881
|
-
|
|
31882
|
-
|
|
31883
|
-
|
|
31884
|
-
|
|
31885
|
-
|
|
31886
|
-
|
|
31887
|
-
|
|
31888
|
-
|
|
31889
|
-
|
|
31890
|
-
|
|
31891
|
-
|
|
31892
|
-
|
|
31893
|
-
|
|
31894
|
-
|
|
31895
|
-
|
|
31896
|
-
|
|
31897
|
-
|
|
31898
|
-
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
31905
|
-
|
|
31906
|
-
|
|
31907
|
-
|
|
31908
|
-
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
|
|
32968
|
+
const ownerInks = target.pseudoElement === "inset" ? headerPaintOwnerInks(target.selector.replace(/::before$/, ""), rules) : "";
|
|
32969
|
+
return `${ownerInks}${target.selector} {\n ${[...pseudoElementSetup, ...rules].join("\n ")}\n}`;
|
|
32970
|
+
}
|
|
32971
|
+
case "viewport-band": return renderViewportBandPaint(target, rules);
|
|
32972
|
+
}
|
|
32973
|
+
}
|
|
32974
|
+
function headerInkRules(rules) {
|
|
32975
|
+
return rules.filter((rule$1) => Object.values(SURFACE_INK_CSS_VARS).some((variable) => rule$1.startsWith(`${variable}:`)));
|
|
32976
|
+
}
|
|
32977
|
+
function headerPaintOwnerInks(selector, rules) {
|
|
32978
|
+
const inks = headerInkRules(rules);
|
|
32979
|
+
return inks.length === 0 ? "" : `${selector} {\n ${inks.join("\n ")}\n}\n`;
|
|
32980
|
+
}
|
|
32981
|
+
/** Viewport paint stays on its pseudo-element; inherited ink belongs to the content owner. */
|
|
32982
|
+
function renderViewportBandPaint(target, rules) {
|
|
32983
|
+
const { top, bottom } = target.extent;
|
|
32984
|
+
const ownerRules = [
|
|
32985
|
+
`${top.customProperty}: ${top.value};`,
|
|
32986
|
+
`${bottom.customProperty}: ${bottom.value};`,
|
|
32987
|
+
`${top.ownerProperty}: var(${top.customProperty});`,
|
|
32988
|
+
...target.ownerNeedsPositioning ? ["position: relative;"] : [],
|
|
32989
|
+
"isolation: isolate;",
|
|
32990
|
+
"background: none;",
|
|
32991
|
+
"backdrop-filter: none;",
|
|
32992
|
+
"-webkit-backdrop-filter: none;",
|
|
32993
|
+
"border: 0;",
|
|
32994
|
+
"box-shadow: none;",
|
|
32995
|
+
...headerInkRules(rules)
|
|
32996
|
+
];
|
|
32997
|
+
const bandRules = [
|
|
32998
|
+
"content: \"\";",
|
|
32999
|
+
"position: absolute;",
|
|
33000
|
+
`top: calc(-1 * var(${top.customProperty}));`,
|
|
33001
|
+
`bottom: calc(-1 * var(${bottom.customProperty}));`,
|
|
33002
|
+
"left: 50%;",
|
|
33003
|
+
"width: 100vw;",
|
|
33004
|
+
"transform: translateX(-50%);",
|
|
33005
|
+
"pointer-events: none;",
|
|
33006
|
+
"border-radius: 0;",
|
|
33007
|
+
"z-index: -1;",
|
|
33008
|
+
...rules
|
|
33009
|
+
];
|
|
33010
|
+
return `${target.clipSelector} {\n overflow-x: clip;\n}\n${target.ownerSelector} {\n ${ownerRules.join("\n ")}\n}\n${target.selector} {\n ${bandRules.join("\n ")}\n}`;
|
|
31912
33011
|
}
|
|
31913
33012
|
|
|
31914
33013
|
//#endregion
|
|
@@ -31932,7 +33031,8 @@ function generateHeaderScrollTreatmentStyles(treatment, themeId, variant, surfac
|
|
|
31932
33031
|
`background-color: rgb(${backgroundChannels} / 0.60);`,
|
|
31933
33032
|
`backdrop-filter: ${CONTRAST_GLASS_BLUR};`,
|
|
31934
33033
|
`-webkit-backdrop-filter: ${CONTRAST_GLASS_BLUR};`,
|
|
31935
|
-
...surfaceRules
|
|
33034
|
+
...surfaceRules,
|
|
33035
|
+
...surfaceInkDeclarations({ kind: "reset" }).map((rule$1) => rule$1.replace(";", " !important;"))
|
|
31936
33036
|
]);
|
|
31937
33037
|
}
|
|
31938
33038
|
}
|
|
@@ -32200,6 +33300,12 @@ const dropdownStyleSchema = z.object({
|
|
|
32200
33300
|
"md",
|
|
32201
33301
|
"lg"
|
|
32202
33302
|
]).default("md"),
|
|
33303
|
+
surfaceTreatment: z.discriminatedUnion("kind", [z.object({ kind: z.literal("solid") }), z.object({
|
|
33304
|
+
kind: z.literal("frosted"),
|
|
33305
|
+
tone: z.enum(["fixed", "opening"]),
|
|
33306
|
+
opacity: z.number().min(0).max(1),
|
|
33307
|
+
blur: z.enum(["sm", "md"])
|
|
33308
|
+
})]).optional(),
|
|
32203
33309
|
hoverBackground: themeColorTokenSchema.nullable().optional(),
|
|
32204
33310
|
hoverTextColor: themeColorTokenSchema.nullable().optional(),
|
|
32205
33311
|
textTransform: z.enum([
|
|
@@ -33239,8 +34345,8 @@ function getGradientPreset(id$2) {
|
|
|
33239
34345
|
*/
|
|
33240
34346
|
function gradientToCss(config, tokens) {
|
|
33241
34347
|
const stops = config.stops.map((stop) => {
|
|
33242
|
-
const hex = tokens[stop.color] ?? "#000000";
|
|
33243
|
-
return `${hex} ${stop.position}%`;
|
|
34348
|
+
const hex$1 = tokens[stop.color] ?? "#000000";
|
|
34349
|
+
return `${hex$1} ${stop.position}%`;
|
|
33244
34350
|
}).join(", ");
|
|
33245
34351
|
const type = config.type ?? "linear";
|
|
33246
34352
|
if (type === "radial") {
|
|
@@ -33366,8 +34472,8 @@ function gradientMediaScrim(input) {
|
|
|
33366
34472
|
function scrimOpacityFor(scrimHex, foreground, palette) {
|
|
33367
34473
|
if (typeof scrimHex !== "string") return { kind: "unreadable" };
|
|
33368
34474
|
const foregroundHexes = Object.values(foreground).flatMap((token) => {
|
|
33369
|
-
const hex = typeof token === "string" ? palette[token] : undefined;
|
|
33370
|
-
return typeof hex === "string" ? [hex] : [];
|
|
34475
|
+
const hex$1 = typeof token === "string" ? palette[token] : undefined;
|
|
34476
|
+
return typeof hex$1 === "string" ? [hex$1] : [];
|
|
33371
34477
|
});
|
|
33372
34478
|
if (foregroundHexes.length === 0) return { kind: "unreadable" };
|
|
33373
34479
|
const opacity = minimumScrimOpacity({
|
|
@@ -33513,13 +34619,13 @@ function surfaceResolutionContextFromPalette(palette) {
|
|
|
33513
34619
|
buttonSystem: undefined
|
|
33514
34620
|
});
|
|
33515
34621
|
}
|
|
33516
|
-
function surfaceResolutionContextFromTheme(theme, declaredSurfaces = []) {
|
|
34622
|
+
function surfaceResolutionContextFromTheme(theme, declaredSurfaces = [], solvedInks) {
|
|
33517
34623
|
return prepareThemeSurfaceRevision({
|
|
33518
34624
|
palette: theme.palette,
|
|
33519
34625
|
buttons: theme.buttons,
|
|
33520
34626
|
skin: theme.skin,
|
|
33521
34627
|
skinMarks: theme.skinMarks
|
|
33522
|
-
}, () => declaredSurfaces).context;
|
|
34628
|
+
}, () => declaredSurfaces, solvedInks).context;
|
|
33523
34629
|
}
|
|
33524
34630
|
function defineDesignRuntimeRules(rules) {
|
|
33525
34631
|
return rules;
|
|
@@ -34194,8 +35300,11 @@ const bookingPageNoPhotoHeroLookIds = {
|
|
|
34194
35300
|
"site-style:poster": "hero-page-head"
|
|
34195
35301
|
};
|
|
34196
35302
|
function bookingPageOfferingFor(siteStyleId) {
|
|
34197
|
-
const
|
|
34198
|
-
return
|
|
35303
|
+
const lookId = bookingPageLookIdValues.find((id$2) => id$2 === siteStyleId);
|
|
35304
|
+
return lookId ? {
|
|
35305
|
+
noPhotoHeroLookId: asAppearancePresetId(bookingPageNoPhotoHeroLookIds[lookId]),
|
|
35306
|
+
colourSpec: bookingLookColourSpecs[lookId]
|
|
35307
|
+
} : undefined;
|
|
34199
35308
|
}
|
|
34200
35309
|
function clientStyleMatchesSite(siteSlugs, target) {
|
|
34201
35310
|
if (target.audience !== "site-picker" && target.audience !== "local-demo") return false;
|
|
@@ -34555,7 +35664,11 @@ const headerCurationBySiteStyle = {
|
|
|
34555
35664
|
"site-style:folio": defineHeaderCuration([
|
|
34556
35665
|
{
|
|
34557
35666
|
layoutId: "floating",
|
|
34558
|
-
styleIds: [
|
|
35667
|
+
styleIds: [
|
|
35668
|
+
"minimal-surface",
|
|
35669
|
+
"soft-shell",
|
|
35670
|
+
"glass"
|
|
35671
|
+
]
|
|
34559
35672
|
},
|
|
34560
35673
|
{
|
|
34561
35674
|
layoutId: "classic",
|
|
@@ -35239,6 +36352,11 @@ const folioLooks = {
|
|
|
35239
36352
|
treatment: "hero-short-panel",
|
|
35240
36353
|
roleMap: folioRoles
|
|
35241
36354
|
},
|
|
36355
|
+
{
|
|
36356
|
+
id: id("hero-text-over-photo"),
|
|
36357
|
+
treatment: "hero-photo-overlay",
|
|
36358
|
+
roleMap: folioRoles
|
|
36359
|
+
},
|
|
35242
36360
|
{
|
|
35243
36361
|
id: id("hero-page-head"),
|
|
35244
36362
|
treatment: "hero-ruled",
|
|
@@ -35289,10 +36407,7 @@ const folioLooks = {
|
|
|
35289
36407
|
id: id("event-calendar-ruled-rows"),
|
|
35290
36408
|
treatment: "calendar-card",
|
|
35291
36409
|
itemRoles: { container: "card" },
|
|
35292
|
-
|
|
35293
|
-
source: "text",
|
|
35294
|
-
opacity: 8
|
|
35295
|
-
} },
|
|
36410
|
+
knobValues: { actionStyle: ["button"] },
|
|
35296
36411
|
roleMap: folioRoles
|
|
35297
36412
|
},
|
|
35298
36413
|
{
|
|
@@ -35303,6 +36418,7 @@ const folioLooks = {
|
|
|
35303
36418
|
{
|
|
35304
36419
|
id: id("event-calendar-next-three"),
|
|
35305
36420
|
treatment: "calendar-compact",
|
|
36421
|
+
knobValues: { actionStyle: ["button"] },
|
|
35306
36422
|
roleMap: folioRoles
|
|
35307
36423
|
},
|
|
35308
36424
|
{
|
|
@@ -37708,12 +38824,15 @@ const curatedSiteStyles = [
|
|
|
37708
38824
|
accordions: editorialDividedAccordionSystem,
|
|
37709
38825
|
testimonialQuote: serifDisplayQuote
|
|
37710
38826
|
},
|
|
37711
|
-
headerStyleRecipes: { "
|
|
38827
|
+
headerStyleRecipes: { "soft-shell": {
|
|
37712
38828
|
tone: "neutral",
|
|
38829
|
+
dropdown: neutralHeaderDropdown,
|
|
37713
38830
|
background: { kind: "transparent" },
|
|
37714
38831
|
nav: {
|
|
37715
|
-
kind: "
|
|
37716
|
-
|
|
38832
|
+
kind: "pill",
|
|
38833
|
+
backing: "capsule",
|
|
38834
|
+
hoverColorToken: "primary",
|
|
38835
|
+
hoverAlpha: 8
|
|
37717
38836
|
},
|
|
37718
38837
|
navWeight: "medium",
|
|
37719
38838
|
navLinkWeight: "medium",
|
|
@@ -37805,6 +38924,7 @@ const curatedSiteStyles = [
|
|
|
37805
38924
|
},
|
|
37806
38925
|
headerStyleRecipes: { "serif-line": {
|
|
37807
38926
|
tone: "neutral",
|
|
38927
|
+
dropdown: neutralHeaderDropdown,
|
|
37808
38928
|
background: {
|
|
37809
38929
|
kind: "token",
|
|
37810
38930
|
token: "background"
|
|
@@ -38554,8 +39674,8 @@ function validateLookFallbacks(style, catalogue = appearancePresets.filter(isArr
|
|
|
38554
39674
|
function validateCalendarModeOffers(style, catalogue = appearancePresets.filter(isArrangementAppearancePreset)) {
|
|
38555
39675
|
const calendarLooks = catalogue.filter((look$1) => look$1.blockKinds.includes("block.event-calendar"));
|
|
38556
39676
|
arrangementModes.forEach((mode) => {
|
|
38557
|
-
const offered = calendarLooks.some((look$1) => lookModes(look$1).includes(mode) && style.offers.some((offer) => offer.id === look$1.id));
|
|
38558
|
-
if (!offered) throw new Error(`Missing automatic calendar Look for ${style.skin}: ${mode}`);
|
|
39677
|
+
const offered$1 = calendarLooks.some((look$1) => lookModes(look$1).includes(mode) && style.offers.some((offer) => offer.id === look$1.id));
|
|
39678
|
+
if (!offered$1) throw new Error(`Missing automatic calendar Look for ${style.skin}: ${mode}`);
|
|
38559
39679
|
});
|
|
38560
39680
|
}
|
|
38561
39681
|
function fallbackEndsAtOffer(entry, mode, style, catalogue, visited) {
|
|
@@ -38977,12 +40097,12 @@ function autoEditorChoice(effectiveValue) {
|
|
|
38977
40097
|
};
|
|
38978
40098
|
}
|
|
38979
40099
|
function userPinnedEditorChoice(value, input = {}) {
|
|
38980
|
-
const status = input.status ?? "supported";
|
|
40100
|
+
const status$1 = input.status ?? "supported";
|
|
38981
40101
|
return {
|
|
38982
40102
|
kind: "user-pinned",
|
|
38983
40103
|
value,
|
|
38984
|
-
status,
|
|
38985
|
-
effectiveValue: input.effectiveValue !== undefined ? input.effectiveValue : status === "supported" ? value : null
|
|
40104
|
+
status: status$1,
|
|
40105
|
+
effectiveValue: input.effectiveValue !== undefined ? input.effectiveValue : status$1 === "supported" ? value : null
|
|
38986
40106
|
};
|
|
38987
40107
|
}
|
|
38988
40108
|
const backgroundImagePromotedFieldPath = "_sectionStyles.background.image";
|
|
@@ -39450,8 +40570,8 @@ function compileFooterAgainstRow(input) {
|
|
|
39450
40570
|
}
|
|
39451
40571
|
/** Style switches retain an offered choice; otherwise use the style's first choice. */
|
|
39452
40572
|
function resolveFooterLookIdForStyle(siteStyle, current) {
|
|
39453
|
-
const offered = siteStyle?.recommendedFooterLookIds ?? [guaranteedFallbackFooterLookId];
|
|
39454
|
-
return current !== null && offered.includes(current) ? current : offered[0] ?? guaranteedFallbackFooterLookId;
|
|
40573
|
+
const offered$1 = siteStyle?.recommendedFooterLookIds ?? [guaranteedFallbackFooterLookId];
|
|
40574
|
+
return current !== null && offered$1.includes(current) ? current : offered$1[0] ?? guaranteedFallbackFooterLookId;
|
|
39455
40575
|
}
|
|
39456
40576
|
|
|
39457
40577
|
//#endregion
|
|
@@ -40775,7 +41895,35 @@ function mergeBodyTextOverride(theme, override) {
|
|
|
40775
41895
|
...intro ? { intro } : {}
|
|
40776
41896
|
});
|
|
40777
41897
|
}
|
|
40778
|
-
function
|
|
41898
|
+
function siteStyleLookMetadata(siteStyle) {
|
|
41899
|
+
const looks = siteStyle.looks;
|
|
41900
|
+
return looks.kind === "offers" ? {
|
|
41901
|
+
skin: looks.skin,
|
|
41902
|
+
skinTokens: looks.skinTokens,
|
|
41903
|
+
fontLoading: looks.fontLoading,
|
|
41904
|
+
...looks.marksFrom === undefined ? {} : { skinMarks: looks.marksFrom }
|
|
41905
|
+
} : {};
|
|
41906
|
+
}
|
|
41907
|
+
function withoutLookMetadata({ skin: _previousSkin, skinTokens: _previousTokens, fontLoading: _previousFonts, skinMarks: _previousMarks, ...theme }) {
|
|
41908
|
+
return theme;
|
|
41909
|
+
}
|
|
41910
|
+
/** Repair absent applied skin fields without refreshing an existing design from the catalogue. */
|
|
41911
|
+
function fillMissingSiteStyleLookMetadata(theme) {
|
|
41912
|
+
const siteStyle = theme.siteStyleId === undefined ? null : getCuratedSiteStyle(theme.siteStyleId);
|
|
41913
|
+
if (siteStyle === null) return theme;
|
|
41914
|
+
const looks = siteStyle.looks;
|
|
41915
|
+
if (looks.kind !== "offers") return withoutLookMetadata(theme);
|
|
41916
|
+
return {
|
|
41917
|
+
...theme,
|
|
41918
|
+
skin: theme.skin ?? looks.skin,
|
|
41919
|
+
skinTokens: theme.skinTokens ?? looks.skinTokens,
|
|
41920
|
+
fontLoading: theme.fontLoading ?? looks.fontLoading,
|
|
41921
|
+
...theme.skinMarks === undefined && looks.marksFrom !== undefined ? { skinMarks: looks.marksFrom } : {}
|
|
41922
|
+
};
|
|
41923
|
+
}
|
|
41924
|
+
/** Full style Apply deliberately replaces applied skin output with the current catalogue. */
|
|
41925
|
+
function withSiteStyleMetadata(previous, siteStyle) {
|
|
41926
|
+
const theme = withoutLookMetadata(previous);
|
|
40779
41927
|
const surfaceContexts = siteStyle.looks.kind === "offers" ? Object.fromEntries(Object.values(siteStyle.looks.surfaces).flatMap((spec) => spec.kind === "token" && spec.buttonContext ? [[spec.token, spec.buttonContext]] : [])) : {};
|
|
40780
41928
|
return {
|
|
40781
41929
|
...theme,
|
|
@@ -40787,12 +41935,7 @@ function withSiteStyleMetadata({ skin: _previousSkin, skinTokens: _previousToken
|
|
|
40787
41935
|
description: siteStyle.description,
|
|
40788
41936
|
rationale: `Compiled from curated Theme V2 site style ${siteStyle.id}.`,
|
|
40789
41937
|
siteStyleId: siteStyle.id,
|
|
40790
|
-
...siteStyle
|
|
40791
|
-
skin: siteStyle.looks.skin,
|
|
40792
|
-
skinTokens: siteStyle.looks.skinTokens,
|
|
40793
|
-
fontLoading: siteStyle.looks.fontLoading,
|
|
40794
|
-
...siteStyle.looks.marksFrom === undefined ? {} : { skinMarks: siteStyle.looks.marksFrom }
|
|
40795
|
-
} : {},
|
|
41938
|
+
...siteStyleLookMetadata(siteStyle),
|
|
40796
41939
|
headingDecoration: siteStyle.headingDecoration
|
|
40797
41940
|
};
|
|
40798
41941
|
}
|
|
@@ -42304,8 +43447,8 @@ function headerStyleAvailabilityReasonLabel(reason) {
|
|
|
42304
43447
|
}
|
|
42305
43448
|
function applyOpeningForeground(theme, header, recipe, context) {
|
|
42306
43449
|
if (!recipe.openingForeground) return header;
|
|
42307
|
-
const foreground = context?.openingForeground ?? { kind: "
|
|
42308
|
-
if (foreground.kind
|
|
43450
|
+
const foreground = context?.openingForeground ?? { kind: "unmeasured-paint" };
|
|
43451
|
+
if (foreground.kind !== "known") return withOpeningForegroundProjection(applyUnknownOpeningForegroundFallback(theme, header), foreground);
|
|
42309
43452
|
return projectKnownOpeningForeground(theme, header, recipe, foreground);
|
|
42310
43453
|
}
|
|
42311
43454
|
function projectKnownOpeningForeground(theme, header, recipe, foreground) {
|
|
@@ -43292,23 +44435,6 @@ const decodedPageDesignIntentSchema = z.union([z.nullish(z.never()).transform(()
|
|
|
43292
44435
|
return typeof value === "object" && value !== null && "schemaVersion" in value && value.schemaVersion !== 2 ? "Unsupported page design decisions schemaVersion." : "Invalid page design decisions shape.";
|
|
43293
44436
|
} });
|
|
43294
44437
|
|
|
43295
|
-
//#endregion
|
|
43296
|
-
//#region ../theme-core/src/site-styles/bookingPageLookIds.ts
|
|
43297
|
-
/** Deterministic fallback order; Fulcrum is the default. */
|
|
43298
|
-
const bookingPageLookIdValues = [
|
|
43299
|
-
"site-style:fulcrum",
|
|
43300
|
-
"site-style:folio",
|
|
43301
|
-
"site-style:measure",
|
|
43302
|
-
"site-style:grounded-practice",
|
|
43303
|
-
"site-style:lantern",
|
|
43304
|
-
"site-style:poster"
|
|
43305
|
-
];
|
|
43306
|
-
/** Boundary admission for draft and suggestion inputs. */
|
|
43307
|
-
function parseBookingPageLookId(value) {
|
|
43308
|
-
const id$2 = bookingPageLookIdValues.find((candidate$1) => candidate$1 === value);
|
|
43309
|
-
return id$2 ? asSiteStyleId(id$2) : null;
|
|
43310
|
-
}
|
|
43311
|
-
|
|
43312
44438
|
//#endregion
|
|
43313
44439
|
//#region ../theme-core/src/site-styles/appearance.ts
|
|
43314
44440
|
const bookingPageLooks = bookingPageLookIdValues.map((value) => {
|
|
@@ -43433,6 +44559,907 @@ function describeAppearance(value) {
|
|
|
43433
44559
|
};
|
|
43434
44560
|
}
|
|
43435
44561
|
|
|
44562
|
+
//#endregion
|
|
44563
|
+
//#region ../theme-core/src/site-styles/ownColours/types.ts
|
|
44564
|
+
const groundRoles = [
|
|
44565
|
+
"background",
|
|
44566
|
+
"surface",
|
|
44567
|
+
"surfaceAlt",
|
|
44568
|
+
"muted",
|
|
44569
|
+
"border"
|
|
44570
|
+
];
|
|
44571
|
+
const paintedRoles = [
|
|
44572
|
+
...groundRoles,
|
|
44573
|
+
"primary",
|
|
44574
|
+
"secondary",
|
|
44575
|
+
"accent"
|
|
44576
|
+
];
|
|
44577
|
+
|
|
44578
|
+
//#endregion
|
|
44579
|
+
//#region ../theme-core/src/site-styles/ownColours/colourSpace.ts
|
|
44580
|
+
/** Below this OKLCH chroma a colour has no hue worth carrying (greys, black, white). */
|
|
44581
|
+
const ACHROMATIC_CHROMA = .02;
|
|
44582
|
+
const wrapHue = (degrees) => (degrees % 360 + 360) % 360;
|
|
44583
|
+
/** The angle between two hues, 0–180. */
|
|
44584
|
+
const hueGap = (a, b) => Math.abs((a - b + 540) % 360 - 180);
|
|
44585
|
+
function lchOf(hex$1) {
|
|
44586
|
+
const { okL, okC, okH } = featuresFromHex(hex$1);
|
|
44587
|
+
return {
|
|
44588
|
+
l: okL,
|
|
44589
|
+
c: okC,
|
|
44590
|
+
h: okH
|
|
44591
|
+
};
|
|
44592
|
+
}
|
|
44593
|
+
/** The in-gamut sRGB colour nearest this OKLCH, holding lightness and hue and reducing chroma. */
|
|
44594
|
+
function fromLch(l, c, h) {
|
|
44595
|
+
const lightness = Math.min(1, Math.max(0, l));
|
|
44596
|
+
const chroma = gamutChroma(lightness, Math.max(0, c), wrapHue(h));
|
|
44597
|
+
const { r, g, b } = oklchToLinearSrgb(lightness, chroma, wrapHue(h));
|
|
44598
|
+
return hexColorSchema.parse(rgbToHex(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b)));
|
|
44599
|
+
}
|
|
44600
|
+
/**
|
|
44601
|
+
* A shade of a hue at another lightness. Yellows and olives turn toward
|
|
44602
|
+
* orange as they darken, as a painter would shade them, so a deep mustard
|
|
44603
|
+
* reads as ochre rather than khaki.
|
|
44604
|
+
*/
|
|
44605
|
+
function shadeAt(l, c, h, fromL) {
|
|
44606
|
+
const turn = h >= 55 && h <= 115 && l < fromL ? Math.min(25, (fromL - l) * 50) : 0;
|
|
44607
|
+
return fromLch(l, c, h - turn);
|
|
44608
|
+
}
|
|
44609
|
+
function oklabOf(hex$1) {
|
|
44610
|
+
const { r, g, b } = hexToRgb(hex$1);
|
|
44611
|
+
return srgbToOklab(r, g, b);
|
|
44612
|
+
}
|
|
44613
|
+
/** OKLab ΔE between two colours. */
|
|
44614
|
+
function colourDistance(a, b) {
|
|
44615
|
+
return oklabDistance(oklabOf(a), oklabOf(b));
|
|
44616
|
+
}
|
|
44617
|
+
|
|
44618
|
+
//#endregion
|
|
44619
|
+
//#region ../theme-core/src/site-styles/ownColours/engine.ts
|
|
44620
|
+
const hex = (value) => hexColorSchema.parse(value.toLowerCase());
|
|
44621
|
+
const clamp = (value, [lo, hi]) => Math.min(hi, Math.max(lo, value));
|
|
44622
|
+
const isGround = (role) => groundRoles.includes(role);
|
|
44623
|
+
const STEP = .005;
|
|
44624
|
+
function lightnessSteps([lo, hi]) {
|
|
44625
|
+
const count = Math.floor((hi - lo + 1e-9) / STEP) + 1;
|
|
44626
|
+
return Array.from({ length: count }, (_, i) => Math.round((lo + i * STEP) * 1e3) / 1e3);
|
|
44627
|
+
}
|
|
44628
|
+
/** The first of the cheapest, so ties keep the search's own order. */
|
|
44629
|
+
function cheapest(options) {
|
|
44630
|
+
return options.reduce((best, next) => !best || next.cost < best.cost ? next : best, undefined);
|
|
44631
|
+
}
|
|
44632
|
+
const labelReads = (fill) => maxContrastBlackOrWhite(solidPaint(fill)).ratio >= 4.5;
|
|
44633
|
+
function meets(candidate$1, needs, placed) {
|
|
44634
|
+
return needs.every((need) => {
|
|
44635
|
+
switch (need.kind) {
|
|
44636
|
+
case "label": return labelReads(candidate$1);
|
|
44637
|
+
case "ladder": {
|
|
44638
|
+
const ground = placed[need.against];
|
|
44639
|
+
const step = generateShades(candidate$1, "x").find((entry) => entry.name === `x-${need.step}`);
|
|
44640
|
+
return !ground || !step || contrastRatio(step.hex, ground) >= need.ratio;
|
|
44641
|
+
}
|
|
44642
|
+
case "contrast": {
|
|
44643
|
+
const other = placed[need.against];
|
|
44644
|
+
return !other || contrastRatio(candidate$1, other) >= need.ratio;
|
|
44645
|
+
}
|
|
44646
|
+
case "distinct": {
|
|
44647
|
+
const other = placed[need.from];
|
|
44648
|
+
return !other || colourDistance(candidate$1, other) >= need.deltaE;
|
|
44649
|
+
}
|
|
44650
|
+
}
|
|
44651
|
+
});
|
|
44652
|
+
}
|
|
44653
|
+
/** A role's own needs, plus every painted role's needs that point back at it, turned round. */
|
|
44654
|
+
function needsFor(spec, role, placed) {
|
|
44655
|
+
const reverse = paintedRoles.flatMap((other) => {
|
|
44656
|
+
if (!placed[other]) return [];
|
|
44657
|
+
return (spec.roles[other].needs ?? []).flatMap((need) => {
|
|
44658
|
+
if (need.kind === "contrast" && need.against === role) return [{
|
|
44659
|
+
kind: "contrast",
|
|
44660
|
+
against: other,
|
|
44661
|
+
ratio: need.ratio
|
|
44662
|
+
}];
|
|
44663
|
+
if (need.kind === "distinct" && need.from === role) return [{
|
|
44664
|
+
kind: "distinct",
|
|
44665
|
+
from: other,
|
|
44666
|
+
deltaE: need.deltaE
|
|
44667
|
+
}];
|
|
44668
|
+
return [];
|
|
44669
|
+
});
|
|
44670
|
+
});
|
|
44671
|
+
return [...spec.roles[role].needs ?? [], ...reverse];
|
|
44672
|
+
}
|
|
44673
|
+
const linkReads = (candidate$1, placed) => ["background", "surface"].every((ground) => {
|
|
44674
|
+
const paint = placed[ground];
|
|
44675
|
+
return !paint || contrastRatio(candidate$1, paint) >= 4.5;
|
|
44676
|
+
});
|
|
44677
|
+
/** The solver's family ink for a link role: the owner's colour moved at most ΔE 0.08 to read on the page and cards. */
|
|
44678
|
+
function linkInk(colour, placed) {
|
|
44679
|
+
const { background, surface: surface$1 } = placed;
|
|
44680
|
+
if (!background || !surface$1) return [];
|
|
44681
|
+
const lo = Math.min(hexLuminance(background), hexLuminance(surface$1));
|
|
44682
|
+
const hi = Math.max(hexLuminance(background), hexLuminance(surface$1));
|
|
44683
|
+
const ink = inkFrom(colourFamily(colour), luminanceInterval(lo, hi), 4.5);
|
|
44684
|
+
return ink.kind === "ink" ? [hex(ink.hex)] : [];
|
|
44685
|
+
}
|
|
44686
|
+
function inRange(roleSpec, candidate$1) {
|
|
44687
|
+
const { l, c } = lchOf(candidate$1);
|
|
44688
|
+
return l >= roleSpec.l[0] - .001 && l <= roleSpec.l[1] + .001 && c <= roleSpec.c[1] + .004;
|
|
44689
|
+
}
|
|
44690
|
+
/**
|
|
44691
|
+
* A held colour in a role: exactly when it fits, otherwise its nearest shade
|
|
44692
|
+
* inside the role's range that meets its needs. A link role first offers the
|
|
44693
|
+
* solver's family ink within ΔE 0.08; a link that still misses 4.5:1 ranks as
|
|
44694
|
+
* if it had moved that far, so a close shade that reads wins.
|
|
44695
|
+
*/
|
|
44696
|
+
function hold(spec, role, colour, placed) {
|
|
44697
|
+
const roleSpec = spec.roles[role];
|
|
44698
|
+
const needs = needsFor(spec, role, placed);
|
|
44699
|
+
const s = lchOf(colour);
|
|
44700
|
+
const linkPenalty = (candidate$1) => roleSpec.link && !linkReads(candidate$1, placed) ? BRAND_INK_MAX_DELTA_E : 0;
|
|
44701
|
+
const chroma = clamp(s.c, [Math.min(roleSpec.c[0], s.c), roleSpec.c[1]]);
|
|
44702
|
+
const candidates = [
|
|
44703
|
+
colour,
|
|
44704
|
+
...roleSpec.link ? linkInk(colour, placed) : [],
|
|
44705
|
+
...lightnessSteps(roleSpec.l).map((l) => shadeAt(l, chroma, s.h, s.l))
|
|
44706
|
+
];
|
|
44707
|
+
const best = cheapest(candidates.filter((candidate$1) => inRange(roleSpec, candidate$1) && meets(candidate$1, needs, placed)).map((candidate$1) => ({
|
|
44708
|
+
hex: candidate$1,
|
|
44709
|
+
cost: colourDistance(candidate$1, colour) + linkPenalty(candidate$1)
|
|
44710
|
+
})));
|
|
44711
|
+
return best ? {
|
|
44712
|
+
kind: "fitted",
|
|
44713
|
+
hex: best.hex
|
|
44714
|
+
} : {
|
|
44715
|
+
kind: "unfit",
|
|
44716
|
+
reason: {
|
|
44717
|
+
kind: "role-unmet",
|
|
44718
|
+
role
|
|
44719
|
+
}
|
|
44720
|
+
};
|
|
44721
|
+
}
|
|
44722
|
+
/** The chroma a derived role takes from its source: a share of it, inside the role's range; none from a grey. */
|
|
44723
|
+
function derivedChroma(roleSpec, sourceChroma) {
|
|
44724
|
+
if (sourceChroma < ACHROMATIC_CHROMA) return 0;
|
|
44725
|
+
const wanted = Math.min(sourceChroma * (roleSpec.chromaShare ?? 1), roleSpec.derivedChroma ?? Infinity);
|
|
44726
|
+
return clamp(wanted, [Math.min(roleSpec.c[0], sourceChroma), roleSpec.c[1]]);
|
|
44727
|
+
}
|
|
44728
|
+
function targetLightness(roleSpec, source, placed) {
|
|
44729
|
+
if (typeof roleSpec.target === "number") return roleSpec.target;
|
|
44730
|
+
return clamp(lchOf(placed[roleSpec.target.from] ?? source).l + roleSpec.target.dl, roleSpec.l);
|
|
44731
|
+
}
|
|
44732
|
+
/** An unheld role, as a shade or tint of its source's hue inside the role's range, as near its target lightness as its needs allow. */
|
|
44733
|
+
function derive(spec, role, source, placed) {
|
|
44734
|
+
const roleSpec = spec.roles[role];
|
|
44735
|
+
const needs = needsFor(spec, role, placed);
|
|
44736
|
+
const s = lchOf(source);
|
|
44737
|
+
const target = targetLightness(roleSpec, source, placed);
|
|
44738
|
+
const chroma = derivedChroma(roleSpec, s.c);
|
|
44739
|
+
const turns = roleSpec.analogous ? [
|
|
44740
|
+
0,
|
|
44741
|
+
20,
|
|
44742
|
+
-20,
|
|
44743
|
+
40,
|
|
44744
|
+
-40
|
|
44745
|
+
] : [0];
|
|
44746
|
+
const best = cheapest(turns.flatMap((turn) => lightnessSteps(roleSpec.l).flatMap((l) => {
|
|
44747
|
+
const candidate$1 = shadeAt(l, chroma, s.h + turn, s.l);
|
|
44748
|
+
return inRange(roleSpec, candidate$1) && meets(candidate$1, needs, placed) ? [{
|
|
44749
|
+
hex: candidate$1,
|
|
44750
|
+
cost: Math.abs(l - target) + .004 * Math.abs(turn)
|
|
44751
|
+
}] : [];
|
|
44752
|
+
})));
|
|
44753
|
+
return best ? {
|
|
44754
|
+
kind: "fitted",
|
|
44755
|
+
hex: best.hex
|
|
44756
|
+
} : {
|
|
44757
|
+
kind: "unfit",
|
|
44758
|
+
reason: {
|
|
44759
|
+
kind: "role-unmet",
|
|
44760
|
+
role
|
|
44761
|
+
}
|
|
44762
|
+
};
|
|
44763
|
+
}
|
|
44764
|
+
const holdsGround = (entry) => entry.slot.roles.some(isGround);
|
|
44765
|
+
/** Each held colour, in the slot role it fits with the smallest move. */
|
|
44766
|
+
function holdPhase(spec, holds, start) {
|
|
44767
|
+
return holds.reduce((result, entry) => {
|
|
44768
|
+
if (result.kind === "unfit") return result;
|
|
44769
|
+
const { placed, held } = result.painting;
|
|
44770
|
+
const fits = entry.slot.roles.flatMap((role) => {
|
|
44771
|
+
const fitted = hold(spec, role, entry.hex, placed);
|
|
44772
|
+
return fitted.kind === "fitted" ? [{
|
|
44773
|
+
role,
|
|
44774
|
+
hex: fitted.hex,
|
|
44775
|
+
cost: colourDistance(fitted.hex, entry.hex)
|
|
44776
|
+
}] : [];
|
|
44777
|
+
});
|
|
44778
|
+
const best = cheapest(fits);
|
|
44779
|
+
if (!best) return {
|
|
44780
|
+
kind: "unfit",
|
|
44781
|
+
reason: {
|
|
44782
|
+
kind: "role-unmet",
|
|
44783
|
+
role: entry.slot.roles[0]
|
|
44784
|
+
}
|
|
44785
|
+
};
|
|
44786
|
+
const landed = {
|
|
44787
|
+
source: entry.source,
|
|
44788
|
+
slot: entry.slot,
|
|
44789
|
+
role: best.role,
|
|
44790
|
+
hex: best.hex,
|
|
44791
|
+
asked: entry.hex,
|
|
44792
|
+
deltaE: best.cost
|
|
44793
|
+
};
|
|
44794
|
+
return {
|
|
44795
|
+
kind: "painting",
|
|
44796
|
+
painting: {
|
|
44797
|
+
placed: {
|
|
44798
|
+
...placed,
|
|
44799
|
+
[best.role]: best.hex
|
|
44800
|
+
},
|
|
44801
|
+
held: [...held, landed]
|
|
44802
|
+
}
|
|
44803
|
+
};
|
|
44804
|
+
}, {
|
|
44805
|
+
kind: "painting",
|
|
44806
|
+
painting: start
|
|
44807
|
+
});
|
|
44808
|
+
}
|
|
44809
|
+
/** The colour an unheld role takes its hue from. Grounds follow a colour already held on a ground, so the page and a held band agree. */
|
|
44810
|
+
function sourceFor(spec, role, request, painting) {
|
|
44811
|
+
const given = request.hueSources?.[role];
|
|
44812
|
+
if (given) return given;
|
|
44813
|
+
const linked = spec.roles[role].hueFrom;
|
|
44814
|
+
const fromRole = linked ? painting.placed[linked] : undefined;
|
|
44815
|
+
if (fromRole) return fromRole;
|
|
44816
|
+
return isGround(role) ? groundTint(painting, request.lead) : request.lead;
|
|
44817
|
+
}
|
|
44818
|
+
function groundTint(painting, lead) {
|
|
44819
|
+
return painting.held.find((entry) => isGround(entry.role))?.asked ?? lead;
|
|
44820
|
+
}
|
|
44821
|
+
function derivePhase(spec, roles, request, start) {
|
|
44822
|
+
return roles.reduce((result, role) => {
|
|
44823
|
+
if (result.kind === "unfit" || result.painting.placed[role]) return result;
|
|
44824
|
+
const { painting } = result;
|
|
44825
|
+
const fitted = derive(spec, role, sourceFor(spec, role, request, painting), painting.placed);
|
|
44826
|
+
if (fitted.kind === "unfit") return fitted;
|
|
44827
|
+
return {
|
|
44828
|
+
kind: "painting",
|
|
44829
|
+
painting: {
|
|
44830
|
+
...painting,
|
|
44831
|
+
placed: {
|
|
44832
|
+
...painting.placed,
|
|
44833
|
+
[role]: fitted.hex
|
|
44834
|
+
}
|
|
44835
|
+
}
|
|
44836
|
+
};
|
|
44837
|
+
}, start);
|
|
44838
|
+
}
|
|
44839
|
+
/** Every painted role, or the first one missing. */
|
|
44840
|
+
function complete(placed) {
|
|
44841
|
+
const { background, surface: surface$1, surfaceAlt, muted, border: border$1, primary, secondary, accent } = placed;
|
|
44842
|
+
if (background && surface$1 && surfaceAlt && muted && border$1 && primary && secondary && accent) {
|
|
44843
|
+
return {
|
|
44844
|
+
kind: "complete",
|
|
44845
|
+
roles: {
|
|
44846
|
+
background,
|
|
44847
|
+
surface: surface$1,
|
|
44848
|
+
surfaceAlt,
|
|
44849
|
+
muted,
|
|
44850
|
+
border: border$1,
|
|
44851
|
+
primary,
|
|
44852
|
+
secondary,
|
|
44853
|
+
accent
|
|
44854
|
+
}
|
|
44855
|
+
};
|
|
44856
|
+
}
|
|
44857
|
+
const missing = paintedRoles.find((role) => !placed[role]) ?? "primary";
|
|
44858
|
+
return {
|
|
44859
|
+
kind: "unfit",
|
|
44860
|
+
reason: {
|
|
44861
|
+
kind: "role-unmet",
|
|
44862
|
+
role: missing
|
|
44863
|
+
}
|
|
44864
|
+
};
|
|
44865
|
+
}
|
|
44866
|
+
/** Status colours keep their meaning (green, amber, red); they are semantic, not part of the look. */
|
|
44867
|
+
const statusHues = {
|
|
44868
|
+
success: 150,
|
|
44869
|
+
warning: 70,
|
|
44870
|
+
destructive: 27
|
|
44871
|
+
};
|
|
44872
|
+
function status(hue, background, dark) {
|
|
44873
|
+
const fits = (l$1) => contrastRatio(fromLch(l$1, .13, hue), background) >= 3;
|
|
44874
|
+
const l = (dark ? lightnessSteps([.6, .95]) : lightnessSteps([.2, .6]).reverse()).find(fits) ?? (dark ? .95 : .2);
|
|
44875
|
+
return fromLch(l, .13, hue);
|
|
44876
|
+
}
|
|
44877
|
+
/** The inks and status colours, solved for the painted roles. */
|
|
44878
|
+
function finish(spec, roles, tintToward) {
|
|
44879
|
+
const { c, h } = lchOf(tintToward);
|
|
44880
|
+
const tint$1 = Math.min(c, .03);
|
|
44881
|
+
const preferred = {
|
|
44882
|
+
dark: fromLch(spec.neutrals.dark[0], Math.min(tint$1, spec.neutrals.dark[1]), h),
|
|
44883
|
+
light: fromLch(spec.neutrals.light[0], Math.min(tint$1, spec.neutrals.light[1]), h)
|
|
44884
|
+
};
|
|
44885
|
+
const ys = spec.reading.map((role) => hexLuminance(roles[role]));
|
|
44886
|
+
const text = neutralInk(luminanceInterval(Math.min(...ys), Math.max(...ys)), 4.5, preferred);
|
|
44887
|
+
const onPrimary = neutralInk(solidPaint(roles.primary), 4.5, preferred);
|
|
44888
|
+
if (text.kind !== "ink" || onPrimary.kind !== "ink") return {
|
|
44889
|
+
kind: "unfit",
|
|
44890
|
+
reason: { kind: "no-readable-text" }
|
|
44891
|
+
};
|
|
44892
|
+
const dark = maxContrastBlackOrWhite(solidPaint(roles.background)).ink === "white";
|
|
44893
|
+
return {
|
|
44894
|
+
kind: "finished",
|
|
44895
|
+
colors: {
|
|
44896
|
+
...roles,
|
|
44897
|
+
success: status(statusHues.success, roles.background, dark),
|
|
44898
|
+
warning: status(statusHues.warning, roles.background, dark),
|
|
44899
|
+
destructive: status(statusHues.destructive, roles.background, dark),
|
|
44900
|
+
text: hex(text.hex),
|
|
44901
|
+
primaryForeground: hex(onPrimary.hex)
|
|
44902
|
+
},
|
|
44903
|
+
mode: dark ? "dark" : "light"
|
|
44904
|
+
};
|
|
44905
|
+
}
|
|
44906
|
+
/**
|
|
44907
|
+
* A band or panel is always a pale or deep tint of a colour, so moving a
|
|
44908
|
+
* colour there counts for less than shading a button colour.
|
|
44909
|
+
*/
|
|
44910
|
+
const GROUND_MOVE_WEIGHT = .3;
|
|
44911
|
+
/** One complete palette for the look: held grounds, derived grounds, held fills, derived fills, then inks. */
|
|
44912
|
+
function paintPalette(spec, request) {
|
|
44913
|
+
const start = {
|
|
44914
|
+
placed: {},
|
|
44915
|
+
held: []
|
|
44916
|
+
};
|
|
44917
|
+
const grounds = derivePhase(spec, spec.order.filter(isGround), request, holdPhase(spec, request.holds.filter(holdsGround), start));
|
|
44918
|
+
if (grounds.kind === "unfit") return grounds;
|
|
44919
|
+
const fills = derivePhase(spec, spec.order.filter((role) => !isGround(role)), request, holdPhase(spec, request.holds.filter((entry) => !holdsGround(entry)), grounds.painting));
|
|
44920
|
+
if (fills.kind === "unfit") return fills;
|
|
44921
|
+
const roles = complete(fills.painting.placed);
|
|
44922
|
+
if (roles.kind === "unfit") return roles;
|
|
44923
|
+
const finished = finish(spec, roles.roles, groundTint(fills.painting, request.lead));
|
|
44924
|
+
if (finished.kind === "unfit") return finished;
|
|
44925
|
+
const { held } = fills.painting;
|
|
44926
|
+
return {
|
|
44927
|
+
kind: "painted",
|
|
44928
|
+
colors: finished.colors,
|
|
44929
|
+
mode: finished.mode,
|
|
44930
|
+
held,
|
|
44931
|
+
cost: held.reduce((sum, entry) => sum + entry.deltaE * (isGround(entry.role) ? GROUND_MOVE_WEIGHT : 1), 0)
|
|
44932
|
+
};
|
|
44933
|
+
}
|
|
44934
|
+
|
|
44935
|
+
//#endregion
|
|
44936
|
+
//#region ../theme-core/src/site-styles/ownColours/ownColourPalettes.ts
|
|
44937
|
+
/** Amber in OKLCH; warmth is closeness to it. */
|
|
44938
|
+
const WARM_CENTRE = 60;
|
|
44939
|
+
const warmth = (h) => hueGap(h, WARM_CENTRE);
|
|
44940
|
+
/** Rose round to gold (OKLCH 340° to 110°). */
|
|
44941
|
+
const isWarm = (h) => h >= 340 || h <= 110;
|
|
44942
|
+
/**
|
|
44943
|
+
* Hues that look muddy or sweet as a contrast (olive and forest greens,
|
|
44944
|
+
* orchids and magentas) move to their warmer neighbour: plum's complement
|
|
44945
|
+
* becomes gold, sage's becomes rose.
|
|
44946
|
+
*/
|
|
44947
|
+
function outOfMud(h) {
|
|
44948
|
+
if (h > 110 && h < 165) return 105;
|
|
44949
|
+
if (h > 295 && h < 350) return 355;
|
|
44950
|
+
return h;
|
|
44951
|
+
}
|
|
44952
|
+
/** Steps round the wheel, 15° apart, from straight across inwards. */
|
|
44953
|
+
const steps = [180, ...[
|
|
44954
|
+
165,
|
|
44955
|
+
150,
|
|
44956
|
+
135,
|
|
44957
|
+
120,
|
|
44958
|
+
105,
|
|
44959
|
+
90,
|
|
44960
|
+
75,
|
|
44961
|
+
60,
|
|
44962
|
+
45,
|
|
44963
|
+
30
|
|
44964
|
+
].flatMap((o) => [o, -o])];
|
|
44965
|
+
const warmChoices = [
|
|
44966
|
+
"warm-complement",
|
|
44967
|
+
"split-warm",
|
|
44968
|
+
"triadic-warm",
|
|
44969
|
+
"warm-other"
|
|
44970
|
+
];
|
|
44971
|
+
function preferredHues(choice, lead) {
|
|
44972
|
+
const byWarmth = (offsets) => offsets.map((o) => wrapHue(lead + o)).sort((a, b) => warmth(a) - warmth(b));
|
|
44973
|
+
const warmest = () => byWarmth(steps).filter(isWarm);
|
|
44974
|
+
switch (choice) {
|
|
44975
|
+
case "complement": return byWarmth([
|
|
44976
|
+
165,
|
|
44977
|
+
180,
|
|
44978
|
+
195
|
|
44979
|
+
]);
|
|
44980
|
+
case "warm-complement": {
|
|
44981
|
+
const across = byWarmth([
|
|
44982
|
+
165,
|
|
44983
|
+
180,
|
|
44984
|
+
195
|
|
44985
|
+
])[0] ?? wrapHue(lead + 180);
|
|
44986
|
+
return isWarm(across) ? [across, ...warmest()] : warmest();
|
|
44987
|
+
}
|
|
44988
|
+
case "split-warm": return [...byWarmth([150, -150]).filter(isWarm), ...warmest()];
|
|
44989
|
+
case "triadic-warm": return [...byWarmth([120, -120]).filter(isWarm), ...warmest()];
|
|
44990
|
+
case "warm-other": return warmest();
|
|
44991
|
+
}
|
|
44992
|
+
}
|
|
44993
|
+
/**
|
|
44994
|
+
* The hues a rule tries, in tiers, best first. Warm rules keep 45° from a
|
|
44995
|
+
* warm brand's own hue, and only when nothing warm is left (terracotta,
|
|
44996
|
+
* mustard) turn to the cool complement (deep teal, ink blue). Every hue is
|
|
44997
|
+
* kept out of the muddy zones and 40° clear of the other rule's colour. The
|
|
44998
|
+
* last tier is any step round the wheel, for when nothing earlier looks
|
|
44999
|
+
* different enough from the palettes before it.
|
|
45000
|
+
*/
|
|
45001
|
+
function candidateHues(rule$1, lead, avoid) {
|
|
45002
|
+
const warmRule = warmChoices.includes(rule$1.hue);
|
|
45003
|
+
const leadGap = warmRule ? 45 : 30;
|
|
45004
|
+
const clear = (h) => hueGap(h, lead) >= leadGap && (avoid === null || hueGap(h, avoid) >= 40);
|
|
45005
|
+
const unique = (hues) => hues.filter((h, i) => hues.findIndex((other) => Math.abs(other - h) < 1) === i);
|
|
45006
|
+
const own = unique(preferredHues(rule$1.hue, lead).map(outOfMud).filter(clear));
|
|
45007
|
+
const cool = own.length > 0 ? [] : unique(preferredHues("complement", lead).map(outOfMud).filter(clear));
|
|
45008
|
+
const anyStep = unique(steps.map((o) => outOfMud(wrapHue(lead + o))).filter((h) => hueGap(h, lead) >= 30));
|
|
45009
|
+
return [
|
|
45010
|
+
own,
|
|
45011
|
+
cool,
|
|
45012
|
+
anyStep
|
|
45013
|
+
];
|
|
45014
|
+
}
|
|
45015
|
+
/**
|
|
45016
|
+
* The rule's second colour at a hue, before it is fitted to its role. Deep
|
|
45017
|
+
* yellows would read as olive and brown, so a deep rule paints amber and
|
|
45018
|
+
* ochre a little lighter, turned toward orange.
|
|
45019
|
+
*/
|
|
45020
|
+
function madeColour(rule$1, lead, hue) {
|
|
45021
|
+
const s = lchOf(lead);
|
|
45022
|
+
const yellow = hue >= 55 && hue <= 105;
|
|
45023
|
+
const l = rule$1.l < .5 && yellow ? rule$1.l + .12 : rule$1.l;
|
|
45024
|
+
const turn = hue >= 55 && hue <= 115 && l < .85 ? Math.min(35, (.85 - l) * 70) : 0;
|
|
45025
|
+
return fromLch(l, Math.min(rule$1.c[1], Math.max(rule$1.c[0], s.c)), hue - turn);
|
|
45026
|
+
}
|
|
45027
|
+
/** [hue upper bound, dark, mid, light] in OKLCH hue. */
|
|
45028
|
+
const names = [
|
|
45029
|
+
[
|
|
45030
|
+
20,
|
|
45031
|
+
"burgundy",
|
|
45032
|
+
"rose",
|
|
45033
|
+
"pink"
|
|
45034
|
+
],
|
|
45035
|
+
[
|
|
45036
|
+
38,
|
|
45037
|
+
"oxblood",
|
|
45038
|
+
"coral",
|
|
45039
|
+
"pale coral"
|
|
45040
|
+
],
|
|
45041
|
+
[
|
|
45042
|
+
60,
|
|
45043
|
+
"rust",
|
|
45044
|
+
"orange",
|
|
45045
|
+
"peach"
|
|
45046
|
+
],
|
|
45047
|
+
[
|
|
45048
|
+
78,
|
|
45049
|
+
"bronze",
|
|
45050
|
+
"amber",
|
|
45051
|
+
"apricot"
|
|
45052
|
+
],
|
|
45053
|
+
[
|
|
45054
|
+
100,
|
|
45055
|
+
"ochre",
|
|
45056
|
+
"gold",
|
|
45057
|
+
"pale gold"
|
|
45058
|
+
],
|
|
45059
|
+
[
|
|
45060
|
+
122,
|
|
45061
|
+
"olive",
|
|
45062
|
+
"yellow",
|
|
45063
|
+
"pale yellow"
|
|
45064
|
+
],
|
|
45065
|
+
[
|
|
45066
|
+
160,
|
|
45067
|
+
"forest green",
|
|
45068
|
+
"green",
|
|
45069
|
+
"pale green"
|
|
45070
|
+
],
|
|
45071
|
+
[
|
|
45072
|
+
182,
|
|
45073
|
+
"deep jade",
|
|
45074
|
+
"jade",
|
|
45075
|
+
"mint"
|
|
45076
|
+
],
|
|
45077
|
+
[
|
|
45078
|
+
228,
|
|
45079
|
+
"deep teal",
|
|
45080
|
+
"teal",
|
|
45081
|
+
"aqua"
|
|
45082
|
+
],
|
|
45083
|
+
[
|
|
45084
|
+
258,
|
|
45085
|
+
"deep blue",
|
|
45086
|
+
"blue",
|
|
45087
|
+
"pale blue"
|
|
45088
|
+
],
|
|
45089
|
+
[
|
|
45090
|
+
288,
|
|
45091
|
+
"navy",
|
|
45092
|
+
"blue",
|
|
45093
|
+
"periwinkle"
|
|
45094
|
+
],
|
|
45095
|
+
[
|
|
45096
|
+
318,
|
|
45097
|
+
"indigo",
|
|
45098
|
+
"violet",
|
|
45099
|
+
"lavender"
|
|
45100
|
+
],
|
|
45101
|
+
[
|
|
45102
|
+
348,
|
|
45103
|
+
"plum",
|
|
45104
|
+
"magenta",
|
|
45105
|
+
"orchid"
|
|
45106
|
+
],
|
|
45107
|
+
[
|
|
45108
|
+
361,
|
|
45109
|
+
"burgundy",
|
|
45110
|
+
"rose",
|
|
45111
|
+
"pink"
|
|
45112
|
+
]
|
|
45113
|
+
];
|
|
45114
|
+
/** A plain name for a colour the look made, as an owner would say it. */
|
|
45115
|
+
function colourName(hex$1) {
|
|
45116
|
+
const { l, c, h } = lchOf(hex$1);
|
|
45117
|
+
if (c < .025) return l > .9 ? "off-white" : l < .3 ? "near-black" : l > .7 ? "pale grey" : "grey";
|
|
45118
|
+
const [, dark, mid, light] = names.find(([upTo]) => h < upTo) ?? names[names.length - 1] ?? [
|
|
45119
|
+
361,
|
|
45120
|
+
"grey",
|
|
45121
|
+
"grey",
|
|
45122
|
+
"grey"
|
|
45123
|
+
];
|
|
45124
|
+
if (l < .42) return dark;
|
|
45125
|
+
if (l > .8) return light;
|
|
45126
|
+
return c < .06 ? `muted ${mid}` : mid;
|
|
45127
|
+
}
|
|
45128
|
+
const sentence = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
45129
|
+
function shadeWords(asked, drawn) {
|
|
45130
|
+
const dl = lchOf(drawn).l - lchOf(asked).l;
|
|
45131
|
+
return dl < -.25 ? "a deep shade of" : dl < 0 ? "a deeper shade of" : dl > .25 ? "a pale shade of" : "a lighter shade of";
|
|
45132
|
+
}
|
|
45133
|
+
function ownerWords(entry, whose) {
|
|
45134
|
+
return entry.hex === entry.asked ? `${whose} on ${entry.slot.name}` : `${shadeWords(entry.asked, entry.hex)} ${whose} on ${entry.slot.name}`;
|
|
45135
|
+
}
|
|
45136
|
+
const isGiltEnd = (spec, role) => spec.roles[role].giltEnd === true;
|
|
45137
|
+
/** Two owner colours this close count as one. */
|
|
45138
|
+
const SAME_COLOUR = .05;
|
|
45139
|
+
/** Palettes for the same look keep at least this OKLab ΔE apart on one of their coloured parts. */
|
|
45140
|
+
const OWN_PALETTE_MIN_DIFFERENCE = .06;
|
|
45141
|
+
const colouredRoles = [
|
|
45142
|
+
"primary",
|
|
45143
|
+
"secondary",
|
|
45144
|
+
"accent",
|
|
45145
|
+
"muted"
|
|
45146
|
+
];
|
|
45147
|
+
/** How far apart two palettes are on the parts that carry colour. */
|
|
45148
|
+
function paletteDifference(a, b) {
|
|
45149
|
+
return Math.max(...colouredRoles.map((role) => colourDistance(a[role], b[role])));
|
|
45150
|
+
}
|
|
45151
|
+
function slotFor(spec, role) {
|
|
45152
|
+
return spec.slots.find((entry) => entry.roles.includes(role)) ?? {
|
|
45153
|
+
roles: [role],
|
|
45154
|
+
name: spec.roles[role].name
|
|
45155
|
+
};
|
|
45156
|
+
}
|
|
45157
|
+
function placementsOf(painted) {
|
|
45158
|
+
const owned = painted.held.flatMap((entry) => entry.source === "made" ? [] : [{
|
|
45159
|
+
colour: entry.source,
|
|
45160
|
+
role: entry.role,
|
|
45161
|
+
place: entry.slot.name,
|
|
45162
|
+
drawn: entry.hex === entry.asked ? { kind: "exact" } : {
|
|
45163
|
+
kind: "nearest-shade",
|
|
45164
|
+
hex: entry.hex,
|
|
45165
|
+
deltaE: entry.deltaE
|
|
45166
|
+
}
|
|
45167
|
+
}]);
|
|
45168
|
+
const [first, ...rest] = owned;
|
|
45169
|
+
return first ? [first, ...rest] : null;
|
|
45170
|
+
}
|
|
45171
|
+
function offered(painted, palette, admit) {
|
|
45172
|
+
return admit(painted) ? {
|
|
45173
|
+
kind: "offered",
|
|
45174
|
+
painted,
|
|
45175
|
+
palette
|
|
45176
|
+
} : {
|
|
45177
|
+
kind: "unfit",
|
|
45178
|
+
reason: { kind: "buttons-replaced" }
|
|
45179
|
+
};
|
|
45180
|
+
}
|
|
45181
|
+
/** The cheapest offered candidate, or the first reason none was. */
|
|
45182
|
+
function bestOffered(candidates) {
|
|
45183
|
+
const offers = candidates.flatMap((candidate$1) => candidate$1.kind === "offered" ? [candidate$1] : []);
|
|
45184
|
+
const best = offers.reduce((chosen, next) => !chosen || next.painted.cost < chosen.painted.cost ? next : chosen, undefined);
|
|
45185
|
+
return best ?? candidates.find((candidate$1) => candidate$1.kind === "unfit") ?? {
|
|
45186
|
+
kind: "unfit",
|
|
45187
|
+
reason: { kind: "buttons-replaced" }
|
|
45188
|
+
};
|
|
45189
|
+
}
|
|
45190
|
+
function singleCandidate(spec, first, slot, admit) {
|
|
45191
|
+
const painted = paintPalette(spec, {
|
|
45192
|
+
lead: first,
|
|
45193
|
+
holds: [{
|
|
45194
|
+
source: "first",
|
|
45195
|
+
hex: first,
|
|
45196
|
+
slot
|
|
45197
|
+
}]
|
|
45198
|
+
});
|
|
45199
|
+
if (painted.kind === "unfit") return painted;
|
|
45200
|
+
const lead = painted.held[0];
|
|
45201
|
+
const placements = placementsOf(painted);
|
|
45202
|
+
if (!lead || !placements) return {
|
|
45203
|
+
kind: "unfit",
|
|
45204
|
+
reason: {
|
|
45205
|
+
kind: "role-unmet",
|
|
45206
|
+
role: slot.roles[0]
|
|
45207
|
+
}
|
|
45208
|
+
};
|
|
45209
|
+
const mentions = spec.order.filter((role) => role !== lead.role && spec.roles[role].mention && !(isGiltEnd(spec, lead.role) && isGiltEnd(spec, role))).map((role) => `${spec.roles[role].name} in ${shadeWords(first, painted.colors[role])} it`);
|
|
45210
|
+
const describe = sentence(`${ownerWords(lead, "your colour")}${mentions.length ? `, with ${mentions.join(" and ")}` : ""}`);
|
|
45211
|
+
return offered(painted, {
|
|
45212
|
+
kind: "single",
|
|
45213
|
+
colors: painted.colors,
|
|
45214
|
+
mode: painted.mode,
|
|
45215
|
+
describe,
|
|
45216
|
+
placements,
|
|
45217
|
+
made: null
|
|
45218
|
+
}, admit);
|
|
45219
|
+
}
|
|
45220
|
+
/** The owner's colour alone, in the place it suits best of those the look keeps its buttons in. */
|
|
45221
|
+
function singlePalette(spec, first, admit) {
|
|
45222
|
+
return bestOffered(spec.slots.map((slot) => singleCandidate(spec, first, slot, admit)));
|
|
45223
|
+
}
|
|
45224
|
+
/** Where the second colour shows, in words: the owner's is "your second colour", a made one is named. */
|
|
45225
|
+
function secondWords(spec, rule$1, painted, placed, whose) {
|
|
45226
|
+
if (isGiltEnd(spec, placed.role)) {
|
|
45227
|
+
return `gilt buttons running from ${colourName(painted.colors.accent)} into ${colourName(painted.colors.primary)}`;
|
|
45228
|
+
}
|
|
45229
|
+
const name = placed.source !== "made" ? placed.hex === placed.asked ? whose : `${shadeWords(placed.asked, placed.hex)} ${whose}` : colourName(placed.hex);
|
|
45230
|
+
return `${name} on ${placed.slot.name}${rule$1.alsoTints ? ` and ${rule$1.alsoTints.name}` : ""}`;
|
|
45231
|
+
}
|
|
45232
|
+
function withSecond(spec, rule$1, first, second, admit) {
|
|
45233
|
+
const holds = [{
|
|
45234
|
+
source: "first",
|
|
45235
|
+
hex: first,
|
|
45236
|
+
slot: slotFor(spec, rule$1.lead)
|
|
45237
|
+
}, {
|
|
45238
|
+
source: second.source,
|
|
45239
|
+
hex: second.hex,
|
|
45240
|
+
slot: slotFor(spec, rule$1.second)
|
|
45241
|
+
}];
|
|
45242
|
+
const hueSources = Object.fromEntries((rule$1.alsoTints?.roles ?? []).map((role) => [role, second.hex]));
|
|
45243
|
+
const painted = paintPalette(spec, {
|
|
45244
|
+
lead: first,
|
|
45245
|
+
holds,
|
|
45246
|
+
hueSources
|
|
45247
|
+
});
|
|
45248
|
+
if (painted.kind === "unfit") return painted;
|
|
45249
|
+
const lead = painted.held.find((entry) => entry.source === "first");
|
|
45250
|
+
const placedSecond = painted.held.find((entry) => entry.source === second.source);
|
|
45251
|
+
const placements = placementsOf(painted);
|
|
45252
|
+
if (!lead || !placedSecond || !placements) return {
|
|
45253
|
+
kind: "unfit",
|
|
45254
|
+
reason: {
|
|
45255
|
+
kind: "role-unmet",
|
|
45256
|
+
role: rule$1.second
|
|
45257
|
+
}
|
|
45258
|
+
};
|
|
45259
|
+
const owner = second.source === "second";
|
|
45260
|
+
const describe = sentence(`${ownerWords(lead, owner ? "your first colour" : "your colour")}, with ${secondWords(spec, rule$1, painted, placedSecond, "your second colour")}`);
|
|
45261
|
+
const body = {
|
|
45262
|
+
colors: painted.colors,
|
|
45263
|
+
mode: painted.mode,
|
|
45264
|
+
describe,
|
|
45265
|
+
placements
|
|
45266
|
+
};
|
|
45267
|
+
return offered(painted, owner ? {
|
|
45268
|
+
...body,
|
|
45269
|
+
kind: "owner-contrast",
|
|
45270
|
+
made: null
|
|
45271
|
+
} : {
|
|
45272
|
+
...body,
|
|
45273
|
+
kind: "made-contrast",
|
|
45274
|
+
made: {
|
|
45275
|
+
rule: rule$1.label,
|
|
45276
|
+
hex: placedSecond.hex,
|
|
45277
|
+
role: placedSecond.role,
|
|
45278
|
+
place: placedSecond.slot.name
|
|
45279
|
+
}
|
|
45280
|
+
}, admit);
|
|
45281
|
+
}
|
|
45282
|
+
const differenceFrom = (before, palette) => Math.min(...before.map((other) => paletteDifference(other.colors, palette.colors)));
|
|
45283
|
+
/**
|
|
45284
|
+
* The rule's contrast: its best hue whose palette the look can offer and
|
|
45285
|
+
* that looks visibly different from the palettes before it, otherwise the
|
|
45286
|
+
* offered one that differs most. Unfit only when no hue gives an offered palette.
|
|
45287
|
+
*/
|
|
45288
|
+
function madeContrast(spec, rule$1, first, avoidHue, before, admit) {
|
|
45289
|
+
const offers = [];
|
|
45290
|
+
const failures = [];
|
|
45291
|
+
for (const hue of candidateHues(rule$1, lchOf(first).h, avoidHue).flat()) {
|
|
45292
|
+
const candidate$1 = withSecond(spec, rule$1, first, {
|
|
45293
|
+
source: "made",
|
|
45294
|
+
hex: madeColour(rule$1, first, hue)
|
|
45295
|
+
}, admit);
|
|
45296
|
+
if (candidate$1.kind === "unfit") {
|
|
45297
|
+
failures.push(candidate$1.reason);
|
|
45298
|
+
continue;
|
|
45299
|
+
}
|
|
45300
|
+
const offer = {
|
|
45301
|
+
palette: candidate$1.palette,
|
|
45302
|
+
hue: lchOf(candidate$1.palette.made?.hex ?? first).h,
|
|
45303
|
+
difference: differenceFrom(before, candidate$1.palette)
|
|
45304
|
+
};
|
|
45305
|
+
if (offer.difference >= OWN_PALETTE_MIN_DIFFERENCE) return offer;
|
|
45306
|
+
offers.push(offer);
|
|
45307
|
+
}
|
|
45308
|
+
const best = offers.reduce((chosen, next) => !chosen || next.difference > chosen.difference ? next : chosen, undefined);
|
|
45309
|
+
if (best) return best;
|
|
45310
|
+
return {
|
|
45311
|
+
kind: "unfit",
|
|
45312
|
+
reason: failures[0] ?? { kind: "buttons-replaced" }
|
|
45313
|
+
};
|
|
45314
|
+
}
|
|
45315
|
+
/**
|
|
45316
|
+
* The owner's own second colour as palette 2, in whichever of the look's two
|
|
45317
|
+
* contrast places needs it moved least among those that look visibly
|
|
45318
|
+
* different from the single-colour palette. Null when neither does.
|
|
45319
|
+
*/
|
|
45320
|
+
function ownerContrastFor(spec, first, second, single, admit) {
|
|
45321
|
+
const distinct$1 = spec.contrast.map((rule$1) => withSecond(spec, rule$1, first, {
|
|
45322
|
+
source: "second",
|
|
45323
|
+
hex: second
|
|
45324
|
+
}, admit)).filter((candidate$1) => candidate$1.kind === "offered" && paletteDifference(candidate$1.palette.colors, single.colors) >= OWN_PALETTE_MIN_DIFFERENCE);
|
|
45325
|
+
const chosen = bestOffered(distinct$1);
|
|
45326
|
+
return chosen.kind === "offered" ? chosen.palette : null;
|
|
45327
|
+
}
|
|
45328
|
+
const isMade = (result) => "palette" in result;
|
|
45329
|
+
/**
|
|
45330
|
+
* Three palettes for a booking look from one or two owner colours (fewer only
|
|
45331
|
+
* when the first colour has no hue to make a contrast from). `admit` is the
|
|
45332
|
+
* surface owner's decision on whether the look keeps its own buttons; see
|
|
45333
|
+
* `bookingLookButtonAdmission`. Deterministic, so a stored palette can be
|
|
45334
|
+
* matched back to the palette it came from.
|
|
45335
|
+
*/
|
|
45336
|
+
function ownColourPalettes(spec, colours, admit) {
|
|
45337
|
+
const [first, maybeSecond] = colours;
|
|
45338
|
+
const tooAlike = maybeSecond !== undefined && colourDistance(first, maybeSecond) < SAME_COLOUR;
|
|
45339
|
+
const second = tooAlike ? undefined : maybeSecond;
|
|
45340
|
+
const singleResult = singlePalette(spec, first, admit);
|
|
45341
|
+
if (singleResult.kind === "unfit") return singleResult;
|
|
45342
|
+
const single = singleResult.palette;
|
|
45343
|
+
const ownerContrast = second ? ownerContrastFor(spec, first, second, single, admit) : null;
|
|
45344
|
+
if (lchOf(first).c < ACHROMATIC_CHROMA) {
|
|
45345
|
+
return {
|
|
45346
|
+
kind: "fewer",
|
|
45347
|
+
palettes: ownerContrast ? [single, ownerContrast] : [single],
|
|
45348
|
+
reason: "no-hue"
|
|
45349
|
+
};
|
|
45350
|
+
}
|
|
45351
|
+
if (second && ownerContrast) return withOwnerContrast(spec, first, second, single, ownerContrast, admit);
|
|
45352
|
+
return withMadeContrasts(spec, first, single, maybeSecond === undefined ? "none" : "too-alike", admit);
|
|
45353
|
+
}
|
|
45354
|
+
function withOwnerContrast(spec, first, second, single, ownerContrast, admit) {
|
|
45355
|
+
const made = madeContrast(spec, spec.contrast[0], first, lchOf(second).h, [single, ownerContrast], admit);
|
|
45356
|
+
if (!isMade(made)) return made;
|
|
45357
|
+
return {
|
|
45358
|
+
kind: "three",
|
|
45359
|
+
palettes: [
|
|
45360
|
+
single,
|
|
45361
|
+
ownerContrast,
|
|
45362
|
+
made.palette
|
|
45363
|
+
],
|
|
45364
|
+
secondColour: "used"
|
|
45365
|
+
};
|
|
45366
|
+
}
|
|
45367
|
+
/** Both of the look's made contrasts. A second colour given but not used was too close to the first, or to the single-colour palette. */
|
|
45368
|
+
function withMadeContrasts(spec, first, single, secondColour, admit) {
|
|
45369
|
+
const [ruleA, ruleB] = spec.contrast;
|
|
45370
|
+
const a = madeContrast(spec, ruleA, first, null, [single], admit);
|
|
45371
|
+
if (!isMade(a)) return a;
|
|
45372
|
+
const b = madeContrast(spec, ruleB, first, a.hue, [single, a.palette], admit);
|
|
45373
|
+
if (!isMade(b)) return b;
|
|
45374
|
+
return {
|
|
45375
|
+
kind: "three",
|
|
45376
|
+
palettes: [
|
|
45377
|
+
single,
|
|
45378
|
+
a.palette,
|
|
45379
|
+
b.palette
|
|
45380
|
+
],
|
|
45381
|
+
secondColour
|
|
45382
|
+
};
|
|
45383
|
+
}
|
|
45384
|
+
|
|
45385
|
+
//#endregion
|
|
45386
|
+
//#region ../theme-core/src/site-styles/ownColours/bookingLookButtonAdmission.ts
|
|
45387
|
+
/**
|
|
45388
|
+
* Where each look's most prominent buttons sit on a booking page, per the
|
|
45389
|
+
* surface table (apps/frontend hostedBookingSurfaceTable.test.ts): the
|
|
45390
|
+
* opening, and the offerings section's cards or section.
|
|
45391
|
+
*/
|
|
45392
|
+
const bookingLookProminentButtonSurfaces = {
|
|
45393
|
+
"site-style:fulcrum": ["base", "sheet"],
|
|
45394
|
+
"site-style:folio": ["base", "sheet"],
|
|
45395
|
+
"site-style:measure": ["base", "base"],
|
|
45396
|
+
"site-style:grounded-practice": ["band", "base"],
|
|
45397
|
+
"site-style:lantern": ["base", "band"],
|
|
45398
|
+
"site-style:poster": ["brand", "sheet"]
|
|
45399
|
+
};
|
|
45400
|
+
/** Grounded Practice's legacy sections carry only resolved styles: the look's button fills from a palette token. */
|
|
45401
|
+
function legacyKeepsButton(theme, surface$1) {
|
|
45402
|
+
const color = surface$1 === "band" ? "muted" : "background";
|
|
45403
|
+
const resolved = resolveSurface({
|
|
45404
|
+
kind: "section",
|
|
45405
|
+
background: {
|
|
45406
|
+
type: "color",
|
|
45407
|
+
color
|
|
45408
|
+
}
|
|
45409
|
+
}, opaqueSurfaceContextFromTheme(theme));
|
|
45410
|
+
const styles = resolved.buttonStyles;
|
|
45411
|
+
return `${styles["--rb-action-primary-base-background-color"] ?? ""}${styles["--rb-action-primary-base-background-image"] ?? ""}`.includes("var(--tb-");
|
|
45412
|
+
}
|
|
45413
|
+
function keptPrimary(primary) {
|
|
45414
|
+
return primary?.kind === "preferred" || primary?.kind === "repaired" && primary.candidate === "contextual-hover";
|
|
45415
|
+
}
|
|
45416
|
+
/**
|
|
45417
|
+
* Whether the surface owner keeps the look's own primary button on this
|
|
45418
|
+
* surface, in every state, rather than replacing it with an outline.
|
|
45419
|
+
*/
|
|
45420
|
+
function keepsLookButton(style, theme, surface$1) {
|
|
45421
|
+
if (style.looks.kind !== "offers") return legacyKeepsButton(theme, surface$1);
|
|
45422
|
+
const declared = style.looks.surfaces[surface$1];
|
|
45423
|
+
if (declared?.kind !== "token") return false;
|
|
45424
|
+
const resolved = resolveSurface({
|
|
45425
|
+
kind: "token",
|
|
45426
|
+
token: declared.token,
|
|
45427
|
+
...declared.buttonContext ? { buttonContext: declared.buttonContext } : {},
|
|
45428
|
+
...declared.foreground ? { foreground: declared.foreground } : {}
|
|
45429
|
+
}, opaqueSurfaceContextFromTheme(theme));
|
|
45430
|
+
return keptPrimary(resolved.buttonContext.roles?.primary);
|
|
45431
|
+
}
|
|
45432
|
+
/** The look's theme with this palette, compiled as a booking page would be. */
|
|
45433
|
+
function bookingLookThemeFor(style, palette) {
|
|
45434
|
+
return compileReplacementSiteThemeFromSelections({
|
|
45435
|
+
siteStyle: style,
|
|
45436
|
+
palette: {
|
|
45437
|
+
colors: Object.entries(palette.colors).map(([name, hex$1]) => ({
|
|
45438
|
+
name,
|
|
45439
|
+
hex: hex$1
|
|
45440
|
+
})),
|
|
45441
|
+
meta: { mode: palette.mode }
|
|
45442
|
+
},
|
|
45443
|
+
compilationPlan: replaceSiteStyleCompilationPlan({
|
|
45444
|
+
headerSelection: defaultHeaderSelectionForSiteStyle(style),
|
|
45445
|
+
buttonStyleSelection: null
|
|
45446
|
+
})
|
|
45447
|
+
}).theme;
|
|
45448
|
+
}
|
|
45449
|
+
/** The surface owner's decision, for one booking look, on whether a palette keeps the look's buttons where they matter most. */
|
|
45450
|
+
function bookingLookButtonAdmission(lookId) {
|
|
45451
|
+
const style = getCuratedSiteStyle(asSiteStyleId(lookId));
|
|
45452
|
+
if (!style) throw new Error(`Missing booking look ${lookId}`);
|
|
45453
|
+
return (palette) => {
|
|
45454
|
+
const theme = bookingLookThemeFor(style, palette);
|
|
45455
|
+
return bookingLookProminentButtonSurfaces[lookId].every((surface$1) => keepsLookButton(style, theme, surface$1));
|
|
45456
|
+
};
|
|
45457
|
+
}
|
|
45458
|
+
/** A booking look's own-colour palettes, admitted through the surface owner. */
|
|
45459
|
+
function bookingLookOwnColourPalettes(lookId, colours) {
|
|
45460
|
+
return ownColourPalettes(bookingLookColourSpecs[lookId], colours, bookingLookButtonAdmission(lookId));
|
|
45461
|
+
}
|
|
45462
|
+
|
|
43436
45463
|
//#endregion
|
|
43437
45464
|
//#region ../site-commands/src/application.ts
|
|
43438
45465
|
const commandEffectKinds = [
|
|
@@ -44095,174 +46122,6 @@ function generateCssVars(palette) {
|
|
|
44095
46122
|
return cssVars;
|
|
44096
46123
|
}
|
|
44097
46124
|
|
|
44098
|
-
//#endregion
|
|
44099
|
-
//#region ../theme-core/src/palette/family.ts
|
|
44100
|
-
/**
|
|
44101
|
-
* Only `colourFamily` makes one (the class is not exported), so a caller can't
|
|
44102
|
-
* swap in its own members and still hand the solver a valid family. That
|
|
44103
|
-
* matters because `unpaintable` relies on the members running from black to
|
|
44104
|
-
* white.
|
|
44105
|
-
*/
|
|
44106
|
-
var Family = class {
|
|
44107
|
-
#members;
|
|
44108
|
-
constructor(seed, members) {
|
|
44109
|
-
this.seed = seed;
|
|
44110
|
-
this.#members = Object.freeze(members);
|
|
44111
|
-
}
|
|
44112
|
-
/** Unique by hex, ascending luminance. Includes the exact seed, black and white. */
|
|
44113
|
-
get members() {
|
|
44114
|
-
return this.#members;
|
|
44115
|
-
}
|
|
44116
|
-
};
|
|
44117
|
-
/** Lightness steps between black and white. */
|
|
44118
|
-
const FAMILY_STEPS = 512;
|
|
44119
|
-
const GAMUT_BISECTION_STEPS = 18;
|
|
44120
|
-
/** The highest chroma at or below `c` that stays inside sRGB at this lightness and hue. */
|
|
44121
|
-
function gamutChroma(l, c, h) {
|
|
44122
|
-
if (inLinearSrgbGamut(oklchToLinearSrgb(l, c, h))) return c;
|
|
44123
|
-
return Array.from({ length: GAMUT_BISECTION_STEPS }).reduce(({ lo, hi }) => {
|
|
44124
|
-
const mid = (lo + hi) / 2;
|
|
44125
|
-
return inLinearSrgbGamut(oklchToLinearSrgb(l, mid, h)) ? {
|
|
44126
|
-
lo: mid,
|
|
44127
|
-
hi
|
|
44128
|
-
} : {
|
|
44129
|
-
lo,
|
|
44130
|
-
hi: mid
|
|
44131
|
-
};
|
|
44132
|
-
}, {
|
|
44133
|
-
lo: 0,
|
|
44134
|
-
hi: c
|
|
44135
|
-
}).lo;
|
|
44136
|
-
}
|
|
44137
|
-
function oklabDistance(a, b) {
|
|
44138
|
-
return Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b);
|
|
44139
|
-
}
|
|
44140
|
-
function colourFamily(seedHex) {
|
|
44141
|
-
if (!/^#[0-9a-f]{6}$/i.test(seedHex)) throw new Error(`Expected an opaque #rrggbb colour: ${seedHex}`);
|
|
44142
|
-
const features = featuresFromHex(seedHex.toLowerCase());
|
|
44143
|
-
const seed = member(seedHex.toLowerCase(), {
|
|
44144
|
-
l: features.okL,
|
|
44145
|
-
c: features.okC,
|
|
44146
|
-
h: features.okH
|
|
44147
|
-
});
|
|
44148
|
-
const seedTaper = Math.max(.001, Math.sin(Math.PI * seed.sample.l));
|
|
44149
|
-
const samples = Array.from({ length: FAMILY_STEPS + 1 }, (_, i) => {
|
|
44150
|
-
const l = i / FAMILY_STEPS;
|
|
44151
|
-
const c = gamutChroma(l, seed.sample.c * Math.min(1, Math.sin(Math.PI * l) / seedTaper), seed.sample.h);
|
|
44152
|
-
return sampleMember({
|
|
44153
|
-
l,
|
|
44154
|
-
c,
|
|
44155
|
-
h: seed.sample.h
|
|
44156
|
-
});
|
|
44157
|
-
});
|
|
44158
|
-
const unique = new Map([seed, ...samples].map((candidate$1) => [candidate$1.hex, candidate$1]).reverse());
|
|
44159
|
-
const members = [...unique.values()].sort((a, b) => a.y - b.y || (a.hex < b.hex ? -1 : a.hex > b.hex ? 1 : 0));
|
|
44160
|
-
return new Family(seed, members);
|
|
44161
|
-
}
|
|
44162
|
-
function sampleMember(sample) {
|
|
44163
|
-
const { r, g, b } = oklchToLinearSrgb(sample.l, sample.c, sample.h);
|
|
44164
|
-
return member(rgbToHex(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b)), sample);
|
|
44165
|
-
}
|
|
44166
|
-
function member(hex, sample) {
|
|
44167
|
-
const { r, g, b } = hexToRgb(hex);
|
|
44168
|
-
return {
|
|
44169
|
-
hex,
|
|
44170
|
-
y: hexLuminance(hex),
|
|
44171
|
-
oklab: srgbToOklab(r, g, b),
|
|
44172
|
-
sample
|
|
44173
|
-
};
|
|
44174
|
-
}
|
|
44175
|
-
|
|
44176
|
-
//#endregion
|
|
44177
|
-
//#region ../theme-core/src/palette/utils/inkSolver.ts
|
|
44178
|
-
/**
|
|
44179
|
-
* How far (OKLab ΔE) a brand colour drawn as ink may be nudged along its own
|
|
44180
|
-
* family before it stops reading as that colour. Past this the caller uses
|
|
44181
|
-
* neutral or palette text instead (Will, 2026-10-14).
|
|
44182
|
-
*/
|
|
44183
|
-
const BRAND_INK_MAX_DELTA_E = .08;
|
|
44184
|
-
/**
|
|
44185
|
-
* The seed itself when it clears `paint`, otherwise the family member nearest
|
|
44186
|
-
* the seed (OKLab ΔE) that clears. On a light ground that is darker, on a dark
|
|
44187
|
-
* one lighter. A passing member further than `maxDistance` from the seed is
|
|
44188
|
-
* refused as `too-far`, which is distinct from `unpaintable`: the family runs
|
|
44189
|
-
* from black to white, so if no member clears, neither does black or white.
|
|
44190
|
-
*/
|
|
44191
|
-
function inkFrom(family, paint, ratio$1, maxDistance = BRAND_INK_MAX_DELTA_E) {
|
|
44192
|
-
assertContrastRatio(ratio$1);
|
|
44193
|
-
if (!Number.isFinite(maxDistance) || maxDistance < 0) {
|
|
44194
|
-
throw new RangeError(`Maximum ink distance must be finite and not negative: ${maxDistance}`);
|
|
44195
|
-
}
|
|
44196
|
-
const seedRatio = worstContrast(family.seed.y, paint);
|
|
44197
|
-
if (seedRatio >= ratio$1) return {
|
|
44198
|
-
kind: "ink",
|
|
44199
|
-
hex: family.seed.hex,
|
|
44200
|
-
ratio: seedRatio,
|
|
44201
|
-
source: "exact"
|
|
44202
|
-
};
|
|
44203
|
-
const nearest = family.members.reduce((best, member$1) => {
|
|
44204
|
-
const achieved = worstContrast(member$1.y, paint);
|
|
44205
|
-
if (achieved < ratio$1) return best;
|
|
44206
|
-
const distance$1 = oklabDistance(member$1.oklab, family.seed.oklab);
|
|
44207
|
-
return best === null || distance$1 < best.distance ? {
|
|
44208
|
-
hex: member$1.hex,
|
|
44209
|
-
distance: distance$1,
|
|
44210
|
-
ratio: achieved
|
|
44211
|
-
} : best;
|
|
44212
|
-
}, null);
|
|
44213
|
-
if (nearest === null) return {
|
|
44214
|
-
kind: "unpaintable",
|
|
44215
|
-
interval: paint,
|
|
44216
|
-
ratio: ratio$1
|
|
44217
|
-
};
|
|
44218
|
-
if (nearest.distance > maxDistance) {
|
|
44219
|
-
return {
|
|
44220
|
-
kind: "too-far",
|
|
44221
|
-
nearest: {
|
|
44222
|
-
hex: nearest.hex,
|
|
44223
|
-
distance: nearest.distance
|
|
44224
|
-
},
|
|
44225
|
-
maxDistance
|
|
44226
|
-
};
|
|
44227
|
-
}
|
|
44228
|
-
return {
|
|
44229
|
-
kind: "ink",
|
|
44230
|
-
hex: nearest.hex,
|
|
44231
|
-
ratio: nearest.ratio,
|
|
44232
|
-
source: "family"
|
|
44233
|
-
};
|
|
44234
|
-
}
|
|
44235
|
-
/**
|
|
44236
|
-
* Neutral text for a paint. The style's preferred neutral wins when one of its
|
|
44237
|
-
* pair clears (the higher-contrast one, dark on a tie); otherwise literal black
|
|
44238
|
-
* or white by `maxContrastBlackOrWhite`; otherwise `unpaintable`.
|
|
44239
|
-
*/
|
|
44240
|
-
function neutralInk(paint, ratio$1, preferred) {
|
|
44241
|
-
assertContrastRatio(ratio$1);
|
|
44242
|
-
if (preferred) {
|
|
44243
|
-
const dark = worstContrast(hexLuminance(preferred.dark), paint);
|
|
44244
|
-
const light = worstContrast(hexLuminance(preferred.light), paint);
|
|
44245
|
-
const [hex, achieved] = dark >= light ? [preferred.dark, dark] : [preferred.light, light];
|
|
44246
|
-
if (achieved >= ratio$1) return {
|
|
44247
|
-
kind: "ink",
|
|
44248
|
-
hex,
|
|
44249
|
-
ratio: achieved,
|
|
44250
|
-
source: "neutral"
|
|
44251
|
-
};
|
|
44252
|
-
}
|
|
44253
|
-
const literal = maxContrastBlackOrWhite(paint);
|
|
44254
|
-
return literal.ratio >= ratio$1 ? {
|
|
44255
|
-
kind: "ink",
|
|
44256
|
-
hex: literal.ink === "black" ? "#000000" : "#ffffff",
|
|
44257
|
-
ratio: literal.ratio,
|
|
44258
|
-
source: "neutral"
|
|
44259
|
-
} : {
|
|
44260
|
-
kind: "unpaintable",
|
|
44261
|
-
interval: paint,
|
|
44262
|
-
ratio: ratio$1
|
|
44263
|
-
};
|
|
44264
|
-
}
|
|
44265
|
-
|
|
44266
46125
|
//#endregion
|
|
44267
46126
|
//#region ../theme-core/src/palette/utils/backgroundGeneration.ts
|
|
44268
46127
|
/**
|
|
@@ -44757,7 +46616,7 @@ const allRecipes = [
|
|
|
44757
46616
|
//#region ../theme-core/src/palette/utils/legs.ts
|
|
44758
46617
|
const clamp01$1 = (x) => Math.max(0, Math.min(1, x));
|
|
44759
46618
|
function nudgeToneForContrast(color, againstHex, target, prefer) {
|
|
44760
|
-
const steps = [
|
|
46619
|
+
const steps$1 = [
|
|
44761
46620
|
0,
|
|
44762
46621
|
.02,
|
|
44763
46622
|
.04,
|
|
@@ -44772,18 +46631,18 @@ function nudgeToneForContrast(color, againstHex, target, prefer) {
|
|
|
44772
46631
|
L: color.L
|
|
44773
46632
|
};
|
|
44774
46633
|
let bestMiss = Infinity;
|
|
44775
|
-
for (const dir of dirs) for (const s of steps) {
|
|
46634
|
+
for (const dir of dirs) for (const s of steps$1) {
|
|
44776
46635
|
const L = clamp01$1(color.L + dir * s);
|
|
44777
|
-
const hex = oklchToHexGamut(L, color.C, color.h);
|
|
44778
|
-
const cr = contrastRatio(hex, againstHex);
|
|
46636
|
+
const hex$1 = oklchToHexGamut(L, color.C, color.h);
|
|
46637
|
+
const cr = contrastRatio(hex$1, againstHex);
|
|
44779
46638
|
const miss = Math.max(0, target - cr);
|
|
44780
46639
|
if (miss === 0) return {
|
|
44781
|
-
hex,
|
|
46640
|
+
hex: hex$1,
|
|
44782
46641
|
L
|
|
44783
46642
|
};
|
|
44784
46643
|
if (miss < bestMiss) {
|
|
44785
46644
|
best = {
|
|
44786
|
-
hex,
|
|
46645
|
+
hex: hex$1,
|
|
44787
46646
|
L
|
|
44788
46647
|
};
|
|
44789
46648
|
bestMiss = miss;
|
|
@@ -44791,8 +46650,8 @@ function nudgeToneForContrast(color, againstHex, target, prefer) {
|
|
|
44791
46650
|
}
|
|
44792
46651
|
return best;
|
|
44793
46652
|
}
|
|
44794
|
-
function oklchFromHex(hex) {
|
|
44795
|
-
const f = featuresFromHex(hex);
|
|
46653
|
+
function oklchFromHex(hex$1) {
|
|
46654
|
+
const f = featuresFromHex(hex$1);
|
|
44796
46655
|
return {
|
|
44797
46656
|
L: f.okL,
|
|
44798
46657
|
C: f.okC,
|
|
@@ -44816,16 +46675,16 @@ function ensureHueLeg(primaryHex, legs, targetL, targetCmid, backgroundHex, pref
|
|
|
44816
46675
|
const L = targetL;
|
|
44817
46676
|
const C = targetCmid;
|
|
44818
46677
|
const h = wrapDeg(p.h + offset);
|
|
44819
|
-
let hex = oklchToHexGamut(L, C, h);
|
|
44820
|
-
hex = nudgeToneForContrast({
|
|
46678
|
+
let hex$1 = oklchToHexGamut(L, C, h);
|
|
46679
|
+
hex$1 = nudgeToneForContrast({
|
|
44821
46680
|
L,
|
|
44822
46681
|
C,
|
|
44823
46682
|
h
|
|
44824
46683
|
}, backgroundHex, 3, prefer).hex;
|
|
44825
|
-
const a = oklchFromHex(hex);
|
|
44826
|
-
const score = .7 * dOKLCH(a, p) + .3 * (contrastRatio(hex, backgroundHex) / 7);
|
|
46684
|
+
const a = oklchFromHex(hex$1);
|
|
46685
|
+
const score = .7 * dOKLCH(a, p) + .3 * (contrastRatio(hex$1, backgroundHex) / 7);
|
|
44827
46686
|
return {
|
|
44828
|
-
hex,
|
|
46687
|
+
hex: hex$1,
|
|
44829
46688
|
h,
|
|
44830
46689
|
score
|
|
44831
46690
|
};
|
|
@@ -44897,12 +46756,12 @@ function generateSupportRole(targetHue, theme, labels, backgroundHex, avoidHexes
|
|
|
44897
46756
|
}
|
|
44898
46757
|
const preferDir = theme === "dark" ? "lighter" : "darker";
|
|
44899
46758
|
const _hex0 = oklchToHexGamut(Ltarget, Cmid, h);
|
|
44900
|
-
const { hex } = nudgeToneForContrast({
|
|
46759
|
+
const { hex: hex$1 } = nudgeToneForContrast({
|
|
44901
46760
|
L: Ltarget,
|
|
44902
46761
|
C: Cmid,
|
|
44903
46762
|
h
|
|
44904
46763
|
}, backgroundHex, 3, preferDir);
|
|
44905
|
-
return hex;
|
|
46764
|
+
return hex$1;
|
|
44906
46765
|
}
|
|
44907
46766
|
|
|
44908
46767
|
//#endregion
|
|
@@ -44930,18 +46789,18 @@ function legibleTextOn(bgHex, _prefer, min = 4.5, opts) {
|
|
|
44930
46789
|
const startL = bgIsLight ? .14 : .97;
|
|
44931
46790
|
const preferDir = bgIsLight ? "darker" : "lighter";
|
|
44932
46791
|
const target = Math.max(3, min + cushion);
|
|
44933
|
-
const { hex } = nudgeToneForContrast({
|
|
46792
|
+
const { hex: hex$1 } = nudgeToneForContrast({
|
|
44934
46793
|
L: startL,
|
|
44935
46794
|
C,
|
|
44936
46795
|
h: neutralH
|
|
44937
46796
|
}, bgHex, target, preferDir);
|
|
44938
|
-
const test = contrastRatio(hex, bgHex);
|
|
46797
|
+
const test = contrastRatio(hex$1, bgHex);
|
|
44939
46798
|
if (test >= min) {
|
|
44940
46799
|
const Lclip = (L) => {
|
|
44941
46800
|
if (bgIsLight) return Math.max(.1, Math.min(.3, L));
|
|
44942
46801
|
return Math.max(.85, Math.min(.98, L));
|
|
44943
46802
|
};
|
|
44944
|
-
const f = featuresFromHex(hex);
|
|
46803
|
+
const f = featuresFromHex(hex$1);
|
|
44945
46804
|
return oklchToHexGamut(Lclip(f.okL), C, neutralH);
|
|
44946
46805
|
}
|
|
44947
46806
|
return bgIsLight ? "#000000" : "#FFFFFF";
|
|
@@ -45287,16 +47146,16 @@ const RULES = [
|
|
|
45287
47146
|
}
|
|
45288
47147
|
}
|
|
45289
47148
|
];
|
|
45290
|
-
function classifyColor(hex, rules = RULES) {
|
|
45291
|
-
const f = featuresFromHex(hex);
|
|
47149
|
+
function classifyColor(hex$1, rules = RULES) {
|
|
47150
|
+
const f = featuresFromHex(hex$1);
|
|
45292
47151
|
const labels = rules.map((r) => ({
|
|
45293
47152
|
label: r.label,
|
|
45294
47153
|
score: clamp01(r.score(f))
|
|
45295
47154
|
})).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
|
|
45296
47155
|
return labels;
|
|
45297
47156
|
}
|
|
45298
|
-
function topLabel(hex, rules = RULES) {
|
|
45299
|
-
const list = classifyColor(hex, rules);
|
|
47157
|
+
function topLabel(hex$1, rules = RULES) {
|
|
47158
|
+
const list = classifyColor(hex$1, rules);
|
|
45300
47159
|
return list[0] ?? null;
|
|
45301
47160
|
}
|
|
45302
47161
|
|
|
@@ -45382,8 +47241,8 @@ function suggestRecipes(labels, fallback = [
|
|
|
45382
47241
|
const ranked = Array.from(scores.entries()).sort((a, b) => b[1] - a[1]).map(([id$2]) => id$2);
|
|
45383
47242
|
return ranked.length ? ranked : fallback;
|
|
45384
47243
|
}
|
|
45385
|
-
function generateAllPalettes(hex, _labels) {
|
|
45386
|
-
return allRecipes.map((recipe) => buildPaletteRespectingSeed(recipe, featuresFromHex(hex), hex));
|
|
47244
|
+
function generateAllPalettes(hex$1, _labels) {
|
|
47245
|
+
return allRecipes.map((recipe) => buildPaletteRespectingSeed(recipe, featuresFromHex(hex$1), hex$1));
|
|
45387
47246
|
}
|
|
45388
47247
|
function paletteToRecord(p) {
|
|
45389
47248
|
const out = {
|
|
@@ -45400,10 +47259,10 @@ function paletteToRecord(p) {
|
|
|
45400
47259
|
}
|
|
45401
47260
|
return out;
|
|
45402
47261
|
}
|
|
45403
|
-
function ensureMinContrast(hex, bg, min = 2.5) {
|
|
45404
|
-
let cr = contrastRatio(hex, bg);
|
|
45405
|
-
if (cr >= min) return hex;
|
|
45406
|
-
const f = featuresFromHex(hex);
|
|
47262
|
+
function ensureMinContrast(hex$1, bg, min = 2.5) {
|
|
47263
|
+
let cr = contrastRatio(hex$1, bg);
|
|
47264
|
+
if (cr >= min) return hex$1;
|
|
47265
|
+
const f = featuresFromHex(hex$1);
|
|
45407
47266
|
const bgL = featuresFromHex(bg).okL;
|
|
45408
47267
|
let L = f.okL;
|
|
45409
47268
|
const dir = bgL >= .5 ? -1 : 1;
|
|
@@ -45413,10 +47272,10 @@ function ensureMinContrast(hex, bg, min = 2.5) {
|
|
|
45413
47272
|
const ncr = contrastRatio(cand, bg);
|
|
45414
47273
|
if (ncr > cr) {
|
|
45415
47274
|
cr = ncr;
|
|
45416
|
-
hex = cand;
|
|
47275
|
+
hex$1 = cand;
|
|
45417
47276
|
}
|
|
45418
47277
|
}
|
|
45419
|
-
return hex;
|
|
47278
|
+
return hex$1;
|
|
45420
47279
|
}
|
|
45421
47280
|
function blendOKLCH(a, b, alpha = .25) {
|
|
45422
47281
|
const A = featuresFromHex(a), B = featuresFromHex(b);
|
|
@@ -47038,13 +48897,13 @@ var require_jsx_runtime = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
47038
48897
|
//#endregion
|
|
47039
48898
|
//#region ../theme-core/src/mock-themes/components/swatch.tsx
|
|
47040
48899
|
var import_jsx_runtime = /* @__PURE__ */ __toESM(require_jsx_runtime());
|
|
47041
|
-
function Swatch({ name, hex }) {
|
|
48900
|
+
function Swatch({ name, hex: hex$1 }) {
|
|
47042
48901
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
47043
48902
|
className: "rb-rounded-xl rb-overflow-hidden rb-shadow-sm rb-border",
|
|
47044
48903
|
style: { borderColor: "rgba(0, 0, 0, 0.05)" },
|
|
47045
48904
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
47046
48905
|
className: "rb-h-12",
|
|
47047
|
-
style: { backgroundColor: hex }
|
|
48906
|
+
style: { backgroundColor: hex$1 }
|
|
47048
48907
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
47049
48908
|
className: "rb-px-3 rb-py-2 rb-text-xs rb-flex rb-items-center rb-justify-between",
|
|
47050
48909
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
@@ -47054,7 +48913,7 @@ function Swatch({ name, hex }) {
|
|
|
47054
48913
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
47055
48914
|
className: "rb-font-mono",
|
|
47056
48915
|
style: { color: "#71717a" },
|
|
47057
|
-
children: hex
|
|
48916
|
+
children: hex$1
|
|
47058
48917
|
})]
|
|
47059
48918
|
})]
|
|
47060
48919
|
});
|
|
@@ -47646,8 +49505,8 @@ const ordinaryEventDashboard = [{
|
|
|
47646
49505
|
surface: "Dashboard",
|
|
47647
49506
|
scope: "Events booked by date"
|
|
47648
49507
|
}];
|
|
47649
|
-
const meaning = (label, description) => ({
|
|
47650
|
-
label,
|
|
49508
|
+
const meaning = (label$1, description) => ({
|
|
49509
|
+
label: label$1,
|
|
47651
49510
|
description,
|
|
47652
49511
|
availability: ordinaryEventDashboard
|
|
47653
49512
|
});
|
|
@@ -51407,4 +53266,4 @@ function parseSdkEventRunUpsertInput(input, runIndex, options) {
|
|
|
51407
53266
|
}
|
|
51408
53267
|
|
|
51409
53268
|
//#endregion
|
|
51410
|
-
export { createFormRef as $,
|
|
53269
|
+
export { createFormRef as $, slugifyMediaIdentifier as $n, mapDrawingStyle as $r, asFooterLookId as $t, getSiteCommandMetadata as A, walkAndCollectImageAttrs as An, introCompositionTreatmentValues as Ar, generateHeaderScrollTreatmentStyles as At, eventBookingOfferIntentSchema as B, getImageUrl as Bn, serializeSurfaceTree as Br, getComponentBorderWidthPx as Bt, sortRecord as C, boxBackgroundOverlaySchema as Cn, defineBlockDesignCapability as Cr, deriveSiteStyleBlockPolicy as Ct, mapEventCommandContent as D, stripStaleTransitionStyleGroup as Dn, eyebrowTreatmentValues as Dr, getGradientPreset as Dt, parseAndPlanCommandBatchForTypes as E, siteChromeBackgroundOverrideOwner as En, eventListTreatmentValues as Er, gradientToCss as Et, pageIdentifierForBlockCommand as F, isExplicitRectCrop as Fn, toNonEmptyReadonlyArray as Fr, BREAKPOINT_MD as Ft, ianaTimezoneSchema as G, buildRenderableMediaAssetIndex as Gn, surfaceForegroundValue as Gr, themeButtonExtraClassesWithTrailingIcon as Gt, bookingPaymentTermsSchema as H, mediaSchema as Hn, surfaceForegroundDeclarations as Hr, ICON_CONFIG as Ht, pageIdentifierForCommand as I, parseAspectRatioValue as In, visibleHeadingDecorationFamilyValues as Ir, BREAKPOINT_SM as It, createCommandRef as J, mediaIdentifierSchema as Jn, backgroundColorStyle as Jr, parseThemeForPersistence as Jt, createBlockRef as K, resolveManagedMediaReference as Kn, surfaceInkDeclarations as Kr, themeButtonSizeClassName as Kt, pagePathForCommand as L, resolveMediaTransformForAspect as Ln, parseResolvedSurface as Lr, BREAKPOINT_XL as Lt, blockIdentifierForCommand as M, looksLikeUuid as Mn, offeringsPreviewCompositionTreatmentValues as Mr, getHeaderPaintTargets as Mt, blockOrderIdentifiersForCommand as N, rotateNormalizedPoint as Nn, testimonialsCompositionTreatmentValues as Nr, renderHeaderPaintTargetRules as Nt, evaluateSiteCommandBatchExposure as O, collectPortableMediaReferencesFromContent as On, featureCardCompositionTreatmentValues as Or, parseGradientPresetId as Ot, entryIdentifierForCommand as P, normalizeWidthCandidates as Pn, tileTreatmentValues as Pr, BREAKPOINT_LG as Pt, createFooterRef as Q, resolveMediaIdentifier as Qn, textColorStyle as Qr, normalizeCardRole as Qt, hasConfiguredBookingVertical as R, getDirectImageUrlForRelativePath as Rn, parseSurfaceTree as Rr, MQ_MD_DOWN as Rt, venueManagementFields as S, sectionForegroundBackground as Sn, assertNever$1 as Sr, resolvePublishedLook as St, createDryRunReport as T, runtimeBoxBackgroundSchema as Tn, defineNoBlockDesignCapability as Tr, compiledRuntimeStyleFieldNames as Tt, require_jsx_runtime as U, persistableMediaSchema as Un, surfaceForegroundInputFromResolved as Ur, generateDefaultButtonSystem as Ut, bookingPaymentCollectionOptionSchema as V, isSiteScopedDirectImageStoragePath as Vn, surfaceDecorationColors as Vr, getComponentShadowVar as Vt, require_react as W, runtimeMediaSchema as Wn, surfaceForegroundStyle as Wr, parseThemeButtonVariantId as Wt, createEventCategoryRef as X, parseCanonicalMediaIdentifier as Xn, mergeStyles as Xr, cardRoleSchema as Xt, createContentEntryRef as Y, buildMediaIdentifierFromFilename as Yn, borderColorStyle as Yr, renderCssDeclarations as Yt, createEventSeriesRef as Z, parseMediaIdentifier as Zn, rgbColorValue as Zr, cardVariantRoleValues as Zt, normalizeLinkValue as _, __toESM as _i, deriveSectionGround as _n, asAppearancePresetId as _r, resolveLookSlotSurfaces as _t, classifySdkEventSchedule as a, PUBLIC_BUTTON_ROLES as ai, buttonStyleSelectionSchema as an, normalizeAmbiguousStoragePathForSite as ar, slugPart as at, isEventVisibility as b, resolveSectionBackground as bn, asPageFlowPresetId as br, resolveEffectiveArrangement as bt, customLinkSchema as c, resolveSurfaceForeground as ci, generateNavLinkCSS as cn, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES as cr, headerLeafTextColor as ct, internalLinkSchema as d, defaultThemeTokens as di, footerPreferencesSchema as dn, parseSafeDownloadFilename as dr, headerPreferencesSchema as dt, opaqueSurfaceDeclarations as ei, footerLookIdValues as en, deriveVideoSlotPolicy as er, createNavigationRef as et, internalResolvedLinkSchema as f, expandPalette as fi, headerNeedsPhotoShade as fn, defineBlockDesignMetadata as fr, mergeHeaderNavTypographyPreference as ft, linkSchema as g, __commonJSMin as gi, sectionFactsForContent as gn, arrangementModes as gr, resolveItemSurface as gt, isResolvedInternalLinkEntityType as h, parseBlockColorToken as hi, composeBoundary as hn, cardListKnobBlocks as hr, arrangementSlotNames as ht, sdkEventUpsertOptionalFieldSchemas as i, resolveTokenOrCssColorValue as ii, headerStyleIdValues as in, asRecord as ir, refSlug as it, eventManagementTiming as j, deleteEnvSpecificKeys as jn, makeBoundaryId as jr, generateMobileOverlayBackdropFilterFix as jt, evaluateSiteCommandExposure as k, summarizePortableMediaReferences as kn, headingDecorationColorTokenValues as kr, themeSchema as kt, entryLinkSchema as l, surfaceInkPublication as li, pageDesignBoundaryTreatmentSchema as ln, classifyKnownMimeType as lr, transparentHeaderForegroundInput as lt, isInternalResolvedLinkValue as m, parseThemeColorToken as mi, boundaryLayout as mn, cardListFamilyBlocks as mr, arrangementLookHooks as mt, sdkCourseBookingSettingsSchema as n, resolveOnColorForThemeToken as ni, asHeaderStyleId as nn, getExtensionForMimeType as nr, createSiteCommandSiteRef as nt, classifySdkEventBookingOffer as o, selectLogoVariant as oi, footerForegroundCandidates as on, parseRelativeStoragePath as or, effectiveHeaderNavLinkStyle as ot, internalRouteOnlyLinkSchema as p, getTextColorForBackground as pi, headerRootPaint as pn, resolveEffectiveKnobs as pr, readHeaderNavFontSizePreference as pt, createBlocksRef as q, mediaTypeDiscriminants as qn, surfaceInkStyle as qr, themeButtonVariantClassNames as qt, sdkEventOccurrenceInputSchema as r, resolveThemeColorReferenceValue as ri, headerLayoutIdValues as rn, getMimeType as rr, createVenueRef as rt, RESOLVED_INTERNAL_LINK_ENTITY_TYPES as s, surfaceToneFromForegroundToken as si, buildTextContrastShadow as sn, FILE_SIZE_LIMITS as sr, headerForegroundCandidates as st, classifySdkEventTiming as t, isCssColorLiteral as ti, asHeaderLayoutId as tn, resolveVideoSelectionIntentForMediaField as tr, createPageRef as tt, externalLinkSchema as u, TokenResolver as ui, pageDesignIntentSchemaVersion as un, managementMediaLibraryUploadLimitForMimeType as ur, readOpeningForegroundProjection as ut, pageLinkSchema as v, heroPhotoPaintsGround as vn, asDesignBlockId as vr, arrangementManifestFromVariants as vt, stableStringify as w, boxBackgroundSchema as wn, defineFrameCapability as wr, requireCuratedSiteStyle as wt, resolvePageUpsertSeo as x, resolveSectionPhotoPlacement as xn, asSiteStyleId as xr, resolveModeArrangement as xt, eventVisibilitySchema as y, parseCanonicalRuntimeBackground as yn, asLayoutVariantKey as yr, readStoredMode as yt, eventCommandScheduleSchema as z, getDirectImageUrlForSiteScopedPath as zn, resolveSurface as zr, headerVariantValues as zt };
|