riverbankcms 0.7.0 → 0.8.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 -28
- package/dist/cli/index.mjs +46682 -42591
- package/dist/cli/init-docs/content/cli-reference.md +0 -11
- package/dist/cli/init-docs/content/getting-started.md +0 -1
- package/dist/cli/init-docs/content/workflow-isr-revalidation.md +1 -1
- package/dist/{eventUpsertInputSchema-CWglzaib.mjs → eventUpsertInputSchema-BE3nSNsn.mjs} +2 -2
- package/dist/load-config-Ca13nCpT.mjs +4 -0
- package/dist/{sdk-peer-BjTmH5Mt.mjs → load-config-De0cFlPg.mjs} +89 -6
- package/dist/{style-D2srLsI5.mjs → local-sync-EcUwLrRx.mjs} +5453 -9856
- package/dist/{plan-DgjlCWxa.mjs → plan-DtQEnwCM.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-CS9tY-KW.mjs → sdk-event-upsert-Bf6wAl4s.mjs} +926 -631
- package/dist/{src-DT7DBZtQ.mjs → src-B8fOSKJ6.mjs} +194 -84
- package/dist/testing.mjs +4 -5
- package/package.json +1 -1
- package/dist/load-config-CZ9qKC9m.mjs +0 -5
- package/dist/load-config-DBGNYfxV.mjs +0 -87
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as parseFooterCompositionV1, Ht as ok, K as parseUtcIsoDateTimeString, M as isCanonicalRichTextDoc, P as isPresent, Vt as err, X as parseBookingTimeZone, b as stableJsonStringify, k as footerNavigationLayouts, l as EVENT_PRESET_VALUES, lt as assertNever, s as resolveSiteVerticalBookingConfig, t as NAVIGATION_LOCATIONS, u as ORDINARY_EVENT_PRESET_VALUES } from "./src-B8fOSKJ6.mjs";
|
|
3
3
|
import "node:module";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -869,6 +869,66 @@ const buttonSystemSchema = z.strictObject({
|
|
|
869
869
|
});
|
|
870
870
|
});
|
|
871
871
|
|
|
872
|
+
//#endregion
|
|
873
|
+
//#region ../theme-core/src/footer/mode.ts
|
|
874
|
+
const footerModeSchema = z.enum([
|
|
875
|
+
"default",
|
|
876
|
+
"blocks",
|
|
877
|
+
"default+blocks",
|
|
878
|
+
"none"
|
|
879
|
+
]);
|
|
880
|
+
/** Reads only SDK footer intent; unrelated config and theme fields are not authorities. */
|
|
881
|
+
function resolveSdkFooterMode(config) {
|
|
882
|
+
const parsed = z.object({ layout: z.object({ footerMode: footerModeSchema.optional() }).optional() }).safeParse(config);
|
|
883
|
+
return parsed.success ? parsed.data.layout?.footerMode ?? "default" : "default";
|
|
884
|
+
}
|
|
885
|
+
/** Mirrors the incumbent footer renderer's variant-specific identity rules. */
|
|
886
|
+
function resolveFooterBrandVisibility(footer) {
|
|
887
|
+
return {
|
|
888
|
+
showLogo: footer.variant === "simple" ? footer.logo?.showLogo === true : footer.logo?.showLogo !== false,
|
|
889
|
+
showTitle: footer.logo?.showLogoText ?? footer.showLogoText === true
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Resolve what to render for the footer given the current mode and whether the
|
|
894
|
+
* site is SDK-managed.
|
|
895
|
+
*
|
|
896
|
+
* Backwards compatible: missing/undefined mode is treated as 'default'.
|
|
897
|
+
*/
|
|
898
|
+
function resolveFooterRenderPlan(params) {
|
|
899
|
+
if (!params.isSdkSite) {
|
|
900
|
+
return {
|
|
901
|
+
mode: "default",
|
|
902
|
+
renderDefaultFooter: true,
|
|
903
|
+
renderFooterBlocks: false
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
const mode = params.mode ?? "default";
|
|
907
|
+
switch (mode) {
|
|
908
|
+
case "blocks": return {
|
|
909
|
+
mode: "blocks",
|
|
910
|
+
renderDefaultFooter: false,
|
|
911
|
+
renderFooterBlocks: true
|
|
912
|
+
};
|
|
913
|
+
case "default+blocks": return {
|
|
914
|
+
mode: "default+blocks",
|
|
915
|
+
renderDefaultFooter: true,
|
|
916
|
+
renderFooterBlocks: true
|
|
917
|
+
};
|
|
918
|
+
case "none": return {
|
|
919
|
+
mode: "none",
|
|
920
|
+
renderDefaultFooter: false,
|
|
921
|
+
renderFooterBlocks: false
|
|
922
|
+
};
|
|
923
|
+
case "default":
|
|
924
|
+
default: return {
|
|
925
|
+
mode: "default",
|
|
926
|
+
renderDefaultFooter: true,
|
|
927
|
+
renderFooterBlocks: false
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
872
932
|
//#endregion
|
|
873
933
|
//#region ../theme-core/src/lib/media.ts
|
|
874
934
|
const mediaTypeDiscriminants = [
|
|
@@ -1805,90 +1865,6 @@ function resolveCardRole(theme, cardSystem, role) {
|
|
|
1805
1865
|
}
|
|
1806
1866
|
}
|
|
1807
1867
|
|
|
1808
|
-
//#endregion
|
|
1809
|
-
//#region ../theme-core/src/tokens/resolver.ts
|
|
1810
|
-
/**
|
|
1811
|
-
* Resolves theme tokens to CSS variables
|
|
1812
|
-
*
|
|
1813
|
-
* This resolver ensures all button styles use theme tokens (not hardcoded colors)
|
|
1814
|
-
* so that changing the palette automatically updates all buttons.
|
|
1815
|
-
*/
|
|
1816
|
-
var TokenResolver = class {
|
|
1817
|
-
constructor(theme) {
|
|
1818
|
-
this.theme = theme;
|
|
1819
|
-
}
|
|
1820
|
-
/**
|
|
1821
|
-
* Resolve a color token to a CSS variable reference wrapped in rgb()
|
|
1822
|
-
*
|
|
1823
|
-
* @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
|
|
1824
|
-
* @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
|
|
1825
|
-
*
|
|
1826
|
-
* Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
|
|
1827
|
-
* so we must wrap them with rgb() to create a valid color value.
|
|
1828
|
-
*
|
|
1829
|
-
* @example
|
|
1830
|
-
* resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
|
|
1831
|
-
* resolver.getColor('white') // => 'rgb(var(--tb-white))'
|
|
1832
|
-
*/
|
|
1833
|
-
getColor(token) {
|
|
1834
|
-
if (!this.tokenExists(token)) {
|
|
1835
|
-
console.warn(`[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`);
|
|
1836
|
-
}
|
|
1837
|
-
return `rgb(var(--tb-${token}))`;
|
|
1838
|
-
}
|
|
1839
|
-
/**
|
|
1840
|
-
* Adjust a color token's shade by a given amount
|
|
1841
|
-
*
|
|
1842
|
-
* @param token - Base token (e.g., 'primary-500')
|
|
1843
|
-
* @param amount - Shade adjustment (+100 = darker, -100 = lighter)
|
|
1844
|
-
* @returns Adjusted token name (e.g., 'primary-600')
|
|
1845
|
-
*
|
|
1846
|
-
* @example
|
|
1847
|
-
* resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
|
|
1848
|
-
* resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
|
|
1849
|
-
*/
|
|
1850
|
-
adjustShade(token, amount) {
|
|
1851
|
-
const parts = token.split("-");
|
|
1852
|
-
if (parts.length < 2) {
|
|
1853
|
-
const newShade$1 = Math.max(100, Math.min(900, 500 + amount));
|
|
1854
|
-
return `${token}-${newShade$1}`;
|
|
1855
|
-
}
|
|
1856
|
-
const base = parts.slice(0, -1).join("-");
|
|
1857
|
-
const shade = parseInt(parts[parts.length - 1] ?? "", 10);
|
|
1858
|
-
if (isNaN(shade)) {
|
|
1859
|
-
console.warn(`[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`);
|
|
1860
|
-
return token;
|
|
1861
|
-
}
|
|
1862
|
-
const newShade = Math.max(100, Math.min(900, shade + amount));
|
|
1863
|
-
return `${base}-${newShade}`;
|
|
1864
|
-
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Check if a token exists in the theme palette
|
|
1867
|
-
*
|
|
1868
|
-
* @param token - Token name to check
|
|
1869
|
-
* @returns True if token exists in theme
|
|
1870
|
-
*/
|
|
1871
|
-
tokenExists(token) {
|
|
1872
|
-
const commonTokens = [
|
|
1873
|
-
"white",
|
|
1874
|
-
"black",
|
|
1875
|
-
"transparent"
|
|
1876
|
-
];
|
|
1877
|
-
if (commonTokens.includes(token)) {
|
|
1878
|
-
return true;
|
|
1879
|
-
}
|
|
1880
|
-
const parts = token.split("-");
|
|
1881
|
-
if (parts.length >= 2) {
|
|
1882
|
-
const lastPart = parts[parts.length - 1] ?? "";
|
|
1883
|
-
const shade = parseInt(lastPart, 10);
|
|
1884
|
-
if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
|
|
1885
|
-
return true;
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
return true;
|
|
1889
|
-
}
|
|
1890
|
-
};
|
|
1891
|
-
|
|
1892
1868
|
//#endregion
|
|
1893
1869
|
//#region ../theme-core/src/surfaces/foregroundRoles.ts
|
|
1894
1870
|
const SURFACE_FOREGROUND_ROLES = [
|
|
@@ -2162,135 +2138,431 @@ function raiseContrastText(bgHex, preferred) {
|
|
|
2162
2138
|
}
|
|
2163
2139
|
|
|
2164
2140
|
//#endregion
|
|
2165
|
-
//#region ../theme-core/src/
|
|
2166
|
-
const
|
|
2167
|
-
"
|
|
2168
|
-
"
|
|
2169
|
-
"
|
|
2170
|
-
"
|
|
2171
|
-
"
|
|
2172
|
-
"
|
|
2173
|
-
"
|
|
2174
|
-
"
|
|
2175
|
-
"
|
|
2176
|
-
"
|
|
2177
|
-
"muted",
|
|
2178
|
-
"neutral"
|
|
2179
|
-
]);
|
|
2180
|
-
function onColorTokenName(backgroundToken) {
|
|
2181
|
-
return `on${backgroundToken.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
|
|
2182
|
-
}
|
|
2183
|
-
function isOnColorBackgroundCandidateToken(token) {
|
|
2184
|
-
const role = token.match(/^(.*?)(?:-\d{2,3})?$/)?.[1] ?? token;
|
|
2185
|
-
return BACKGROUND_ROLE_NAMES.has(role);
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
//#endregion
|
|
2189
|
-
//#region ../theme-core/src/palette/utils/opaqueSurfaceColor.ts
|
|
2190
|
-
/** Resolve an opaque colour, compositing alpha against an explicit underlay. */
|
|
2191
|
-
function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
|
|
2192
|
-
const opaque = resolveConcreteColorHex(reference, palette);
|
|
2193
|
-
if (opaque) return opaque;
|
|
2194
|
-
if (!underlayReference) return null;
|
|
2195
|
-
const underlayHex = resolveConcreteColorHex(underlayReference, palette);
|
|
2196
|
-
if (!underlayHex) return null;
|
|
2197
|
-
const parts = parseColorTokenParts(reference);
|
|
2198
|
-
if (parts.kind === "token" && parts.opacityPercent !== null) {
|
|
2199
|
-
const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
|
|
2200
|
-
return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
|
|
2201
|
-
}
|
|
2202
|
-
const raw = parts.kind === "raw" ? parts.value : reference;
|
|
2203
|
-
const rgba = parseRgba(raw);
|
|
2204
|
-
return rgba ? compositeHex(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
|
|
2205
|
-
}
|
|
2206
|
-
/** Resolve a colour reference only when it is already fully opaque. */
|
|
2207
|
-
function resolveConcreteColorHex(reference, palette) {
|
|
2208
|
-
const literalHex = normalizePaletteHex(reference);
|
|
2209
|
-
if (literalHex) return literalHex;
|
|
2210
|
-
const parts = parseColorTokenParts(reference);
|
|
2211
|
-
if (parts.kind === "token") {
|
|
2212
|
-
if (parts.opacityPercent !== null) return null;
|
|
2213
|
-
return normalizePaletteHex(palette[parts.token] ?? "");
|
|
2214
|
-
}
|
|
2215
|
-
const literal = parts.kind === "raw" ? parts.value : reference;
|
|
2216
|
-
if (literal.toLowerCase() === "black") return "#000000";
|
|
2217
|
-
if (literal.toLowerCase() === "white") return "#ffffff";
|
|
2218
|
-
return normalizePaletteHex(literal);
|
|
2219
|
-
}
|
|
2220
|
-
function compositeHex(foregroundHex, underlayHex, alpha) {
|
|
2221
|
-
const foreground = hexToRgb(foregroundHex);
|
|
2222
|
-
const underlay = hexToRgb(underlayHex);
|
|
2223
|
-
const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
|
|
2224
|
-
return rgbToHex(channel(foreground.r, underlay.r), channel(foreground.g, underlay.g), channel(foreground.b, underlay.b));
|
|
2225
|
-
}
|
|
2226
|
-
function parseRgba(value) {
|
|
2227
|
-
const match = value.trim().match(/^rgba?\(\s*([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*(?:,|\/)\s*([\d.]+%?))?\s*\)$/i);
|
|
2228
|
-
if (!match) return null;
|
|
2229
|
-
const [, rSource, gSource, bSource] = match;
|
|
2230
|
-
if (!rSource || !gSource || !bSource) return null;
|
|
2231
|
-
const r = Number(rSource);
|
|
2232
|
-
const g = Number(gSource);
|
|
2233
|
-
const b = Number(bSource);
|
|
2234
|
-
const alpha = parseAlpha(match[4]);
|
|
2235
|
-
if (alpha === null || !isRgbChannel(r) || !isRgbChannel(g) || !isRgbChannel(b)) {
|
|
2236
|
-
return null;
|
|
2237
|
-
}
|
|
2238
|
-
return {
|
|
2239
|
-
r,
|
|
2240
|
-
g,
|
|
2241
|
-
b,
|
|
2242
|
-
alpha
|
|
2243
|
-
};
|
|
2244
|
-
}
|
|
2245
|
-
function parseAlpha(source) {
|
|
2246
|
-
if (!source) return 1;
|
|
2247
|
-
const value = source.endsWith("%") ? Number(source.slice(0, -1)) / 100 : Number(source);
|
|
2248
|
-
return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
|
|
2249
|
-
}
|
|
2250
|
-
function isRgbChannel(value) {
|
|
2251
|
-
return Number.isFinite(value) && value >= 0 && value <= 255;
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
//#endregion
|
|
2255
|
-
//#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
|
|
2256
|
-
const FALLBACK_TEXT_TOKENS = [
|
|
2257
|
-
"text",
|
|
2258
|
-
"primaryForeground",
|
|
2259
|
-
"secondaryForeground",
|
|
2260
|
-
"accentForeground",
|
|
2261
|
-
"neutral-950",
|
|
2262
|
-
"neutral-50"
|
|
2141
|
+
//#region ../theme-core/src/tokens/tokens.ts
|
|
2142
|
+
const colorTokens = [
|
|
2143
|
+
"background/base",
|
|
2144
|
+
"background/elevated",
|
|
2145
|
+
"surface/base",
|
|
2146
|
+
"surface/subtle",
|
|
2147
|
+
"surface/inverted",
|
|
2148
|
+
"content/primary",
|
|
2149
|
+
"content/secondary",
|
|
2150
|
+
"accent/primary",
|
|
2151
|
+
"accent/secondary",
|
|
2152
|
+
"border/base"
|
|
2263
2153
|
];
|
|
2264
|
-
const
|
|
2265
|
-
"
|
|
2266
|
-
"
|
|
2267
|
-
"
|
|
2268
|
-
"
|
|
2269
|
-
"
|
|
2270
|
-
"
|
|
2271
|
-
"
|
|
2272
|
-
"
|
|
2273
|
-
"neutral-950",
|
|
2274
|
-
"neutral-50"
|
|
2154
|
+
const typographyTokens = [
|
|
2155
|
+
"display/hero",
|
|
2156
|
+
"display/section",
|
|
2157
|
+
"heading/large",
|
|
2158
|
+
"heading/medium",
|
|
2159
|
+
"heading/small",
|
|
2160
|
+
"body/normal",
|
|
2161
|
+
"body/small",
|
|
2162
|
+
"caption"
|
|
2275
2163
|
];
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2164
|
+
const spacingTokens = [
|
|
2165
|
+
"none",
|
|
2166
|
+
"3xs",
|
|
2167
|
+
"2xs",
|
|
2168
|
+
"xs",
|
|
2169
|
+
"sm",
|
|
2170
|
+
"md",
|
|
2171
|
+
"lg",
|
|
2172
|
+
"xl",
|
|
2173
|
+
"2xl",
|
|
2174
|
+
"3xl"
|
|
2175
|
+
];
|
|
2176
|
+
const radiusTokens = [
|
|
2177
|
+
"none",
|
|
2178
|
+
"xs",
|
|
2179
|
+
"sm",
|
|
2180
|
+
"md",
|
|
2181
|
+
"lg",
|
|
2182
|
+
"pill",
|
|
2183
|
+
"full"
|
|
2184
|
+
];
|
|
2185
|
+
const shadowTokens = [
|
|
2186
|
+
"none",
|
|
2187
|
+
"soft",
|
|
2188
|
+
"medium",
|
|
2189
|
+
"strong"
|
|
2190
|
+
];
|
|
2191
|
+
const defaultThemeTokens = {
|
|
2192
|
+
color: {
|
|
2193
|
+
"background/base": "#ffffff",
|
|
2194
|
+
"background/elevated": "#f5f5f5",
|
|
2195
|
+
"surface/base": "#ffffff",
|
|
2196
|
+
"surface/subtle": "#f9f9fb",
|
|
2197
|
+
"surface/inverted": "#111111",
|
|
2198
|
+
"content/primary": "#111111",
|
|
2199
|
+
"content/secondary": "#3f3f46",
|
|
2200
|
+
"accent/primary": "#2563eb",
|
|
2201
|
+
"accent/secondary": "#1d4ed8",
|
|
2202
|
+
"border/base": "#e5e7eb"
|
|
2203
|
+
},
|
|
2204
|
+
typography: {
|
|
2205
|
+
"display/hero": {
|
|
2206
|
+
fontSize: "clamp(48px, 8vw, 96px)",
|
|
2207
|
+
lineHeight: "1.05",
|
|
2208
|
+
fontWeight: "700"
|
|
2209
|
+
},
|
|
2210
|
+
"display/section": {
|
|
2211
|
+
fontSize: "clamp(40px, 6vw, 72px)",
|
|
2212
|
+
lineHeight: "1.1",
|
|
2213
|
+
fontWeight: "700"
|
|
2214
|
+
},
|
|
2215
|
+
"heading/large": {
|
|
2216
|
+
fontSize: "clamp(32px, 4vw, 48px)",
|
|
2217
|
+
lineHeight: "1.2",
|
|
2218
|
+
fontWeight: "600"
|
|
2219
|
+
},
|
|
2220
|
+
"heading/medium": {
|
|
2221
|
+
fontSize: "clamp(24px, 3vw, 32px)",
|
|
2222
|
+
lineHeight: "1.25",
|
|
2223
|
+
fontWeight: "600"
|
|
2224
|
+
},
|
|
2225
|
+
"heading/small": {
|
|
2226
|
+
fontSize: "20px",
|
|
2227
|
+
lineHeight: "1.3",
|
|
2228
|
+
fontWeight: "600"
|
|
2229
|
+
},
|
|
2230
|
+
"body/normal": {
|
|
2231
|
+
fontSize: "16px",
|
|
2232
|
+
lineHeight: "1.6",
|
|
2233
|
+
fontWeight: "400"
|
|
2234
|
+
},
|
|
2235
|
+
"body/small": {
|
|
2236
|
+
fontSize: "14px",
|
|
2237
|
+
lineHeight: "1.5",
|
|
2238
|
+
fontWeight: "400"
|
|
2239
|
+
},
|
|
2240
|
+
caption: {
|
|
2241
|
+
fontSize: "12px",
|
|
2242
|
+
lineHeight: "1.4",
|
|
2243
|
+
fontWeight: "500"
|
|
2244
|
+
}
|
|
2245
|
+
},
|
|
2246
|
+
spacing: {
|
|
2247
|
+
none: "0px",
|
|
2248
|
+
"3xs": "4px",
|
|
2249
|
+
"2xs": "8px",
|
|
2250
|
+
xs: "12px",
|
|
2251
|
+
sm: "16px",
|
|
2252
|
+
md: "24px",
|
|
2253
|
+
lg: "32px",
|
|
2254
|
+
xl: "40px",
|
|
2255
|
+
"2xl": "56px",
|
|
2256
|
+
"3xl": "72px"
|
|
2257
|
+
},
|
|
2258
|
+
radius: {
|
|
2259
|
+
none: "0px",
|
|
2260
|
+
xs: "2px",
|
|
2261
|
+
sm: "4px",
|
|
2262
|
+
md: "8px",
|
|
2263
|
+
lg: "12px",
|
|
2264
|
+
pill: "999px",
|
|
2265
|
+
full: "9999px"
|
|
2266
|
+
},
|
|
2267
|
+
shadow: {
|
|
2268
|
+
none: "none",
|
|
2269
|
+
soft: "0px 2px 8px rgba(15, 23, 42, 0.08)",
|
|
2270
|
+
medium: "0px 6px 18px rgba(15, 23, 42, 0.12)",
|
|
2271
|
+
strong: "0px 12px 36px rgba(15, 23, 42, 0.16)"
|
|
2272
|
+
},
|
|
2273
|
+
palette: {
|
|
2274
|
+
surface: "#ffffff",
|
|
2275
|
+
"neutral-50": "#f9fafb",
|
|
2276
|
+
"neutral-900": "#111827",
|
|
2277
|
+
primary: "#2563eb",
|
|
2278
|
+
"primary-50": "#eff6ff",
|
|
2279
|
+
"primary-500": "#2563eb",
|
|
2280
|
+
"primary-900": "#1d4ed8",
|
|
2281
|
+
secondary: "#9333ea",
|
|
2282
|
+
"secondary-50": "#f3e8ff",
|
|
2283
|
+
"secondary-500": "#9333ea",
|
|
2284
|
+
"secondary-900": "#4c1d95",
|
|
2285
|
+
accent: "#f97316",
|
|
2286
|
+
"accent-50": "#fff7ed",
|
|
2287
|
+
"accent-500": "#f97316",
|
|
2288
|
+
"accent-900": "#7c2d12"
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
/**
|
|
2292
|
+
* Project the expanded semantic palette into the token buckets consumed by
|
|
2293
|
+
* runtime transforms. Keep this mapping shared by full themes and the
|
|
2294
|
+
* tokens-only hosted-booking runtime so the two paths cannot drift.
|
|
2295
|
+
*/
|
|
2296
|
+
function buildThemeTokens(palette) {
|
|
2297
|
+
const tokens = cloneThemeTokens(defaultThemeTokens);
|
|
2298
|
+
const paletteStrings = {};
|
|
2299
|
+
for (const [key, value] of Object.entries(palette)) {
|
|
2300
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
2301
|
+
paletteStrings[key] = value;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
tokens.palette = paletteStrings;
|
|
2305
|
+
const resolveColor = (...keys) => {
|
|
2306
|
+
for (const key of keys) {
|
|
2307
|
+
const value = paletteStrings[key];
|
|
2308
|
+
if (value) return value;
|
|
2309
|
+
}
|
|
2310
|
+
return undefined;
|
|
2311
|
+
};
|
|
2312
|
+
const assignColor = (token, ...keys) => {
|
|
2313
|
+
const value = resolveColor(...keys);
|
|
2314
|
+
if (value) tokens.color[token] = value;
|
|
2315
|
+
};
|
|
2316
|
+
assignColor("background/base", "background", "surface", "primary-50");
|
|
2317
|
+
assignColor("background/elevated", "surface", "background");
|
|
2318
|
+
assignColor("surface/base", "surface", "background");
|
|
2319
|
+
assignColor("surface/subtle", "neutral-100", "surface", "background");
|
|
2320
|
+
assignColor("surface/inverted", "neutral-900", "text", "primary-900");
|
|
2321
|
+
assignColor("content/primary", "text", "body", "neutral-900");
|
|
2322
|
+
assignColor("content/secondary", "mutedText", "secondary", "neutral-600");
|
|
2323
|
+
assignColor("accent/primary", "primary", "accent");
|
|
2324
|
+
assignColor("accent/secondary", "accent", "secondary", "primary-400");
|
|
2325
|
+
assignColor("border/base", "border", "neutral-300");
|
|
2326
|
+
return tokens;
|
|
2327
|
+
}
|
|
2328
|
+
function cloneThemeTokens(source) {
|
|
2329
|
+
return JSON.parse(JSON.stringify(source));
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
//#endregion
|
|
2333
|
+
//#region ../theme-core/src/surfaces/foregroundInteraction.ts
|
|
2334
|
+
/**
|
|
2335
|
+
* How far a hover state softens away from its resting foreground, as a
|
|
2336
|
+
* percentage of full opacity.
|
|
2337
|
+
*
|
|
2338
|
+
* Softening — rather than brightening — is forced by the token vocabulary
|
|
2339
|
+
* rather than chosen for taste. A resolved foreground is frequently an
|
|
2340
|
+
* `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
|
|
2341
|
+
* with no shade ramp: over a primary-painted section the resolved text role
|
|
2342
|
+
* is `onPrimary`, and white has no headroom left to move further from its
|
|
2343
|
+
* background. Moving *toward* the mid-range always has headroom, whatever
|
|
2344
|
+
* the resting colour, and it keeps the hue exactly.
|
|
2345
|
+
*
|
|
2346
|
+
* 85% is the measured floor, not a round number. Softening costs contrast, and
|
|
2347
|
+
* the binding case is a scrolled overlay header: `contrast-glass` paints 60%
|
|
2348
|
+
* black or white, which composites the backdrop into a known band whatever the
|
|
2349
|
+
* page content underneath happens to be. The lightest backdrop a dark glass can
|
|
2350
|
+
* present is 60% black over white, `#666666`; a white foreground softened to
|
|
2351
|
+
* 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
|
|
2352
|
+
* 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
|
|
2353
|
+
* the large-text 3:1.
|
|
2354
|
+
*
|
|
2355
|
+
* The mirror case — a light glass over dark content, `#999999` — cannot be met
|
|
2356
|
+
* by any softening, because the *resting* colour is already only about 4.6:1
|
|
2357
|
+
* there. That is a property of the glass rather than of this derivation, and
|
|
2358
|
+
* it wants fixing where the glass tone is chosen.
|
|
2359
|
+
*/
|
|
2360
|
+
const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
|
|
2361
|
+
/**
|
|
2362
|
+
* Derive the hover foreground that belongs with a resolved resting foreground.
|
|
2363
|
+
*
|
|
2364
|
+
* A resting foreground is chosen to be legible against a known surface. A hover
|
|
2365
|
+
* state derived from it stays within a bounded distance of that colour, so it
|
|
2366
|
+
* inherits most of the resting colour's contrast margin instead of asserting
|
|
2367
|
+
* an unrelated brand colour that no one checked against the surface.
|
|
2368
|
+
*
|
|
2369
|
+
* That bounded-distance property is what lets this be used on a transparent
|
|
2370
|
+
* overlay header, where the backdrop is only partly knowable: the scrolled
|
|
2371
|
+
* state composites a 60% glass tint over arbitrary page content, so no
|
|
2372
|
+
* compile-time contrast proof is available there. Staying near a colour that
|
|
2373
|
+
* is already trusted in both states is a weaker claim than a contrast proof,
|
|
2374
|
+
* and it is one this function can actually keep.
|
|
2375
|
+
*
|
|
2376
|
+
* Returns `null` when the resting token is not a plain palette token — an
|
|
2377
|
+
* already-faded or raw value has no well-defined softening, and the caller
|
|
2378
|
+
* should leave hover alone rather than guess.
|
|
2379
|
+
*/
|
|
2380
|
+
function deriveForegroundHoverToken(restingToken) {
|
|
2381
|
+
const parts = parseColorTokenParts(restingToken);
|
|
2382
|
+
if (parts.kind !== "token") return null;
|
|
2383
|
+
if (parts.opacityPercent !== null) return null;
|
|
2384
|
+
return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* The opacity `contrast-glass` paints at. Mirrors the value in
|
|
2388
|
+
* `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
|
|
2389
|
+
* point of the bound below is that it describes what that treatment can produce.
|
|
2390
|
+
*/
|
|
2391
|
+
const CONTRAST_GLASS_ALPHA = .6;
|
|
2392
|
+
/**
|
|
2393
|
+
* The extreme backdrop a glass tone can present, whatever page content happens
|
|
2394
|
+
* to be underneath it.
|
|
2395
|
+
*
|
|
2396
|
+
* A 60% tint clamps the composite into a known band, which is what makes a
|
|
2397
|
+
* conservative check possible at compile time even though the content passing
|
|
2398
|
+
* under a sticky header is not knowable. For a dark glass the composite is
|
|
2399
|
+
* lightest over white; for a light glass it is darkest over black. Those are
|
|
2400
|
+
* the two ends that a foreground has to survive.
|
|
2401
|
+
*/
|
|
2402
|
+
function worstCaseGlassBackdrop(tone) {
|
|
2403
|
+
const [over, tint] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
|
|
2404
|
+
return compositeHex$1(tint, over, CONTRAST_GLASS_ALPHA);
|
|
2405
|
+
}
|
|
2406
|
+
/** Flatten `foreground` at `alpha` over `background` into an opaque hex. */
|
|
2407
|
+
function compositeHex$1(foreground, background, alpha) {
|
|
2408
|
+
const f = hexToRgb(foreground);
|
|
2409
|
+
const b = hexToRgb(background);
|
|
2410
|
+
return rgbToHex(Math.round(mix$2(b.r, f.r, alpha)), Math.round(mix$2(b.g, f.g, alpha)), Math.round(mix$2(b.b, f.b, alpha)));
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Decide whether the resting foreground can safely be softened for hover.
|
|
2414
|
+
*
|
|
2415
|
+
* `glassTone` is the scroll treatment's compiled tone, or `null` when the style
|
|
2416
|
+
* has no glass. Without one there is no bound on what the header can end up
|
|
2417
|
+
* over, so this declines to soften rather than guess.
|
|
2418
|
+
*
|
|
2419
|
+
* The light-glass end cannot be satisfied by any softening: against `#999999`
|
|
2420
|
+
* a dark resting foreground is already close to the 4.5:1 line, so every step
|
|
2421
|
+
* away from it fails. That is why this returns a plan rather than a colour —
|
|
2422
|
+
* the caller has to have a non-colour affordance to fall back on.
|
|
2423
|
+
*/
|
|
2424
|
+
function planForegroundHover(restingToken, restingHex, glassTone) {
|
|
2425
|
+
const softened = deriveForegroundHoverToken(restingToken);
|
|
2426
|
+
if (!softened) return { kind: "resting" };
|
|
2427
|
+
if (!restingHex || !glassTone) return { kind: "resting" };
|
|
2428
|
+
const backdrop = worstCaseGlassBackdrop(glassTone);
|
|
2429
|
+
const softenedHex = compositeHex$1(restingHex, backdrop, FOREGROUND_HOVER_OPACITY_PERCENT / 100);
|
|
2430
|
+
return contrastRatio(softenedHex, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO ? {
|
|
2431
|
+
kind: "softened",
|
|
2432
|
+
token: softened
|
|
2433
|
+
} : { kind: "resting" };
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
//#endregion
|
|
2437
|
+
//#region ../theme-core/src/palette/utils/onColor.ts
|
|
2438
|
+
const BACKGROUND_ROLE_NAMES = new Set([
|
|
2439
|
+
"primary",
|
|
2440
|
+
"secondary",
|
|
2441
|
+
"accent",
|
|
2442
|
+
"success",
|
|
2443
|
+
"warning",
|
|
2444
|
+
"destructive",
|
|
2445
|
+
"info",
|
|
2446
|
+
"background",
|
|
2447
|
+
"surface",
|
|
2448
|
+
"surfaceAlt",
|
|
2449
|
+
"muted",
|
|
2450
|
+
"neutral"
|
|
2451
|
+
]);
|
|
2452
|
+
function onColorTokenName(backgroundToken) {
|
|
2453
|
+
return `on${backgroundToken.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
|
|
2454
|
+
}
|
|
2455
|
+
function isOnColorBackgroundCandidateToken(token) {
|
|
2456
|
+
const role = token.match(/^(.*?)(?:-\d{2,3})?$/)?.[1] ?? token;
|
|
2457
|
+
return BACKGROUND_ROLE_NAMES.has(role);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
//#endregion
|
|
2461
|
+
//#region ../theme-core/src/palette/utils/opaqueSurfaceColor.ts
|
|
2462
|
+
/** Resolve an opaque colour, compositing alpha against an explicit underlay. */
|
|
2463
|
+
function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
|
|
2464
|
+
const opaque = resolveConcreteColorHex(reference, palette);
|
|
2465
|
+
if (opaque) return opaque;
|
|
2466
|
+
if (!underlayReference) return null;
|
|
2467
|
+
const underlayHex = resolveConcreteColorHex(underlayReference, palette);
|
|
2468
|
+
if (!underlayHex) return null;
|
|
2469
|
+
const parts = parseColorTokenParts(reference);
|
|
2470
|
+
if (parts.kind === "token" && parts.opacityPercent !== null) {
|
|
2471
|
+
const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
|
|
2472
|
+
return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
|
|
2473
|
+
}
|
|
2474
|
+
const raw = parts.kind === "raw" ? parts.value : reference;
|
|
2475
|
+
const rgba = parseRgba(raw);
|
|
2476
|
+
return rgba ? compositeHex(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
|
|
2477
|
+
}
|
|
2478
|
+
/** Resolve a colour reference only when it is already fully opaque. */
|
|
2479
|
+
function resolveConcreteColorHex(reference, palette) {
|
|
2480
|
+
const literalHex = normalizePaletteHex(reference);
|
|
2481
|
+
if (literalHex) return literalHex;
|
|
2482
|
+
const parts = parseColorTokenParts(reference);
|
|
2483
|
+
if (parts.kind === "token") {
|
|
2484
|
+
if (parts.opacityPercent !== null) return null;
|
|
2485
|
+
return normalizePaletteHex(palette[parts.token] ?? "");
|
|
2486
|
+
}
|
|
2487
|
+
const literal = parts.kind === "raw" ? parts.value : reference;
|
|
2488
|
+
if (literal.toLowerCase() === "black") return "#000000";
|
|
2489
|
+
if (literal.toLowerCase() === "white") return "#ffffff";
|
|
2490
|
+
return normalizePaletteHex(literal);
|
|
2491
|
+
}
|
|
2492
|
+
function compositeHex(foregroundHex, underlayHex, alpha) {
|
|
2493
|
+
const foreground = hexToRgb(foregroundHex);
|
|
2494
|
+
const underlay = hexToRgb(underlayHex);
|
|
2495
|
+
const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
|
|
2496
|
+
return rgbToHex(channel(foreground.r, underlay.r), channel(foreground.g, underlay.g), channel(foreground.b, underlay.b));
|
|
2497
|
+
}
|
|
2498
|
+
function parseRgba(value) {
|
|
2499
|
+
const match = value.trim().match(/^rgba?\(\s*([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*(?:,|\/)\s*([\d.]+%?))?\s*\)$/i);
|
|
2500
|
+
if (!match) return null;
|
|
2501
|
+
const [, rSource, gSource, bSource] = match;
|
|
2502
|
+
if (!rSource || !gSource || !bSource) return null;
|
|
2503
|
+
const r = Number(rSource);
|
|
2504
|
+
const g = Number(gSource);
|
|
2505
|
+
const b = Number(bSource);
|
|
2506
|
+
const alpha = parseAlpha(match[4]);
|
|
2507
|
+
if (alpha === null || !isRgbChannel(r) || !isRgbChannel(g) || !isRgbChannel(b)) {
|
|
2508
|
+
return null;
|
|
2509
|
+
}
|
|
2510
|
+
return {
|
|
2511
|
+
r,
|
|
2512
|
+
g,
|
|
2513
|
+
b,
|
|
2514
|
+
alpha
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
function parseAlpha(source) {
|
|
2518
|
+
if (!source) return 1;
|
|
2519
|
+
const value = source.endsWith("%") ? Number(source.slice(0, -1)) / 100 : Number(source);
|
|
2520
|
+
return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
|
|
2521
|
+
}
|
|
2522
|
+
function isRgbChannel(value) {
|
|
2523
|
+
return Number.isFinite(value) && value >= 0 && value <= 255;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
//#endregion
|
|
2527
|
+
//#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
|
|
2528
|
+
const FALLBACK_TEXT_TOKENS = [
|
|
2529
|
+
"text",
|
|
2530
|
+
"primaryForeground",
|
|
2531
|
+
"secondaryForeground",
|
|
2532
|
+
"accentForeground",
|
|
2533
|
+
"neutral-950",
|
|
2534
|
+
"neutral-50"
|
|
2535
|
+
];
|
|
2536
|
+
const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
|
|
2537
|
+
"text",
|
|
2538
|
+
"primaryForeground",
|
|
2539
|
+
"secondaryForeground",
|
|
2540
|
+
"accentForeground",
|
|
2541
|
+
"onPrimary",
|
|
2542
|
+
"onSecondary",
|
|
2543
|
+
"onAccent",
|
|
2544
|
+
"onSurface",
|
|
2545
|
+
"neutral-950",
|
|
2546
|
+
"neutral-50"
|
|
2547
|
+
];
|
|
2548
|
+
/**
|
|
2549
|
+
* Resolve the complete foreground contract for an effective page/section or
|
|
2550
|
+
* site-chrome background.
|
|
2551
|
+
*
|
|
2552
|
+
* Deterministic colour backgrounds are validated at ordinary-text WCAG AA.
|
|
2553
|
+
* Existing image/video/gradient treatments are trusted only when they already
|
|
2554
|
+
* carry a usable foreground token; this function deliberately does not claim
|
|
2555
|
+
* pixel-level contrast proof for media.
|
|
2556
|
+
*/
|
|
2557
|
+
function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
|
|
2558
|
+
const authored = foregroundCandidates(background, candidates);
|
|
2559
|
+
if (background === null) {
|
|
2560
|
+
return resolveDeterministicForeground("background", {
|
|
2561
|
+
...authored,
|
|
2562
|
+
text: authored.text ?? "text"
|
|
2563
|
+
}, palette);
|
|
2564
|
+
}
|
|
2565
|
+
if (background.type === "color") {
|
|
2294
2566
|
const backgroundReference = normalizedReference(background.color);
|
|
2295
2567
|
if (!backgroundReference || backgroundReference === "transparent") {
|
|
2296
2568
|
return { kind: "unknown" };
|
|
@@ -2328,63 +2600,288 @@ function resolveDeterministicForeground(backgroundReference, candidates, palette
|
|
|
2328
2600
|
link: safeTokenOr(candidates.link, text, backgroundHex, palette),
|
|
2329
2601
|
supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
|
|
2330
2602
|
}
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
function resolveTrustedTreatmentForeground(candidates, palette) {
|
|
2334
|
-
const text = canonicalToken(candidates.text, palette);
|
|
2335
|
-
if (!text) return { kind: "unknown" };
|
|
2336
|
-
return {
|
|
2337
|
-
kind: "known",
|
|
2338
|
-
roles: {
|
|
2339
|
-
text,
|
|
2340
|
-
heading: canonicalToken(candidates.heading, palette) ?? text,
|
|
2341
|
-
link: canonicalToken(candidates.link, palette) ?? text,
|
|
2342
|
-
supporting: canonicalToken(candidates.supporting, palette) ?? text
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
function resolveTrustedTreatmentForeground(candidates, palette) {
|
|
2606
|
+
const text = canonicalToken(candidates.text, palette);
|
|
2607
|
+
if (!text) return { kind: "unknown" };
|
|
2608
|
+
return {
|
|
2609
|
+
kind: "known",
|
|
2610
|
+
roles: {
|
|
2611
|
+
text,
|
|
2612
|
+
heading: canonicalToken(candidates.heading, palette) ?? text,
|
|
2613
|
+
link: canonicalToken(candidates.link, palette) ?? text,
|
|
2614
|
+
supporting: canonicalToken(candidates.supporting, palette) ?? text
|
|
2615
|
+
}
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
|
|
2619
|
+
return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
|
|
2620
|
+
}
|
|
2621
|
+
function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
|
|
2622
|
+
const seen = new Set();
|
|
2623
|
+
for (const candidate$1 of candidates) {
|
|
2624
|
+
const token = canonicalToken(candidate$1, palette, preferredEquivalentTokens);
|
|
2625
|
+
if (!token || seen.has(token)) continue;
|
|
2626
|
+
seen.add(token);
|
|
2627
|
+
const foregroundHex = resolveConcreteColorHex(token, palette);
|
|
2628
|
+
if (foregroundHex && contrastRatio(backgroundHex, foregroundHex) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO) {
|
|
2629
|
+
return token;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
return null;
|
|
2633
|
+
}
|
|
2634
|
+
function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
|
|
2635
|
+
const normalized = normalizedReference(reference);
|
|
2636
|
+
if (!normalized || normalized === "transparent") return null;
|
|
2637
|
+
const direct = parseThemeColorToken(normalized);
|
|
2638
|
+
if (direct && resolveConcreteColorHex(direct, palette)) return direct;
|
|
2639
|
+
const referenceHex = resolveConcreteColorHex(normalized, palette);
|
|
2640
|
+
if (!referenceHex) return null;
|
|
2641
|
+
const equivalent = [
|
|
2642
|
+
...preferredEquivalentTokens,
|
|
2643
|
+
...CANONICAL_EQUIVALENT_TOKEN_ORDER,
|
|
2644
|
+
...Object.keys(palette).sort()
|
|
2645
|
+
].find((token) => {
|
|
2646
|
+
const parsed = parseThemeColorToken(token);
|
|
2647
|
+
return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
|
|
2648
|
+
});
|
|
2649
|
+
return equivalent ? parseThemeColorToken(equivalent) : null;
|
|
2650
|
+
}
|
|
2651
|
+
function paletteToken(reference) {
|
|
2652
|
+
const parts = parseColorTokenParts(reference);
|
|
2653
|
+
return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
|
|
2654
|
+
}
|
|
2655
|
+
function normalizedReference(value) {
|
|
2656
|
+
if (typeof value !== "string") return null;
|
|
2657
|
+
const normalized = value.trim();
|
|
2658
|
+
return normalized.length > 0 ? normalized : null;
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
//#endregion
|
|
2662
|
+
//#region ../theme-core/src/utils/colorStyles.ts
|
|
2663
|
+
const COLOR_VAR_PREFIX = "--tb-";
|
|
2664
|
+
function parseToken(source) {
|
|
2665
|
+
const parts = parseColorTokenParts(source);
|
|
2666
|
+
if (parts.kind === "raw") {
|
|
2667
|
+
return {
|
|
2668
|
+
token: source,
|
|
2669
|
+
raw: parts.value
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
if (parts.kind === "invalid") {
|
|
2673
|
+
return { token: source };
|
|
2674
|
+
}
|
|
2675
|
+
return {
|
|
2676
|
+
token: parts.token,
|
|
2677
|
+
opacityPercent: parts.opacityPercent === null ? undefined : parts.opacityPercent
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
const SURFACE_TEXT_ROLE_BY_TOKEN = {
|
|
2681
|
+
text: "text",
|
|
2682
|
+
heading: "heading",
|
|
2683
|
+
mutedText: "supporting"
|
|
2684
|
+
};
|
|
2685
|
+
function surfaceRoleForTextToken(token) {
|
|
2686
|
+
return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
|
|
2687
|
+
}
|
|
2688
|
+
/**
|
|
2689
|
+
* Converts a theme color token to a CSS rgb value.
|
|
2690
|
+
* This is the primary function for applying theme colors to CSS properties.
|
|
2691
|
+
*
|
|
2692
|
+
* @param token - Theme color token (semantic, with opacity, or raw)
|
|
2693
|
+
* @returns CSS color value (rgb with optional alpha, or raw CSS value)
|
|
2694
|
+
*
|
|
2695
|
+
* @example
|
|
2696
|
+
* rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
|
|
2697
|
+
* rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
|
|
2698
|
+
* rgbColorValue('raw:transparent') // => 'transparent'
|
|
2699
|
+
*/
|
|
2700
|
+
function rgbColorValue(token) {
|
|
2701
|
+
const { token: baseToken, opacityPercent, raw } = parseToken(token);
|
|
2702
|
+
if (raw) {
|
|
2703
|
+
return raw;
|
|
2704
|
+
}
|
|
2705
|
+
const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
|
|
2706
|
+
if (opacityPercent === undefined) {
|
|
2707
|
+
return `rgb(var(${cssVar}))`;
|
|
2708
|
+
}
|
|
2709
|
+
return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
|
|
2710
|
+
}
|
|
2711
|
+
/**
|
|
2712
|
+
* Converts a theme color token to a CSS variable reference.
|
|
2713
|
+
* Note: This returns the base variable without rgb() wrapper, ignoring opacity.
|
|
2714
|
+
*
|
|
2715
|
+
* @param token - Theme color token
|
|
2716
|
+
* @returns CSS variable reference
|
|
2717
|
+
*
|
|
2718
|
+
* @example
|
|
2719
|
+
* cssVarValue('primary') // => 'var(--tb-primary)'
|
|
2720
|
+
* cssVarValue('surface/95') // => 'var(--tb-surface)' (opacity ignored)
|
|
2721
|
+
*/
|
|
2722
|
+
function cssVarValue(token) {
|
|
2723
|
+
const { token: baseToken } = parseToken(token);
|
|
2724
|
+
return `var(${COLOR_VAR_PREFIX}${baseToken})`;
|
|
2725
|
+
}
|
|
2726
|
+
/**
|
|
2727
|
+
* Creates a backgroundColor style object from a theme token.
|
|
2728
|
+
*
|
|
2729
|
+
* @param token - Theme color token
|
|
2730
|
+
* @returns CSSProperties with backgroundColor
|
|
2731
|
+
*/
|
|
2732
|
+
function backgroundColorStyle(token) {
|
|
2733
|
+
return { backgroundColor: rgbColorValue(token) };
|
|
2734
|
+
}
|
|
2735
|
+
/**
|
|
2736
|
+
* Creates a text color style object from a theme token.
|
|
2737
|
+
*
|
|
2738
|
+
* @param token - Theme color token
|
|
2739
|
+
* @returns CSSProperties with color
|
|
2740
|
+
*/
|
|
2741
|
+
function textColorStyle(token) {
|
|
2742
|
+
const parsed = parseToken(token);
|
|
2743
|
+
const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
|
|
2744
|
+
if (surfaceRole) {
|
|
2745
|
+
return { color: parsed.opacityPercent === undefined ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
|
|
2746
|
+
}
|
|
2747
|
+
return { color: rgbColorValue(token) };
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Creates a border color style object from a theme token.
|
|
2751
|
+
*
|
|
2752
|
+
* @param token - Theme color token
|
|
2753
|
+
* @returns CSSProperties with borderColor
|
|
2754
|
+
*/
|
|
2755
|
+
function borderColorStyle(token) {
|
|
2756
|
+
return { borderColor: rgbColorValue(token) };
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2759
|
+
* Creates a linear gradient background from theme tokens.
|
|
2760
|
+
*
|
|
2761
|
+
* @param tokens - Array of theme color tokens for gradient stops
|
|
2762
|
+
* @param direction - CSS gradient direction (default: 'to bottom')
|
|
2763
|
+
* @returns CSSProperties with backgroundImage
|
|
2764
|
+
*
|
|
2765
|
+
* @example
|
|
2766
|
+
* gradientBackgroundStyle(['primary', 'secondary'])
|
|
2767
|
+
* // => { backgroundImage: 'linear-gradient(to bottom, rgb(var(--tb-primary)), rgb(var(--tb-secondary)))' }
|
|
2768
|
+
*
|
|
2769
|
+
* @example
|
|
2770
|
+
* gradientBackgroundStyle(['primary/80', 'secondary/60'], '45deg')
|
|
2771
|
+
* // => { backgroundImage: 'linear-gradient(45deg, rgb(var(--tb-primary) / 0.8), rgb(var(--tb-secondary) / 0.6))' }
|
|
2772
|
+
*/
|
|
2773
|
+
function gradientBackgroundStyle(tokens, direction = "to bottom") {
|
|
2774
|
+
const stops = tokens.map((stop) => rgbColorValue(stop)).join(", ");
|
|
2775
|
+
return { backgroundImage: `linear-gradient(${direction}, ${stops})` };
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* Merges multiple CSSProperties objects into a single object.
|
|
2779
|
+
* Later styles override earlier ones. Undefined values are filtered out.
|
|
2780
|
+
* Returns undefined if the merged result is empty.
|
|
2781
|
+
*
|
|
2782
|
+
* @param styles - Variable number of CSSProperties objects (can be undefined)
|
|
2783
|
+
* @returns Merged CSSProperties object, or undefined if empty
|
|
2784
|
+
*
|
|
2785
|
+
* @example
|
|
2786
|
+
* mergeStyles(
|
|
2787
|
+
* backgroundColorStyle('surface'),
|
|
2788
|
+
* textColorStyle('text'),
|
|
2789
|
+
* borderColorStyle('border')
|
|
2790
|
+
* )
|
|
2791
|
+
* // => { backgroundColor: '...', color: '...', borderColor: '...' }
|
|
2792
|
+
*/
|
|
2793
|
+
function mergeStyles(...styles) {
|
|
2794
|
+
const merged = {};
|
|
2795
|
+
for (const style of styles) {
|
|
2796
|
+
if (!style) continue;
|
|
2797
|
+
Object.assign(merged, style);
|
|
2798
|
+
}
|
|
2799
|
+
return Object.keys(merged).length ? merged : undefined;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
//#endregion
|
|
2803
|
+
//#region ../theme-core/src/tokens/resolver.ts
|
|
2804
|
+
/**
|
|
2805
|
+
* Resolves theme tokens to CSS variables
|
|
2806
|
+
*
|
|
2807
|
+
* This resolver ensures all button styles use theme tokens (not hardcoded colors)
|
|
2808
|
+
* so that changing the palette automatically updates all buttons.
|
|
2809
|
+
*/
|
|
2810
|
+
var TokenResolver = class {
|
|
2811
|
+
constructor(theme) {
|
|
2812
|
+
this.theme = theme;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Resolve a color token to a CSS variable reference wrapped in rgb()
|
|
2816
|
+
*
|
|
2817
|
+
* @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
|
|
2818
|
+
* @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
|
|
2819
|
+
*
|
|
2820
|
+
* Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
|
|
2821
|
+
* so we must wrap them with rgb() to create a valid color value.
|
|
2822
|
+
*
|
|
2823
|
+
* @example
|
|
2824
|
+
* resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
|
|
2825
|
+
* resolver.getColor('white') // => 'rgb(var(--tb-white))'
|
|
2826
|
+
*/
|
|
2827
|
+
getColor(token) {
|
|
2828
|
+
if (!this.tokenExists(token)) {
|
|
2829
|
+
console.warn(`[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`);
|
|
2830
|
+
}
|
|
2831
|
+
return rgbColorValue(token);
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Adjust a color token's shade by a given amount
|
|
2835
|
+
*
|
|
2836
|
+
* @param token - Base token (e.g., 'primary-500')
|
|
2837
|
+
* @param amount - Shade adjustment (+100 = darker, -100 = lighter)
|
|
2838
|
+
* @returns Adjusted token name (e.g., 'primary-600')
|
|
2839
|
+
*
|
|
2840
|
+
* @example
|
|
2841
|
+
* resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
|
|
2842
|
+
* resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
|
|
2843
|
+
*/
|
|
2844
|
+
adjustShade(token, amount) {
|
|
2845
|
+
const parts = token.split("-");
|
|
2846
|
+
if (parts.length < 2) {
|
|
2847
|
+
const newShade$1 = Math.max(100, Math.min(900, 500 + amount));
|
|
2848
|
+
return `${token}-${newShade$1}`;
|
|
2343
2849
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
}
|
|
2349
|
-
function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
|
|
2350
|
-
const seen = new Set();
|
|
2351
|
-
for (const candidate$1 of candidates) {
|
|
2352
|
-
const token = canonicalToken(candidate$1, palette, preferredEquivalentTokens);
|
|
2353
|
-
if (!token || seen.has(token)) continue;
|
|
2354
|
-
seen.add(token);
|
|
2355
|
-
const foregroundHex = resolveConcreteColorHex(token, palette);
|
|
2356
|
-
if (foregroundHex && contrastRatio(backgroundHex, foregroundHex) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO) {
|
|
2850
|
+
const base = parts.slice(0, -1).join("-");
|
|
2851
|
+
const shade = parseInt(parts[parts.length - 1] ?? "", 10);
|
|
2852
|
+
if (isNaN(shade)) {
|
|
2853
|
+
console.warn(`[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`);
|
|
2357
2854
|
return token;
|
|
2358
2855
|
}
|
|
2856
|
+
const newShade = Math.max(100, Math.min(900, shade + amount));
|
|
2857
|
+
return `${base}-${newShade}`;
|
|
2359
2858
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
return normalized.length > 0 ? normalized : null;
|
|
2387
|
-
}
|
|
2859
|
+
/**
|
|
2860
|
+
* Check if a token exists in the theme palette
|
|
2861
|
+
*
|
|
2862
|
+
* @param token - Token name to check
|
|
2863
|
+
* @returns True if token exists in theme
|
|
2864
|
+
*/
|
|
2865
|
+
tokenExists(token) {
|
|
2866
|
+
const commonTokens = [
|
|
2867
|
+
"white",
|
|
2868
|
+
"black",
|
|
2869
|
+
"transparent"
|
|
2870
|
+
];
|
|
2871
|
+
if (commonTokens.includes(token)) {
|
|
2872
|
+
return true;
|
|
2873
|
+
}
|
|
2874
|
+
const parts = token.split("-");
|
|
2875
|
+
if (parts.length >= 2) {
|
|
2876
|
+
const lastPart = parts[parts.length - 1] ?? "";
|
|
2877
|
+
const shade = parseInt(lastPart, 10);
|
|
2878
|
+
if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
|
|
2879
|
+
return true;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
return true;
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2388
2885
|
|
|
2389
2886
|
//#endregion
|
|
2390
2887
|
//#region ../theme-core/src/cards/generateCardCss.ts
|
|
@@ -2513,14 +3010,12 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
|
|
|
2513
3010
|
const shadowValue = generateShadowValue$1(shadow$1);
|
|
2514
3011
|
rules.push(`box-shadow: ${shadowValue}`);
|
|
2515
3012
|
}
|
|
2516
|
-
|
|
2517
|
-
rules.push(`flex-direction: column`);
|
|
2518
|
-
rules.push(`position: relative`);
|
|
2519
|
-
rules.push(`overflow: visible`);
|
|
3013
|
+
const structureSelector = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) :where(.${cardClass})`).join(",\n");
|
|
2520
3014
|
const mediaRadius = MEDIA_CORNER_MAP[corners$1];
|
|
2521
3015
|
const mediaRules = [`border-top-left-radius: ${mediaRadius}`, `border-top-right-radius: ${mediaRadius}`];
|
|
2522
3016
|
const contentRules = [`padding: ${resolveContentPadding(variant)}`];
|
|
2523
3017
|
return [
|
|
3018
|
+
`${structureSelector} {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: visible;\n}`,
|
|
2524
3019
|
`${containerSelector} {\n ${rules.join(";\n ")};\n}`,
|
|
2525
3020
|
...cardDecorationPseudoRules(containerSelectors, variant.decorations, resolveColor),
|
|
2526
3021
|
`${fallbackBackgroundSelector} {\n background: transparent;\n}`,
|
|
@@ -4096,149 +4591,8 @@ function isThemeV2NormalEditorRawStyleFieldName(fieldName) {
|
|
|
4096
4591
|
return themeV2NormalEditorRawStyleFieldNameSet.has(fieldName);
|
|
4097
4592
|
}
|
|
4098
4593
|
function isThemeV2NormalEditorRawStylePath(fieldPath) {
|
|
4099
|
-
const rootField = fieldPath.split(".")[0] ?? "";
|
|
4100
|
-
return isThemeV2NormalEditorRawStyleFieldName(rootField);
|
|
4101
|
-
}
|
|
4102
|
-
|
|
4103
|
-
//#endregion
|
|
4104
|
-
//#region ../theme-core/src/utils/colorStyles.ts
|
|
4105
|
-
const COLOR_VAR_PREFIX = "--tb-";
|
|
4106
|
-
function parseToken(source) {
|
|
4107
|
-
const parts = parseColorTokenParts(source);
|
|
4108
|
-
if (parts.kind === "raw") {
|
|
4109
|
-
return {
|
|
4110
|
-
token: source,
|
|
4111
|
-
raw: parts.value
|
|
4112
|
-
};
|
|
4113
|
-
}
|
|
4114
|
-
if (parts.kind === "invalid") {
|
|
4115
|
-
return { token: source };
|
|
4116
|
-
}
|
|
4117
|
-
return {
|
|
4118
|
-
token: parts.token,
|
|
4119
|
-
opacityPercent: parts.opacityPercent === null ? undefined : parts.opacityPercent
|
|
4120
|
-
};
|
|
4121
|
-
}
|
|
4122
|
-
const SURFACE_TEXT_ROLE_BY_TOKEN = {
|
|
4123
|
-
text: "text",
|
|
4124
|
-
heading: "heading",
|
|
4125
|
-
mutedText: "supporting"
|
|
4126
|
-
};
|
|
4127
|
-
function surfaceRoleForTextToken(token) {
|
|
4128
|
-
return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
|
|
4129
|
-
}
|
|
4130
|
-
/**
|
|
4131
|
-
* Converts a theme color token to a CSS rgb value.
|
|
4132
|
-
* This is the primary function for applying theme colors to CSS properties.
|
|
4133
|
-
*
|
|
4134
|
-
* @param token - Theme color token (semantic, with opacity, or raw)
|
|
4135
|
-
* @returns CSS color value (rgb with optional alpha, or raw CSS value)
|
|
4136
|
-
*
|
|
4137
|
-
* @example
|
|
4138
|
-
* rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
|
|
4139
|
-
* rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
|
|
4140
|
-
* rgbColorValue('raw:transparent') // => 'transparent'
|
|
4141
|
-
*/
|
|
4142
|
-
function rgbColorValue(token) {
|
|
4143
|
-
const { token: baseToken, opacityPercent, raw } = parseToken(token);
|
|
4144
|
-
if (raw) {
|
|
4145
|
-
return raw;
|
|
4146
|
-
}
|
|
4147
|
-
const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
|
|
4148
|
-
if (opacityPercent === undefined) {
|
|
4149
|
-
return `rgb(var(${cssVar}))`;
|
|
4150
|
-
}
|
|
4151
|
-
return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
|
|
4152
|
-
}
|
|
4153
|
-
/**
|
|
4154
|
-
* Converts a theme color token to a CSS variable reference.
|
|
4155
|
-
* Note: This returns the base variable without rgb() wrapper, ignoring opacity.
|
|
4156
|
-
*
|
|
4157
|
-
* @param token - Theme color token
|
|
4158
|
-
* @returns CSS variable reference
|
|
4159
|
-
*
|
|
4160
|
-
* @example
|
|
4161
|
-
* cssVarValue('primary') // => 'var(--tb-primary)'
|
|
4162
|
-
* cssVarValue('surface/95') // => 'var(--tb-surface)' (opacity ignored)
|
|
4163
|
-
*/
|
|
4164
|
-
function cssVarValue(token) {
|
|
4165
|
-
const { token: baseToken } = parseToken(token);
|
|
4166
|
-
return `var(${COLOR_VAR_PREFIX}${baseToken})`;
|
|
4167
|
-
}
|
|
4168
|
-
/**
|
|
4169
|
-
* Creates a backgroundColor style object from a theme token.
|
|
4170
|
-
*
|
|
4171
|
-
* @param token - Theme color token
|
|
4172
|
-
* @returns CSSProperties with backgroundColor
|
|
4173
|
-
*/
|
|
4174
|
-
function backgroundColorStyle(token) {
|
|
4175
|
-
return { backgroundColor: rgbColorValue(token) };
|
|
4176
|
-
}
|
|
4177
|
-
/**
|
|
4178
|
-
* Creates a text color style object from a theme token.
|
|
4179
|
-
*
|
|
4180
|
-
* @param token - Theme color token
|
|
4181
|
-
* @returns CSSProperties with color
|
|
4182
|
-
*/
|
|
4183
|
-
function textColorStyle(token) {
|
|
4184
|
-
const parsed = parseToken(token);
|
|
4185
|
-
const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
|
|
4186
|
-
if (surfaceRole) {
|
|
4187
|
-
return { color: parsed.opacityPercent === undefined ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
|
|
4188
|
-
}
|
|
4189
|
-
return { color: rgbColorValue(token) };
|
|
4190
|
-
}
|
|
4191
|
-
/**
|
|
4192
|
-
* Creates a border color style object from a theme token.
|
|
4193
|
-
*
|
|
4194
|
-
* @param token - Theme color token
|
|
4195
|
-
* @returns CSSProperties with borderColor
|
|
4196
|
-
*/
|
|
4197
|
-
function borderColorStyle(token) {
|
|
4198
|
-
return { borderColor: rgbColorValue(token) };
|
|
4199
|
-
}
|
|
4200
|
-
/**
|
|
4201
|
-
* Creates a linear gradient background from theme tokens.
|
|
4202
|
-
*
|
|
4203
|
-
* @param tokens - Array of theme color tokens for gradient stops
|
|
4204
|
-
* @param direction - CSS gradient direction (default: 'to bottom')
|
|
4205
|
-
* @returns CSSProperties with backgroundImage
|
|
4206
|
-
*
|
|
4207
|
-
* @example
|
|
4208
|
-
* gradientBackgroundStyle(['primary', 'secondary'])
|
|
4209
|
-
* // => { backgroundImage: 'linear-gradient(to bottom, rgb(var(--tb-primary)), rgb(var(--tb-secondary)))' }
|
|
4210
|
-
*
|
|
4211
|
-
* @example
|
|
4212
|
-
* gradientBackgroundStyle(['primary/80', 'secondary/60'], '45deg')
|
|
4213
|
-
* // => { backgroundImage: 'linear-gradient(45deg, rgb(var(--tb-primary) / 0.8), rgb(var(--tb-secondary) / 0.6))' }
|
|
4214
|
-
*/
|
|
4215
|
-
function gradientBackgroundStyle(tokens, direction = "to bottom") {
|
|
4216
|
-
const stops = tokens.map((stop) => rgbColorValue(stop)).join(", ");
|
|
4217
|
-
return { backgroundImage: `linear-gradient(${direction}, ${stops})` };
|
|
4218
|
-
}
|
|
4219
|
-
/**
|
|
4220
|
-
* Merges multiple CSSProperties objects into a single object.
|
|
4221
|
-
* Later styles override earlier ones. Undefined values are filtered out.
|
|
4222
|
-
* Returns undefined if the merged result is empty.
|
|
4223
|
-
*
|
|
4224
|
-
* @param styles - Variable number of CSSProperties objects (can be undefined)
|
|
4225
|
-
* @returns Merged CSSProperties object, or undefined if empty
|
|
4226
|
-
*
|
|
4227
|
-
* @example
|
|
4228
|
-
* mergeStyles(
|
|
4229
|
-
* backgroundColorStyle('surface'),
|
|
4230
|
-
* textColorStyle('text'),
|
|
4231
|
-
* borderColorStyle('border')
|
|
4232
|
-
* )
|
|
4233
|
-
* // => { backgroundColor: '...', color: '...', borderColor: '...' }
|
|
4234
|
-
*/
|
|
4235
|
-
function mergeStyles(...styles) {
|
|
4236
|
-
const merged = {};
|
|
4237
|
-
for (const style of styles) {
|
|
4238
|
-
if (!style) continue;
|
|
4239
|
-
Object.assign(merged, style);
|
|
4240
|
-
}
|
|
4241
|
-
return Object.keys(merged).length ? merged : undefined;
|
|
4594
|
+
const rootField = fieldPath.split(".")[0] ?? "";
|
|
4595
|
+
return isThemeV2NormalEditorRawStyleFieldName(rootField);
|
|
4242
4596
|
}
|
|
4243
4597
|
|
|
4244
4598
|
//#endregion
|
|
@@ -4634,8 +4988,14 @@ const headerInteractionPillPaletteValues = [
|
|
|
4634
4988
|
"accent"
|
|
4635
4989
|
];
|
|
4636
4990
|
/**
|
|
4637
|
-
* "No pill":
|
|
4638
|
-
*
|
|
4991
|
+
* "No pill": a colour change on hover rather than a filled background.
|
|
4992
|
+
*
|
|
4993
|
+
* The palette step writes nothing (#1318 Phase 4c decision 2), but on a
|
|
4994
|
+
* foreground-adaptive style that is not the end of the story: the hover colour
|
|
4995
|
+
* is derived from the resolved opening foreground at render time, paired with
|
|
4996
|
+
* the resting colour, so `quiet` resolves to a definite colour rather than to
|
|
4997
|
+
* whatever brand token the recipe happened to name. See
|
|
4998
|
+
* `applyOpeningForeground`.
|
|
4639
4999
|
*
|
|
4640
5000
|
* It exists because "a colour change on hover, no filled background" was not
|
|
4641
5001
|
* expressible in the vocabulary, and it is what
|
|
@@ -5221,12 +5581,6 @@ const footerVariant = z.enum([
|
|
|
5221
5581
|
"split"
|
|
5222
5582
|
]);
|
|
5223
5583
|
const footerMaxWidth = z.enum(["container", "full"]);
|
|
5224
|
-
const footerMode = z.enum([
|
|
5225
|
-
"default",
|
|
5226
|
-
"blocks",
|
|
5227
|
-
"default+blocks",
|
|
5228
|
-
"none"
|
|
5229
|
-
]);
|
|
5230
5584
|
const footerNavLayoutMode = z.enum([
|
|
5231
5585
|
"stack",
|
|
5232
5586
|
"inline",
|
|
@@ -5312,7 +5666,7 @@ const footerLogoSchema = z.object({
|
|
|
5312
5666
|
]).optional()
|
|
5313
5667
|
});
|
|
5314
5668
|
const footerSchema = z.object({
|
|
5315
|
-
mode:
|
|
5669
|
+
mode: footerModeSchema.optional(),
|
|
5316
5670
|
variant: footerVariant,
|
|
5317
5671
|
maxWidth: footerMaxWidth,
|
|
5318
5672
|
showLogoText: z.boolean().optional(),
|
|
@@ -5935,7 +6289,7 @@ const BUTTON_CONTRAST_POLICY = {
|
|
|
5935
6289
|
nonTextMinimumRatio: 3,
|
|
5936
6290
|
gradientSamplesPerSegment: 20,
|
|
5937
6291
|
alphaBackgrounds: "reject-without-known-surface",
|
|
5938
|
-
disabled: "
|
|
6292
|
+
disabled: "validated-colour-pair-with-muted-control-opacity"
|
|
5939
6293
|
};
|
|
5940
6294
|
function formatButtonColourContractError(error) {
|
|
5941
6295
|
switch (error.tag) {
|
|
@@ -6560,6 +6914,26 @@ function parseThemeForPersistence(value) {
|
|
|
6560
6914
|
};
|
|
6561
6915
|
}
|
|
6562
6916
|
|
|
6917
|
+
//#endregion
|
|
6918
|
+
//#region ../theme-core/src/buttons/affordanceSuppression.ts
|
|
6919
|
+
/**
|
|
6920
|
+
* Internal: the opt-out the generated affordance rule is written against.
|
|
6921
|
+
*
|
|
6922
|
+
* A button style may declare a trailing `affordance`, which the theme renders as
|
|
6923
|
+
* a CSS `::after` on every button of that variant. An authored button that
|
|
6924
|
+
* renders its own trailing icon must not also receive the theme's, or it shows
|
|
6925
|
+
* two.
|
|
6926
|
+
*
|
|
6927
|
+
* Deliberately not exported from `./index`. This used to be public, and two
|
|
6928
|
+
* block components reached past the theme to draw arrows of their own — one of
|
|
6929
|
+
* them doubling up on the styles that declare an affordance, both of them
|
|
6930
|
+
* overriding the twelve styles that deliberately declare none. Whether a button
|
|
6931
|
+
* carries a trailing affordance is the style's decision; the only caller that
|
|
6932
|
+
* legitimately opts out is the authored-button renderer, which reaches it
|
|
6933
|
+
* through `themeButtonClassNameWithTrailingIcon`.
|
|
6934
|
+
*/
|
|
6935
|
+
const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
|
|
6936
|
+
|
|
6563
6937
|
//#endregion
|
|
6564
6938
|
//#region ../theme-core/src/buttons/classNames.ts
|
|
6565
6939
|
/**
|
|
@@ -6604,6 +6978,27 @@ function themeButtonClassName(spec) {
|
|
|
6604
6978
|
];
|
|
6605
6979
|
return classes.filter(Boolean).join(" ");
|
|
6606
6980
|
}
|
|
6981
|
+
/**
|
|
6982
|
+
* Button classes for an authored button that renders its own trailing icon.
|
|
6983
|
+
*
|
|
6984
|
+
* A separate function rather than a flag on `themeButtonClassName`, because
|
|
6985
|
+
* there is exactly one caller and it should stay visible in a diff. The name
|
|
6986
|
+
* states what the caller knows to be true of its own markup — that it renders a
|
|
6987
|
+
* trailing icon — rather than what the theme does about it. The consequence is
|
|
6988
|
+
* the theme's to decide and to change; a caller naming the consequence would be
|
|
6989
|
+
* asserting a policy it does not own, and would quietly become a lie the moment
|
|
6990
|
+
* that policy moved.
|
|
6991
|
+
*
|
|
6992
|
+
* Do not reach for this to remove an arrow you simply do not want. A trailing
|
|
6993
|
+
* affordance is declared by the button style, and a block overriding that is the
|
|
6994
|
+
* defect this replaced.
|
|
6995
|
+
*/
|
|
6996
|
+
function themeButtonClassNameWithTrailingIcon(spec) {
|
|
6997
|
+
return themeButtonClassName({
|
|
6998
|
+
...spec,
|
|
6999
|
+
extraClassName: [spec.extraClassName, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ")
|
|
7000
|
+
});
|
|
7001
|
+
}
|
|
6607
7002
|
|
|
6608
7003
|
//#endregion
|
|
6609
7004
|
//#region ../theme-core/src/interactive/generateBaseCSS.ts
|
|
@@ -8856,27 +9251,20 @@ function resolvedButtonFocusDeclarations(indicator, role) {
|
|
|
8856
9251
|
] : [["outline", value("outline", `3px solid ${indicator.colorCss}`)], ["outline-offset", value("outline-offset", "3px")]];
|
|
8857
9252
|
}
|
|
8858
9253
|
/**
|
|
8859
|
-
* Disabled buttons
|
|
8860
|
-
*
|
|
8861
|
-
*
|
|
8862
|
-
* remain observable.
|
|
9254
|
+
* Disabled buttons are deliberately muted as unavailable controls. Native
|
|
9255
|
+
* disabled controls are exempt from active-control contrast requirements;
|
|
9256
|
+
* opacity distinguishes the state without introducing a focus-like outline.
|
|
9257
|
+
* Retaining pointer events lets the not-allowed cursor remain observable.
|
|
8863
9258
|
*/
|
|
8864
9259
|
const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
|
|
8865
9260
|
["cursor", "not-allowed"],
|
|
8866
9261
|
["transform", "none"],
|
|
8867
9262
|
["box-shadow", "none"],
|
|
8868
|
-
["
|
|
8869
|
-
["outline-offset", "-4px"]
|
|
9263
|
+
["opacity", "0.55"]
|
|
8870
9264
|
];
|
|
8871
9265
|
|
|
8872
9266
|
//#endregion
|
|
8873
9267
|
//#region ../theme-core/src/buttons/generateButtonCss.ts
|
|
8874
|
-
/**
|
|
8875
|
-
* Class the `button` runtime node adds when it renders its own trailing icon,
|
|
8876
|
-
* so a node-level icon suppresses the theme's CSS `::after` affordance.
|
|
8877
|
-
* Shared so generator and renderer can't drift.
|
|
8878
|
-
*/
|
|
8879
|
-
const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
|
|
8880
9268
|
const BUTTON_SIZE_NAMES = buttonSizeNameSchema.options;
|
|
8881
9269
|
/**
|
|
8882
9270
|
* Letter spacing token → CSS value.
|
|
@@ -15999,198 +16387,6 @@ function assertUniqueLayoutProfileKeys(profiles) {
|
|
|
15999
16387
|
}
|
|
16000
16388
|
}
|
|
16001
16389
|
|
|
16002
|
-
//#endregion
|
|
16003
|
-
//#region ../theme-core/src/tokens/tokens.ts
|
|
16004
|
-
const colorTokens = [
|
|
16005
|
-
"background/base",
|
|
16006
|
-
"background/elevated",
|
|
16007
|
-
"surface/base",
|
|
16008
|
-
"surface/subtle",
|
|
16009
|
-
"surface/inverted",
|
|
16010
|
-
"content/primary",
|
|
16011
|
-
"content/secondary",
|
|
16012
|
-
"accent/primary",
|
|
16013
|
-
"accent/secondary",
|
|
16014
|
-
"border/base"
|
|
16015
|
-
];
|
|
16016
|
-
const typographyTokens = [
|
|
16017
|
-
"display/hero",
|
|
16018
|
-
"display/section",
|
|
16019
|
-
"heading/large",
|
|
16020
|
-
"heading/medium",
|
|
16021
|
-
"heading/small",
|
|
16022
|
-
"body/normal",
|
|
16023
|
-
"body/small",
|
|
16024
|
-
"caption"
|
|
16025
|
-
];
|
|
16026
|
-
const spacingTokens = [
|
|
16027
|
-
"none",
|
|
16028
|
-
"3xs",
|
|
16029
|
-
"2xs",
|
|
16030
|
-
"xs",
|
|
16031
|
-
"sm",
|
|
16032
|
-
"md",
|
|
16033
|
-
"lg",
|
|
16034
|
-
"xl",
|
|
16035
|
-
"2xl",
|
|
16036
|
-
"3xl"
|
|
16037
|
-
];
|
|
16038
|
-
const radiusTokens = [
|
|
16039
|
-
"none",
|
|
16040
|
-
"xs",
|
|
16041
|
-
"sm",
|
|
16042
|
-
"md",
|
|
16043
|
-
"lg",
|
|
16044
|
-
"pill",
|
|
16045
|
-
"full"
|
|
16046
|
-
];
|
|
16047
|
-
const shadowTokens = [
|
|
16048
|
-
"none",
|
|
16049
|
-
"soft",
|
|
16050
|
-
"medium",
|
|
16051
|
-
"strong"
|
|
16052
|
-
];
|
|
16053
|
-
const defaultThemeTokens = {
|
|
16054
|
-
color: {
|
|
16055
|
-
"background/base": "#ffffff",
|
|
16056
|
-
"background/elevated": "#f5f5f5",
|
|
16057
|
-
"surface/base": "#ffffff",
|
|
16058
|
-
"surface/subtle": "#f9f9fb",
|
|
16059
|
-
"surface/inverted": "#111111",
|
|
16060
|
-
"content/primary": "#111111",
|
|
16061
|
-
"content/secondary": "#3f3f46",
|
|
16062
|
-
"accent/primary": "#2563eb",
|
|
16063
|
-
"accent/secondary": "#1d4ed8",
|
|
16064
|
-
"border/base": "#e5e7eb"
|
|
16065
|
-
},
|
|
16066
|
-
typography: {
|
|
16067
|
-
"display/hero": {
|
|
16068
|
-
fontSize: "clamp(48px, 8vw, 96px)",
|
|
16069
|
-
lineHeight: "1.05",
|
|
16070
|
-
fontWeight: "700"
|
|
16071
|
-
},
|
|
16072
|
-
"display/section": {
|
|
16073
|
-
fontSize: "clamp(40px, 6vw, 72px)",
|
|
16074
|
-
lineHeight: "1.1",
|
|
16075
|
-
fontWeight: "700"
|
|
16076
|
-
},
|
|
16077
|
-
"heading/large": {
|
|
16078
|
-
fontSize: "clamp(32px, 4vw, 48px)",
|
|
16079
|
-
lineHeight: "1.2",
|
|
16080
|
-
fontWeight: "600"
|
|
16081
|
-
},
|
|
16082
|
-
"heading/medium": {
|
|
16083
|
-
fontSize: "clamp(24px, 3vw, 32px)",
|
|
16084
|
-
lineHeight: "1.25",
|
|
16085
|
-
fontWeight: "600"
|
|
16086
|
-
},
|
|
16087
|
-
"heading/small": {
|
|
16088
|
-
fontSize: "20px",
|
|
16089
|
-
lineHeight: "1.3",
|
|
16090
|
-
fontWeight: "600"
|
|
16091
|
-
},
|
|
16092
|
-
"body/normal": {
|
|
16093
|
-
fontSize: "16px",
|
|
16094
|
-
lineHeight: "1.6",
|
|
16095
|
-
fontWeight: "400"
|
|
16096
|
-
},
|
|
16097
|
-
"body/small": {
|
|
16098
|
-
fontSize: "14px",
|
|
16099
|
-
lineHeight: "1.5",
|
|
16100
|
-
fontWeight: "400"
|
|
16101
|
-
},
|
|
16102
|
-
caption: {
|
|
16103
|
-
fontSize: "12px",
|
|
16104
|
-
lineHeight: "1.4",
|
|
16105
|
-
fontWeight: "500"
|
|
16106
|
-
}
|
|
16107
|
-
},
|
|
16108
|
-
spacing: {
|
|
16109
|
-
none: "0px",
|
|
16110
|
-
"3xs": "4px",
|
|
16111
|
-
"2xs": "8px",
|
|
16112
|
-
xs: "12px",
|
|
16113
|
-
sm: "16px",
|
|
16114
|
-
md: "24px",
|
|
16115
|
-
lg: "32px",
|
|
16116
|
-
xl: "40px",
|
|
16117
|
-
"2xl": "56px",
|
|
16118
|
-
"3xl": "72px"
|
|
16119
|
-
},
|
|
16120
|
-
radius: {
|
|
16121
|
-
none: "0px",
|
|
16122
|
-
xs: "2px",
|
|
16123
|
-
sm: "4px",
|
|
16124
|
-
md: "8px",
|
|
16125
|
-
lg: "12px",
|
|
16126
|
-
pill: "999px",
|
|
16127
|
-
full: "9999px"
|
|
16128
|
-
},
|
|
16129
|
-
shadow: {
|
|
16130
|
-
none: "none",
|
|
16131
|
-
soft: "0px 2px 8px rgba(15, 23, 42, 0.08)",
|
|
16132
|
-
medium: "0px 6px 18px rgba(15, 23, 42, 0.12)",
|
|
16133
|
-
strong: "0px 12px 36px rgba(15, 23, 42, 0.16)"
|
|
16134
|
-
},
|
|
16135
|
-
palette: {
|
|
16136
|
-
surface: "#ffffff",
|
|
16137
|
-
"neutral-50": "#f9fafb",
|
|
16138
|
-
"neutral-900": "#111827",
|
|
16139
|
-
primary: "#2563eb",
|
|
16140
|
-
"primary-50": "#eff6ff",
|
|
16141
|
-
"primary-500": "#2563eb",
|
|
16142
|
-
"primary-900": "#1d4ed8",
|
|
16143
|
-
secondary: "#9333ea",
|
|
16144
|
-
"secondary-50": "#f3e8ff",
|
|
16145
|
-
"secondary-500": "#9333ea",
|
|
16146
|
-
"secondary-900": "#4c1d95",
|
|
16147
|
-
accent: "#f97316",
|
|
16148
|
-
"accent-50": "#fff7ed",
|
|
16149
|
-
"accent-500": "#f97316",
|
|
16150
|
-
"accent-900": "#7c2d12"
|
|
16151
|
-
}
|
|
16152
|
-
};
|
|
16153
|
-
/**
|
|
16154
|
-
* Project the expanded semantic palette into the token buckets consumed by
|
|
16155
|
-
* runtime transforms. Keep this mapping shared by full themes and the
|
|
16156
|
-
* tokens-only hosted-booking runtime so the two paths cannot drift.
|
|
16157
|
-
*/
|
|
16158
|
-
function buildThemeTokens(palette) {
|
|
16159
|
-
const tokens = cloneThemeTokens(defaultThemeTokens);
|
|
16160
|
-
const paletteStrings = {};
|
|
16161
|
-
for (const [key, value] of Object.entries(palette)) {
|
|
16162
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
16163
|
-
paletteStrings[key] = value;
|
|
16164
|
-
}
|
|
16165
|
-
}
|
|
16166
|
-
tokens.palette = paletteStrings;
|
|
16167
|
-
const resolveColor = (...keys) => {
|
|
16168
|
-
for (const key of keys) {
|
|
16169
|
-
const value = paletteStrings[key];
|
|
16170
|
-
if (value) return value;
|
|
16171
|
-
}
|
|
16172
|
-
return undefined;
|
|
16173
|
-
};
|
|
16174
|
-
const assignColor = (token, ...keys) => {
|
|
16175
|
-
const value = resolveColor(...keys);
|
|
16176
|
-
if (value) tokens.color[token] = value;
|
|
16177
|
-
};
|
|
16178
|
-
assignColor("background/base", "background", "surface", "primary-50");
|
|
16179
|
-
assignColor("background/elevated", "surface", "background");
|
|
16180
|
-
assignColor("surface/base", "surface", "background");
|
|
16181
|
-
assignColor("surface/subtle", "neutral-100", "surface", "background");
|
|
16182
|
-
assignColor("surface/inverted", "neutral-900", "text", "primary-900");
|
|
16183
|
-
assignColor("content/primary", "text", "body", "neutral-900");
|
|
16184
|
-
assignColor("content/secondary", "mutedText", "secondary", "neutral-600");
|
|
16185
|
-
assignColor("accent/primary", "primary", "accent");
|
|
16186
|
-
assignColor("accent/secondary", "accent", "secondary", "primary-400");
|
|
16187
|
-
assignColor("border/base", "border", "neutral-300");
|
|
16188
|
-
return tokens;
|
|
16189
|
-
}
|
|
16190
|
-
function cloneThemeTokens(source) {
|
|
16191
|
-
return JSON.parse(JSON.stringify(source));
|
|
16192
|
-
}
|
|
16193
|
-
|
|
16194
16390
|
//#endregion
|
|
16195
16391
|
//#region ../theme-core/src/site-styles/backgroundAppearance.ts
|
|
16196
16392
|
const backgroundAppearanceQuickColorTokenNames = [
|
|
@@ -28373,6 +28569,10 @@ function compareCompiledHeaders(left, right) {
|
|
|
28373
28569
|
|
|
28374
28570
|
//#endregion
|
|
28375
28571
|
//#region ../theme-core/src/site-styles/headerOpeningForeground.ts
|
|
28572
|
+
/** The nav-link hover effect that is a plain colour change. */
|
|
28573
|
+
const TEXT_COLOR_CHANGE_EFFECT_ID = "text-color-change";
|
|
28574
|
+
/** The nav-link hover affordance that does not depend on a colour delta. */
|
|
28575
|
+
const NAV_UNDERLINE_EFFECT_ID = "nav-underline";
|
|
28376
28576
|
function headerStyleAvailability(recipe, context) {
|
|
28377
28577
|
if (!recipe.openingForeground) return { kind: "available" };
|
|
28378
28578
|
return context?.openingForeground?.kind === "known" ? { kind: "available" } : {
|
|
@@ -28399,10 +28599,7 @@ function applyOpeningForeground(theme, header, recipe, context) {
|
|
|
28399
28599
|
return {
|
|
28400
28600
|
...withBrandForeground,
|
|
28401
28601
|
navColor: foreground.roles.text,
|
|
28402
|
-
navLinkStyle: header.navLinkStyle ?
|
|
28403
|
-
...header.navLinkStyle,
|
|
28404
|
-
colorToken: foreground.roles.link
|
|
28405
|
-
} : header.navLinkStyle,
|
|
28602
|
+
navLinkStyle: header.navLinkStyle ? adaptNavLinkStyleToForeground(header.navLinkStyle, foreground.roles.link, planForegroundHover(foreground.roles.link, paletteHexForToken(theme, foreground.roles.link), scrollTreatment?.kind === "contrast-glass" ? scrollTreatment.backgroundTone : null)) : header.navLinkStyle,
|
|
28406
28603
|
border: {
|
|
28407
28604
|
style: "solid",
|
|
28408
28605
|
width: "thin",
|
|
@@ -28412,6 +28609,79 @@ function applyOpeningForeground(theme, header, recipe, context) {
|
|
|
28412
28609
|
...scrollTreatment ? { scrollTreatment } : {}
|
|
28413
28610
|
};
|
|
28414
28611
|
}
|
|
28612
|
+
/**
|
|
28613
|
+
* Give the resolved foreground both the resting and the hover colour.
|
|
28614
|
+
*
|
|
28615
|
+
* Adapting the resting colour alone left hover as whatever the recipe wrote —
|
|
28616
|
+
* for `minimal-surface` a bare `primary`, which is a brand colour no one had
|
|
28617
|
+
* checked against the section the header floats over. On a primary-painted
|
|
28618
|
+
* opening section that resolves to the section's own colour, and the link
|
|
28619
|
+
* disappears on hover.
|
|
28620
|
+
*
|
|
28621
|
+
* The two colours are a pair: the scroll treatment picks its glass tone from
|
|
28622
|
+
* the resting foreground precisely so one colour reads both against the opening
|
|
28623
|
+
* section and against the glass. A hover colour outside that pairing is not
|
|
28624
|
+
* covered by either half of it.
|
|
28625
|
+
*
|
|
28626
|
+
* A pill palette is left alone. It owns hover through `background-on-hover`,
|
|
28627
|
+
* which carries its own paired background and foreground tokens, so hover
|
|
28628
|
+
* legibility there does not depend on the surface underneath at all.
|
|
28629
|
+
*/
|
|
28630
|
+
function adaptNavLinkStyleToForeground(navLinkStyle, linkToken, hoverPlan) {
|
|
28631
|
+
const adapted = {
|
|
28632
|
+
...navLinkStyle,
|
|
28633
|
+
colorToken: linkToken
|
|
28634
|
+
};
|
|
28635
|
+
const hoverEffects = navLinkStyle.effects?.hover ?? [];
|
|
28636
|
+
const hasColorChange = hoverEffects.some(isTextColorChangeEffect);
|
|
28637
|
+
if (!hasColorChange && hoverEffects.length > 0) return adapted;
|
|
28638
|
+
if (!hasColorChange && !navLinkStyle.hoverColorToken) return adapted;
|
|
28639
|
+
const underline = {
|
|
28640
|
+
effectId: NAV_UNDERLINE_EFFECT_ID,
|
|
28641
|
+
options: {
|
|
28642
|
+
style: "grow",
|
|
28643
|
+
colorToken: linkToken
|
|
28644
|
+
}
|
|
28645
|
+
};
|
|
28646
|
+
const withoutColorChange = hoverEffects.filter((effect) => !isTextColorChangeEffect(effect) && !isNavUnderlineEffect(effect));
|
|
28647
|
+
if (hoverPlan.kind === "resting") {
|
|
28648
|
+
return {
|
|
28649
|
+
...adapted,
|
|
28650
|
+
hoverColorToken: null,
|
|
28651
|
+
effects: {
|
|
28652
|
+
...adapted.effects,
|
|
28653
|
+
hover: [...withoutColorChange, underline]
|
|
28654
|
+
}
|
|
28655
|
+
};
|
|
28656
|
+
}
|
|
28657
|
+
const hoverColorToken = hoverPlan.token;
|
|
28658
|
+
return {
|
|
28659
|
+
...adapted,
|
|
28660
|
+
hoverColorToken,
|
|
28661
|
+
effects: {
|
|
28662
|
+
...adapted.effects,
|
|
28663
|
+
hover: [...hoverEffects.map((effect) => isTextColorChangeEffect(effect) ? {
|
|
28664
|
+
...effect,
|
|
28665
|
+
options: {
|
|
28666
|
+
...effect.options,
|
|
28667
|
+
hoverColorToken
|
|
28668
|
+
}
|
|
28669
|
+
} : effect).filter((effect) => !isNavUnderlineEffect(effect)), underline]
|
|
28670
|
+
}
|
|
28671
|
+
};
|
|
28672
|
+
}
|
|
28673
|
+
function isTextColorChangeEffect(effect) {
|
|
28674
|
+
return effect.effectId === TEXT_COLOR_CHANGE_EFFECT_ID;
|
|
28675
|
+
}
|
|
28676
|
+
function isNavUnderlineEffect(effect) {
|
|
28677
|
+
return effect.effectId === NAV_UNDERLINE_EFFECT_ID;
|
|
28678
|
+
}
|
|
28679
|
+
/** Concrete hex behind a plain palette token, or null when it is not one. */
|
|
28680
|
+
function paletteHexForToken(theme, token) {
|
|
28681
|
+
const parts = parseColorTokenParts(token);
|
|
28682
|
+
if (parts.kind !== "token" || parts.opacityPercent !== null) return null;
|
|
28683
|
+
return expandPalette(theme.palette)[parts.token] ?? null;
|
|
28684
|
+
}
|
|
28415
28685
|
function compileHeaderScrollTreatment(theme, recipe, foregroundToken) {
|
|
28416
28686
|
if (recipe.scrollTreatment !== "contrast-glass") return undefined;
|
|
28417
28687
|
const tokenParts = parseColorTokenParts(foregroundToken);
|
|
@@ -28458,6 +28728,31 @@ function applyPageOpeningForegroundToTheme(theme, recipe, context) {
|
|
|
28458
28728
|
};
|
|
28459
28729
|
}
|
|
28460
28730
|
|
|
28731
|
+
//#endregion
|
|
28732
|
+
//#region ../theme-core/src/site-styles/effectivePageDesign.ts
|
|
28733
|
+
/**
|
|
28734
|
+
* Every page with an applicable site style has an effective design, whether or
|
|
28735
|
+
* not a design version was ever persisted. Missing persistence is ordinary
|
|
28736
|
+
* default resolution, not a failure.
|
|
28737
|
+
*
|
|
28738
|
+
* Corrupt persisted state never reaches this function: callers keep their own
|
|
28739
|
+
* invalid state untouched and only resolve `stored` values they could parse.
|
|
28740
|
+
*/
|
|
28741
|
+
function resolveEffectivePageDesignIntent(stored, currentSiteStyleId) {
|
|
28742
|
+
if (stored) {
|
|
28743
|
+
return {
|
|
28744
|
+
kind: "resolved",
|
|
28745
|
+
intent: currentSiteStyleId ? rebasePageDesignIntentToSiteStyle(stored, currentSiteStyleId) : stored,
|
|
28746
|
+
source: "stored"
|
|
28747
|
+
};
|
|
28748
|
+
}
|
|
28749
|
+
return currentSiteStyleId ? {
|
|
28750
|
+
kind: "resolved",
|
|
28751
|
+
intent: buildInitialPolicyPageDesignIntent(currentSiteStyleId),
|
|
28752
|
+
source: "default"
|
|
28753
|
+
} : { kind: "no-site-style" };
|
|
28754
|
+
}
|
|
28755
|
+
|
|
28461
28756
|
//#endregion
|
|
28462
28757
|
//#region ../theme-core/src/site-styles/pageDesignAuthority.ts
|
|
28463
28758
|
const nonEmptyStringSchema = z.string().min(1);
|
|
@@ -32553,4 +32848,4 @@ function parseSdkEventRunUpsertInput(input, runIndex, options) {
|
|
|
32553
32848
|
}
|
|
32554
32849
|
|
|
32555
32850
|
//#endregion
|
|
32556
|
-
export {
|
|
32851
|
+
export { generateBaseInteractiveCSS as $, createContentEntryRef as $n, NAV_LINK_LETTER_SPACING_MAP as $t, getGradientPreset as A, featuresFromHex as An, resolveTokenOrCssColorValue as At, generateCssVars as B, normalizeCardRole as Bn, featureCardCompositionTreatmentValues as Bt, retargetCompiledBoundaryRuntimeForContentAdjacency as C, resolveSurfaceForeground as Cn, MQ_SM_DOWN as Ct, compileTransitionRuntime as D, defaultThemeTokens as Dn, isCssColorLiteral as Dt, getCuratedSiteStyle as E, buildThemeTokens as En, headerVariantValues as Et, parseBackgroundAppearanceColorToken as F, surfaceForegroundStyle as Fn, defineBlockDesignCapability as Ft, resolvedButtonColourDeclarations as G, footerModeSchema as Gn, testimonialsCompositionTreatmentValues as Gt, PUBLIC_BUTTON_ROLES as H, mediaSchema as Hn, introCompositionTreatmentValues as Ht, defineBlockDesignMetadata as I, surfaceForegroundValue as In, defineFrameCapability as It, contextualButtonSurfaceStyles as J, unsafeAsOpaqueButtonColourToken as Jn, getComponentBorderWidthPx as Jt, resolvedButtonFocusDeclarations as K, VARIANT_ROLES as Kn, tileTreatmentValues as Kt, require_jsx_runtime as L, generateShadowValue$1 as Ln, defineNoBlockDesignCapability as Lt, boundaryRuntimeMetricsForTransition as M, oklchToHexGamut as Mn, asAppearancePresetId as Mt, backgroundAppearanceColorTokenNames as N, DEFAULT_SURFACE_TEXT_COLOR as Nn, asDesignBlockId as Nt, gradientToCss as O, ORDINARY_TEXT_MIN_CONTRAST_RATIO as On, resolveOnColorForThemeToken as Ot, backgroundAppearanceQuickColorTokenNames as P, surfaceForegroundDeclarations as Pn, assertNever$1 as Pt, generateEffectsCSS as Q, createCommandRef as Qn, NAV_LINK_BORDER_RADIUS_MAP as Qt, require_react as R, cardRoleSchema as Rn, eventListTreatmentValues as Rt, eventBookingOfferIntentSchema as S, textColorStyle as Sn, MQ_MD_DOWN as St, readHeaderNavFontSizePreference as T, onColorTokenName as Tn, MQ_TABLET_ONLY as Tt, generateButtonCss as U, mediaTypeDiscriminants as Un, makeBoundaryId as Ut, buttonStyleSelectionSchema as V, mediaIdentifierSchema as Vn, headingDecorationColorTokenValues as Vt, RESOLVED_BUTTON_DISABLED_DECLARATIONS as W, persistableMediaSchema as Wn, offeringsPreviewCompositionTreatmentValues as Wt, resolveContextualButtonRole as X, createBlockRef as Xn, parseBlockCustomCssRules as Xt, deriveButtonSurfaceContext as Y, parseBlockColorToken as Yn, getComponentShadowVar as Yt, generateDefaultButtonSystem as Z, createBlocksRef as Zn, parseCustomCssRules as Zt, entryIdentifierForCommand as _, backgroundColorStyle as _n, BREAKPOINT_LG as _t, classifySdkEventBookingOffer as a, INPUT_SPACING_MAP as an, createPageRef as ar, compileButtonColourStates as at, pagePathForCommand as b, mergeStyles as bn, BREAKPOINT_XL as bt, stableStringify as c, ACCORDION_SPACING_MAP as cn, refSlug as cr, expandPalette as ct, mapEventCommandContent as d, ICON_CONFIG as dn, boxBackgroundOverlaySchema as dt, NAV_LINK_PADDING_MAP as en, createEventCategoryRef as er, generateInteractiveChildInheritanceCSS as et, evaluateSiteCommandBatchExposure as f, ICON_SIZE_PX as fn, boxBackgroundSchema as ft, blockOrderIdentifiersForCommand as g, TokenResolver as gn, renderHeaderPaintTargetRules as gt, blockIdentifierForCommand as h, generateCardCss as hn, getHeaderPaintTargets as ht, classifySdkEventSchedule as i, INPUT_FONT_SIZE_MAP as in, createNavigationRef as ir, parseThemeForPersistence as it, parseGradientPresetId as j, hexToRgb as jn, pageDesignEditorBackgroundOverrideOwner as jt, GRADIENT_PRESET_IDS as k, contrastRatio as kn, resolveThemeColorReferenceValue as kt, createDryRunReport as l, ACCORDION_TRIGGER_FONT_SCALE as ln, slugPart as lr, getTextColorForBackground as lt, getSiteCommandMetadata as m, getDefaultCardVariants as mn, generateMobileOverlayBackdropFilterFix as mt, sdkEventOccurrenceInputSchema as n, NAV_LINK_TEXT_SIZE_MAP as nn, createFooterRef as nr, themeButtonClassName as nt, venueManagementFields as o, LABEL_FONT_SIZE_MAP as on, createSiteCommandSiteRef as or, formatButtonColourContractError as ot, evaluateSiteCommandExposure as p, generateCardCoreCss as pn, generateHeaderScrollTreatmentStyles as pt, contextualButtonSurfaceResetStyles as q, buttonVariantIdSchema as qn, visibleHeadingDecorationFamilyValues as qt, sdkEventUpsertOptionalFieldSchemas as r, FONT_WEIGHT_MAP$1 as rn, createFormRef as rr, themeButtonClassNameWithTrailingIcon as rt, sortRecord as s, ACCORDION_OPEN_ACCENT_THICKNESS as sn, createVenueRef as sr, generateShadowValue as st, classifySdkEventTiming as t, NAV_LINK_PADDING_X_MAP as tn, createEventSeriesRef as tr, parseThemeButtonVariantId as tt, parseAndPlanCommandBatchForTypes as u, ACCORDION_TRIGGER_LETTER_SPACING as un, __toESM as ur, themeSchema as ut, pageIdentifierForBlockCommand as v, borderColorStyle as vn, BREAKPOINT_MD as vt, mergeHeaderNavTypographyPreference as w, isOnColorBackgroundCandidateToken as wn, MQ_SM_UP as wt, hasConfiguredBookingVertical as x, rgbColorValue as xn, MQ_LG_UP as xt, pageIdentifierForCommand as y, cssVarValue as yn, BREAKPOINT_SM as yt, pickShadeTowardBackground as z, cardVariantRoleValues as zn, eyebrowTreatmentValues as zt };
|