riverbankcms 0.11.0 → 0.13.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 +31164 -30936
- package/dist/cli/init-docs/content/cli-reference.md +8 -1
- package/dist/cli/init-docs/content/content-management.md +7 -1
- package/dist/cli/init-docs/content/workflow-add-block.md +6 -0
- package/dist/cli/init-docs/content/workflow-create-page.md +6 -0
- package/dist/cli/init-docs/content/workflow-editor-workflows.md +2 -2
- package/dist/{eventUpsertInputSchema-DFPgv4ay.mjs → eventUpsertInputSchema-XM4oaNoq.mjs} +37 -85
- package/dist/load-config-CoGYSr5z.mjs +4 -0
- package/dist/{load-config-B7AiZalH.mjs → load-config-DidU5HfW.mjs} +2 -2
- package/dist/{local-sync-7ecbT9He.mjs → local-sync-DQcLxBg3.mjs} +68 -26
- package/dist/{plan-BIwseF-X.mjs → plan-mb_YslEh.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-CRQseim3.mjs → sdk-event-upsert-D48eLVOi.mjs} +881 -495
- package/dist/{src-CiZrHLCj.mjs → src-CxEAuQpf.mjs} +303 -270
- package/dist/testing.mjs +3 -3
- package/package.json +1 -1
- package/dist/load-config-ELWKxyb5.mjs +0 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as footerNavigationLayouts, F as isPresent, J as parseUtcIsoDateTimeString, N as isCanonicalRichTextDoc, Q as parseBookingTimeZone, Ut as err, Wt as ok, b as stableJsonStringify, dt as assertNever, j as parseFooterCompositionV1, l as EVENT_PRESET_VALUES, n as NAVIGATION_LOCATIONS, s as resolveSiteVerticalBookingConfig, u as ORDINARY_EVENT_PRESET_VALUES } from "./src-
|
|
2
|
+
import { A as footerNavigationLayouts, F as isPresent, J as parseUtcIsoDateTimeString, N as isCanonicalRichTextDoc, Q as parseBookingTimeZone, Ut as err, Wt as ok, b as stableJsonStringify, dt as assertNever, j as parseFooterCompositionV1, l as EVENT_PRESET_VALUES, n as NAVIGATION_LOCATIONS, s as resolveSiteVerticalBookingConfig, u as ORDINARY_EVENT_PRESET_VALUES } from "./src-CxEAuQpf.mjs";
|
|
3
3
|
import "node:module";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -2096,21 +2096,13 @@ function parseThemeColorToken(value) {
|
|
|
2096
2096
|
}
|
|
2097
2097
|
const themeColorTokenSchema = z.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
|
|
2098
2098
|
|
|
2099
|
-
//#endregion
|
|
2100
|
-
//#region ../theme-core/src/surfaces/foregroundRoles.ts
|
|
2101
|
-
const SURFACE_FOREGROUND_ROLES = [
|
|
2102
|
-
"text",
|
|
2103
|
-
"heading",
|
|
2104
|
-
"link",
|
|
2105
|
-
"supporting"
|
|
2106
|
-
];
|
|
2107
|
-
|
|
2108
2099
|
//#endregion
|
|
2109
2100
|
//#region ../theme-core/src/surfaces/foregroundContract.ts
|
|
2110
2101
|
const SURFACE_FOREGROUND_CSS_VARS = {
|
|
2111
2102
|
text: "--rb-surface-text-color",
|
|
2112
2103
|
heading: "--rb-surface-heading-color",
|
|
2113
2104
|
link: "--rb-surface-link-color",
|
|
2105
|
+
linkHover: "--rb-surface-link-hover-color",
|
|
2114
2106
|
supporting: "--rb-surface-supporting-color"
|
|
2115
2107
|
};
|
|
2116
2108
|
const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
|
|
@@ -2120,6 +2112,7 @@ const DEFAULT_ROLE_FALLBACKS = {
|
|
|
2120
2112
|
text: DEFAULT_SURFACE_TEXT_COLOR,
|
|
2121
2113
|
heading: DEFAULT_SURFACE_TEXT_COLOR,
|
|
2122
2114
|
link: DEFAULT_SURFACE_LINK_COLOR,
|
|
2115
|
+
linkHover: DEFAULT_SURFACE_LINK_COLOR,
|
|
2123
2116
|
supporting: DEFAULT_SURFACE_SUPPORTING_COLOR
|
|
2124
2117
|
};
|
|
2125
2118
|
function requireSurfaceColorValue(role, value) {
|
|
@@ -2131,10 +2124,12 @@ function requireSurfaceColorValue(role, value) {
|
|
|
2131
2124
|
}
|
|
2132
2125
|
function completeSurfaceForegrounds(input) {
|
|
2133
2126
|
const text = requireSurfaceColorValue("text", input.text);
|
|
2127
|
+
const link = input.link === undefined ? text : requireSurfaceColorValue("link", input.link);
|
|
2134
2128
|
return {
|
|
2135
2129
|
text,
|
|
2136
2130
|
heading: input.heading === undefined ? text : requireSurfaceColorValue("heading", input.heading),
|
|
2137
|
-
link
|
|
2131
|
+
link,
|
|
2132
|
+
linkHover: input.linkHover === undefined ? link : requireSurfaceColorValue("linkHover", input.linkHover),
|
|
2138
2133
|
supporting: input.supporting === undefined ? text : requireSurfaceColorValue("supporting", input.supporting)
|
|
2139
2134
|
};
|
|
2140
2135
|
}
|
|
@@ -2144,6 +2139,9 @@ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FAL
|
|
|
2144
2139
|
if (role === "text") {
|
|
2145
2140
|
return `var(${roleVariable}, ${fallback})`;
|
|
2146
2141
|
}
|
|
2142
|
+
if (role === "linkHover") {
|
|
2143
|
+
return `var(${roleVariable}, ${surfaceForegroundValue("link", fallback)})`;
|
|
2144
|
+
}
|
|
2147
2145
|
return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
|
|
2148
2146
|
}
|
|
2149
2147
|
function surfaceForegroundValueWithOpacity(role, opacityPercent) {
|
|
@@ -2159,116 +2157,27 @@ function surfaceForegroundStyle(input) {
|
|
|
2159
2157
|
[SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
|
|
2160
2158
|
[SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
|
|
2161
2159
|
[SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
|
|
2162
|
-
[SURFACE_FOREGROUND_CSS_VARS.
|
|
2160
|
+
[SURFACE_FOREGROUND_CSS_VARS.linkHover]: foregrounds.linkHover,
|
|
2161
|
+
[SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting,
|
|
2162
|
+
"--rb-surface-link-hover-decoration": hoverDecoration(input)
|
|
2163
2163
|
};
|
|
2164
2164
|
}
|
|
2165
2165
|
function surfaceForegroundDeclarations(input) {
|
|
2166
|
-
|
|
2167
|
-
return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
//#endregion
|
|
2171
|
-
//#region ../theme-core/src/surfaces/foregroundInteraction.ts
|
|
2172
|
-
/**
|
|
2173
|
-
* How far a hover state softens away from its resting foreground, as a
|
|
2174
|
-
* percentage of full opacity.
|
|
2175
|
-
*
|
|
2176
|
-
* Softening — rather than brightening — is forced by the token vocabulary
|
|
2177
|
-
* rather than chosen for taste. A resolved foreground is frequently an
|
|
2178
|
-
* `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
|
|
2179
|
-
* with no shade ramp: over a primary-painted section the resolved text role
|
|
2180
|
-
* is `onPrimary`, and white has no headroom left to move further from its
|
|
2181
|
-
* background. Moving *toward* the mid-range always has headroom, whatever
|
|
2182
|
-
* the resting colour, and it keeps the hue exactly.
|
|
2183
|
-
*
|
|
2184
|
-
* 85% is the measured floor, not a round number. Softening costs contrast, and
|
|
2185
|
-
* the binding case is a scrolled overlay header: `contrast-glass` paints 60%
|
|
2186
|
-
* black or white, which composites the backdrop into a known band whatever the
|
|
2187
|
-
* page content underneath happens to be. The lightest backdrop a dark glass can
|
|
2188
|
-
* present is 60% black over white, `#666666`; a white foreground softened to
|
|
2189
|
-
* 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
|
|
2190
|
-
* 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
|
|
2191
|
-
* the large-text 3:1.
|
|
2192
|
-
*
|
|
2193
|
-
* The mirror case — a light glass over dark content, `#999999` — cannot be met
|
|
2194
|
-
* by any softening, because the *resting* colour is already only about 4.6:1
|
|
2195
|
-
* there. That is a property of the glass rather than of this derivation, and
|
|
2196
|
-
* it wants fixing where the glass tone is chosen.
|
|
2197
|
-
*/
|
|
2198
|
-
const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
|
|
2199
|
-
/**
|
|
2200
|
-
* Derive the hover foreground that belongs with a resolved resting foreground.
|
|
2201
|
-
*
|
|
2202
|
-
* A resting foreground is chosen to be legible against a known surface. A hover
|
|
2203
|
-
* state derived from it stays within a bounded distance of that colour, so it
|
|
2204
|
-
* inherits most of the resting colour's contrast margin instead of asserting
|
|
2205
|
-
* an unrelated brand colour that no one checked against the surface.
|
|
2206
|
-
*
|
|
2207
|
-
* That bounded-distance property is what lets this be used on a transparent
|
|
2208
|
-
* overlay header, where the backdrop is only partly knowable: the scrolled
|
|
2209
|
-
* state composites a 60% glass tint over arbitrary page content, so no
|
|
2210
|
-
* compile-time contrast proof is available there. Staying near a colour that
|
|
2211
|
-
* is already trusted in both states is a weaker claim than a contrast proof,
|
|
2212
|
-
* and it is one this function can actually keep.
|
|
2213
|
-
*
|
|
2214
|
-
* Returns `null` when the resting token is not a plain palette token — an
|
|
2215
|
-
* already-faded or raw value has no well-defined softening, and the caller
|
|
2216
|
-
* should leave hover alone rather than guess.
|
|
2217
|
-
*/
|
|
2218
|
-
function deriveForegroundHoverToken(restingToken) {
|
|
2219
|
-
const parts = parseColorTokenParts(restingToken);
|
|
2220
|
-
if (parts.kind !== "token") return null;
|
|
2221
|
-
if (parts.opacityPercent !== null) return null;
|
|
2222
|
-
return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
|
|
2223
|
-
}
|
|
2224
|
-
/**
|
|
2225
|
-
* The opacity `contrast-glass` paints at. Mirrors the value in
|
|
2226
|
-
* `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
|
|
2227
|
-
* point of the bound below is that it describes what that treatment can produce.
|
|
2228
|
-
*/
|
|
2229
|
-
const CONTRAST_GLASS_ALPHA = .6;
|
|
2230
|
-
/**
|
|
2231
|
-
* The extreme backdrop a glass tone can present, whatever page content happens
|
|
2232
|
-
* to be underneath it.
|
|
2233
|
-
*
|
|
2234
|
-
* A 60% tint clamps the composite into a known band, which is what makes a
|
|
2235
|
-
* conservative check possible at compile time even though the content passing
|
|
2236
|
-
* under a sticky header is not knowable. For a dark glass the composite is
|
|
2237
|
-
* lightest over white; for a light glass it is darkest over black. Those are
|
|
2238
|
-
* the two ends that a foreground has to survive.
|
|
2239
|
-
*/
|
|
2240
|
-
function worstCaseGlassBackdrop(tone) {
|
|
2241
|
-
const [over, tint] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
|
|
2242
|
-
return compositeHex$1(tint, over, CONTRAST_GLASS_ALPHA);
|
|
2166
|
+
return Object.entries(surfaceForegroundStyle(input)).map(([property, value]) => `${property}: ${value};`);
|
|
2243
2167
|
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2168
|
+
function surfaceForegroundInputFromResolved(foreground, color) {
|
|
2169
|
+
const { roles } = foreground;
|
|
2170
|
+
return {
|
|
2171
|
+
text: color(roles.text),
|
|
2172
|
+
heading: color(roles.heading),
|
|
2173
|
+
link: color(roles.link),
|
|
2174
|
+
linkHover: color(roles.linkHover),
|
|
2175
|
+
supporting: color(roles.supporting),
|
|
2176
|
+
hoverDecoration: foreground.hoverDecoration
|
|
2177
|
+
};
|
|
2249
2178
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
*
|
|
2253
|
-
* `glassTone` is the scroll treatment's compiled tone, or `null` when the style
|
|
2254
|
-
* has no glass. Without one there is no bound on what the header can end up
|
|
2255
|
-
* over, so this declines to soften rather than guess.
|
|
2256
|
-
*
|
|
2257
|
-
* The light-glass end cannot be satisfied by any softening: against `#999999`
|
|
2258
|
-
* a dark resting foreground is already close to the 4.5:1 line, so every step
|
|
2259
|
-
* away from it fails. That is why this returns a plan rather than a colour —
|
|
2260
|
-
* the caller has to have a non-colour affordance to fall back on.
|
|
2261
|
-
*/
|
|
2262
|
-
function planForegroundHover(restingToken, restingHex, glassTone) {
|
|
2263
|
-
const softened = deriveForegroundHoverToken(restingToken);
|
|
2264
|
-
if (!softened) return { kind: "resting" };
|
|
2265
|
-
if (!restingHex || !glassTone) return { kind: "resting" };
|
|
2266
|
-
const backdrop = worstCaseGlassBackdrop(glassTone);
|
|
2267
|
-
const softenedHex = compositeHex$1(restingHex, backdrop, FOREGROUND_HOVER_OPACITY_PERCENT / 100);
|
|
2268
|
-
return contrastRatio(softenedHex, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO ? {
|
|
2269
|
-
kind: "softened",
|
|
2270
|
-
token: softened
|
|
2271
|
-
} : { kind: "resting" };
|
|
2179
|
+
function hoverDecoration(input) {
|
|
2180
|
+
return input.hoverDecoration ?? "underline";
|
|
2272
2181
|
}
|
|
2273
2182
|
|
|
2274
2183
|
//#endregion
|
|
@@ -2446,11 +2355,11 @@ function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
|
|
|
2446
2355
|
const parts = parseColorTokenParts(reference);
|
|
2447
2356
|
if (parts.kind === "token" && parts.opacityPercent !== null) {
|
|
2448
2357
|
const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
|
|
2449
|
-
return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
|
|
2358
|
+
return foregroundHex ? compositeHex$1(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
|
|
2450
2359
|
}
|
|
2451
2360
|
const raw = parts.kind === "raw" ? parts.value : reference;
|
|
2452
2361
|
const rgba = parseRgba(raw);
|
|
2453
|
-
return rgba ? compositeHex(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
|
|
2362
|
+
return rgba ? compositeHex$1(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
|
|
2454
2363
|
}
|
|
2455
2364
|
/** Resolve a colour reference only when it is already fully opaque. */
|
|
2456
2365
|
function resolveConcreteColorHex(reference, palette) {
|
|
@@ -2466,7 +2375,7 @@ function resolveConcreteColorHex(reference, palette) {
|
|
|
2466
2375
|
if (literal.toLowerCase() === "white") return "#ffffff";
|
|
2467
2376
|
return normalizePaletteHex(literal);
|
|
2468
2377
|
}
|
|
2469
|
-
function compositeHex(foregroundHex, underlayHex, alpha) {
|
|
2378
|
+
function compositeHex$1(foregroundHex, underlayHex, alpha) {
|
|
2470
2379
|
const foreground = hexToRgb(foregroundHex);
|
|
2471
2380
|
const underlay = hexToRgb(underlayHex);
|
|
2472
2381
|
const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
|
|
@@ -2500,6 +2409,134 @@ function isRgbChannel(value) {
|
|
|
2500
2409
|
return Number.isFinite(value) && value >= 0 && value <= 255;
|
|
2501
2410
|
}
|
|
2502
2411
|
|
|
2412
|
+
//#endregion
|
|
2413
|
+
//#region ../theme-core/src/surfaces/foregroundInteraction.ts
|
|
2414
|
+
/**
|
|
2415
|
+
* How far a hover state softens away from its resting foreground, as a
|
|
2416
|
+
* percentage of full opacity.
|
|
2417
|
+
*
|
|
2418
|
+
* Softening — rather than brightening — is forced by the token vocabulary
|
|
2419
|
+
* rather than chosen for taste. A resolved foreground is frequently an
|
|
2420
|
+
* `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
|
|
2421
|
+
* with no shade ramp: over a primary-painted section the resolved text role
|
|
2422
|
+
* is `onPrimary`, and white has no headroom left to move further from its
|
|
2423
|
+
* background. Moving *toward* the mid-range always has headroom, whatever
|
|
2424
|
+
* the resting colour, and it keeps the hue exactly.
|
|
2425
|
+
*
|
|
2426
|
+
* 85% is the measured floor, not a round number. Softening costs contrast, and
|
|
2427
|
+
* the binding case is a scrolled overlay header: `contrast-glass` paints 60%
|
|
2428
|
+
* black or white, which composites the backdrop into a known band whatever the
|
|
2429
|
+
* page content underneath happens to be. The lightest backdrop a dark glass can
|
|
2430
|
+
* present is 60% black over white, `#666666`; a white foreground softened to
|
|
2431
|
+
* 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
|
|
2432
|
+
* 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
|
|
2433
|
+
* the large-text 3:1.
|
|
2434
|
+
*
|
|
2435
|
+
* The mirror case — a light glass over dark content, `#999999` — cannot be met
|
|
2436
|
+
* by any softening, because the *resting* colour is already only about 4.6:1
|
|
2437
|
+
* there. That is a property of the glass rather than of this derivation, and
|
|
2438
|
+
* it wants fixing where the glass tone is chosen.
|
|
2439
|
+
*/
|
|
2440
|
+
const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
|
|
2441
|
+
/**
|
|
2442
|
+
* Derive the hover foreground that belongs with a resolved resting foreground.
|
|
2443
|
+
*
|
|
2444
|
+
* A resting foreground is chosen to be legible against a known surface. A hover
|
|
2445
|
+
* state derived from it stays within a bounded distance of that colour, so it
|
|
2446
|
+
* inherits most of the resting colour's contrast margin instead of asserting
|
|
2447
|
+
* an unrelated brand colour that no one checked against the surface.
|
|
2448
|
+
*
|
|
2449
|
+
* That bounded-distance property is what lets this be used on a transparent
|
|
2450
|
+
* overlay header, where the backdrop is only partly knowable: the scrolled
|
|
2451
|
+
* state composites a 60% glass tint over arbitrary page content, so no
|
|
2452
|
+
* compile-time contrast proof is available there. Staying near a colour that
|
|
2453
|
+
* is already trusted in both states is a weaker claim than a contrast proof,
|
|
2454
|
+
* and it is one this function can actually keep.
|
|
2455
|
+
*
|
|
2456
|
+
* Returns `null` when the resting token is not a plain palette token — an
|
|
2457
|
+
* already-faded or raw value has no well-defined softening, and the caller
|
|
2458
|
+
* should leave hover alone rather than guess.
|
|
2459
|
+
*/
|
|
2460
|
+
function deriveForegroundHoverToken(restingToken) {
|
|
2461
|
+
const parts = parseColorTokenParts(restingToken);
|
|
2462
|
+
if (parts.kind !== "token") return null;
|
|
2463
|
+
if (parts.opacityPercent !== null) return null;
|
|
2464
|
+
return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* The opacity `contrast-glass` paints at. Mirrors the value in
|
|
2468
|
+
* `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
|
|
2469
|
+
* point of the bound below is that it describes what that treatment can produce.
|
|
2470
|
+
*/
|
|
2471
|
+
const CONTRAST_GLASS_ALPHA = .6;
|
|
2472
|
+
/**
|
|
2473
|
+
* The extreme backdrop a glass tone can present, whatever page content happens
|
|
2474
|
+
* to be underneath it.
|
|
2475
|
+
*
|
|
2476
|
+
* A 60% tint clamps the composite into a known band, which is what makes a
|
|
2477
|
+
* conservative check possible at compile time even though the content passing
|
|
2478
|
+
* under a sticky header is not knowable. For a dark glass the composite is
|
|
2479
|
+
* lightest over white; for a light glass it is darkest over black. Those are
|
|
2480
|
+
* the two ends that a foreground has to survive.
|
|
2481
|
+
*/
|
|
2482
|
+
function worstCaseGlassBackdrop(tone) {
|
|
2483
|
+
const [over, tint] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
|
|
2484
|
+
return compositeHex(tint, over, CONTRAST_GLASS_ALPHA);
|
|
2485
|
+
}
|
|
2486
|
+
/** Flatten `foreground` at `alpha` over `background` into an opaque hex. */
|
|
2487
|
+
function compositeHex(foreground, background, alpha) {
|
|
2488
|
+
const f = hexToRgb(foreground);
|
|
2489
|
+
const b = hexToRgb(background);
|
|
2490
|
+
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)));
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Prefer an authored hover that survives every measured backdrop, then a
|
|
2494
|
+
* checked softening of the resting colour. A null backdrop set represents a
|
|
2495
|
+
* trusted treatment: only an explicit authored hover may change the colour.
|
|
2496
|
+
* When neither option works, retain the resting colour and underline it.
|
|
2497
|
+
*/
|
|
2498
|
+
function planForegroundHover(input) {
|
|
2499
|
+
const { restingToken, candidate: candidate$1, palette, backdrops } = input;
|
|
2500
|
+
if (candidate$1 && candidate$1 !== restingToken) {
|
|
2501
|
+
if (backdrops === null || survivesEveryBackdrop(candidate$1, palette, backdrops)) {
|
|
2502
|
+
return {
|
|
2503
|
+
kind: "candidate",
|
|
2504
|
+
token: candidate$1,
|
|
2505
|
+
decoration: "initial"
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
const softened = deriveForegroundHoverToken(restingToken);
|
|
2510
|
+
if (softened && backdrops && survivesEveryBackdrop(softened, palette, backdrops)) {
|
|
2511
|
+
return {
|
|
2512
|
+
kind: "softened",
|
|
2513
|
+
token: softened,
|
|
2514
|
+
decoration: "initial"
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
return {
|
|
2518
|
+
kind: "resting",
|
|
2519
|
+
token: restingToken,
|
|
2520
|
+
decoration: "underline"
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
function survivesEveryBackdrop(token, palette, backdrops) {
|
|
2524
|
+
return backdrops.length > 0 && backdrops.every((backdrop) => {
|
|
2525
|
+
const foreground = resolveOpaqueSurfaceHex(token, palette, backdrop);
|
|
2526
|
+
return foreground !== null && contrastRatio(foreground, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO;
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
//#endregion
|
|
2531
|
+
//#region ../theme-core/src/surfaces/foregroundRoles.ts
|
|
2532
|
+
const SURFACE_FOREGROUND_ROLES = [
|
|
2533
|
+
"text",
|
|
2534
|
+
"heading",
|
|
2535
|
+
"link",
|
|
2536
|
+
"linkHover",
|
|
2537
|
+
"supporting"
|
|
2538
|
+
];
|
|
2539
|
+
|
|
2503
2540
|
//#endregion
|
|
2504
2541
|
//#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
|
|
2505
2542
|
const FALLBACK_TEXT_TOKENS = [
|
|
@@ -2550,12 +2587,16 @@ function resolveSurfaceForeground(background, palette, candidates = {}, options
|
|
|
2550
2587
|
}
|
|
2551
2588
|
function foregroundCandidates(background, overrides) {
|
|
2552
2589
|
return {
|
|
2553
|
-
text: overrides.text
|
|
2554
|
-
heading: overrides.heading
|
|
2555
|
-
link: overrides.link
|
|
2556
|
-
|
|
2590
|
+
text: candidateOrBackground(overrides.text, background?.textColor),
|
|
2591
|
+
heading: candidateOrBackground(overrides.heading, background?.headingColor),
|
|
2592
|
+
link: candidateOrBackground(overrides.link, background?.linkColor),
|
|
2593
|
+
linkHover: candidateOrBackground(overrides.linkHover),
|
|
2594
|
+
supporting: candidateOrBackground(overrides.supporting, background?.supportingColor)
|
|
2557
2595
|
};
|
|
2558
2596
|
}
|
|
2597
|
+
function candidateOrBackground(candidate$1, backgroundValue) {
|
|
2598
|
+
return candidate$1 ?? backgroundValue ?? null;
|
|
2599
|
+
}
|
|
2559
2600
|
function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
|
|
2560
2601
|
const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
|
|
2561
2602
|
if (!backgroundHex) {
|
|
@@ -2569,29 +2610,53 @@ function resolveDeterministicForeground(backgroundReference, candidates, palette
|
|
|
2569
2610
|
...FALLBACK_TEXT_TOKENS
|
|
2570
2611
|
], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []);
|
|
2571
2612
|
if (!text) return { kind: "unknown" };
|
|
2572
|
-
return {
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
};
|
|
2613
|
+
return knownForeground({
|
|
2614
|
+
text,
|
|
2615
|
+
heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
|
|
2616
|
+
link: safeTokenOr(candidates.link, text, backgroundHex, palette),
|
|
2617
|
+
supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
|
|
2618
|
+
}, {
|
|
2619
|
+
kind: "measured",
|
|
2620
|
+
backdropHex: backgroundHex
|
|
2621
|
+
}, candidates.linkHover, palette);
|
|
2581
2622
|
}
|
|
2582
2623
|
function resolveTrustedTreatmentForeground(candidates, palette) {
|
|
2583
2624
|
const text = canonicalToken(candidates.text, palette);
|
|
2584
2625
|
if (!text) return { kind: "unknown" };
|
|
2626
|
+
return knownForeground({
|
|
2627
|
+
text,
|
|
2628
|
+
heading: canonicalToken(candidates.heading, palette) ?? text,
|
|
2629
|
+
link: canonicalToken(candidates.link, palette) ?? text,
|
|
2630
|
+
supporting: canonicalToken(candidates.supporting, palette) ?? text
|
|
2631
|
+
}, { kind: "trusted-treatment" }, candidates.linkHover, palette);
|
|
2632
|
+
}
|
|
2633
|
+
function knownForeground(roles, provenance, hoverCandidate, palette) {
|
|
2634
|
+
const hover = planForegroundHover({
|
|
2635
|
+
restingToken: roles.link,
|
|
2636
|
+
candidate: canonicalHoverToken(hoverCandidate, palette),
|
|
2637
|
+
palette,
|
|
2638
|
+
backdrops: provenance.kind === "measured" ? [provenance.backdropHex] : null
|
|
2639
|
+
});
|
|
2585
2640
|
return {
|
|
2586
2641
|
kind: "known",
|
|
2642
|
+
provenance,
|
|
2587
2643
|
roles: {
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
}
|
|
2644
|
+
...roles,
|
|
2645
|
+
linkHover: hover.token
|
|
2646
|
+
},
|
|
2647
|
+
hoverDecoration: hover.decoration
|
|
2593
2648
|
};
|
|
2594
2649
|
}
|
|
2650
|
+
function canonicalHoverToken(reference, palette) {
|
|
2651
|
+
const token = parseThemeColorToken(reference);
|
|
2652
|
+
if (token) {
|
|
2653
|
+
const parts = parseColorTokenParts(token);
|
|
2654
|
+
if (parts.kind === "token" && resolveConcreteColorHex(parts.token, palette)) {
|
|
2655
|
+
return token;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
return canonicalToken(reference, palette);
|
|
2659
|
+
}
|
|
2595
2660
|
function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
|
|
2596
2661
|
return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
|
|
2597
2662
|
}
|
|
@@ -3591,6 +3656,226 @@ function resolveCardRole(theme, cardSystem, role) {
|
|
|
3591
3656
|
}
|
|
3592
3657
|
}
|
|
3593
3658
|
|
|
3659
|
+
//#endregion
|
|
3660
|
+
//#region ../theme-core/src/palette/generateShades.ts
|
|
3661
|
+
function generateShades(hexColor, name) {
|
|
3662
|
+
const shadeSteps = [
|
|
3663
|
+
50,
|
|
3664
|
+
100,
|
|
3665
|
+
200,
|
|
3666
|
+
300,
|
|
3667
|
+
400,
|
|
3668
|
+
500,
|
|
3669
|
+
600,
|
|
3670
|
+
700,
|
|
3671
|
+
800,
|
|
3672
|
+
900,
|
|
3673
|
+
950
|
|
3674
|
+
];
|
|
3675
|
+
const base = featuresFromHex(hexColor);
|
|
3676
|
+
const hue = base.okH;
|
|
3677
|
+
const baseChroma = base.okC;
|
|
3678
|
+
const toneMap = {
|
|
3679
|
+
50: .975,
|
|
3680
|
+
100: .945,
|
|
3681
|
+
200: .89,
|
|
3682
|
+
300: .82,
|
|
3683
|
+
400: .74,
|
|
3684
|
+
500: .66,
|
|
3685
|
+
600: .58,
|
|
3686
|
+
700: .49,
|
|
3687
|
+
800: .4,
|
|
3688
|
+
900: .28,
|
|
3689
|
+
950: .16
|
|
3690
|
+
};
|
|
3691
|
+
const shadeDefs = shadeSteps.map((step, idx) => {
|
|
3692
|
+
const t = idx / (shadeSteps.length - 1);
|
|
3693
|
+
const L = toneMap[step];
|
|
3694
|
+
const chromaFactor = .06 + .94 * Math.sin(Math.PI * t);
|
|
3695
|
+
const C = baseChroma * chromaFactor;
|
|
3696
|
+
return {
|
|
3697
|
+
name: `${name}-${step}`,
|
|
3698
|
+
hex: oklchToHexGamut(L, C, hue)
|
|
3699
|
+
};
|
|
3700
|
+
});
|
|
3701
|
+
shadeDefs.push({
|
|
3702
|
+
name,
|
|
3703
|
+
hex: hexColor
|
|
3704
|
+
});
|
|
3705
|
+
return shadeDefs;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
//#endregion
|
|
3709
|
+
//#region ../theme-core/src/palette/neutralScale.ts
|
|
3710
|
+
function generateNeutralScale({ background, steps = 11, warm = false }) {
|
|
3711
|
+
const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
|
|
3712
|
+
const shadeNames = [
|
|
3713
|
+
50,
|
|
3714
|
+
100,
|
|
3715
|
+
200,
|
|
3716
|
+
300,
|
|
3717
|
+
400,
|
|
3718
|
+
500,
|
|
3719
|
+
600,
|
|
3720
|
+
700,
|
|
3721
|
+
800,
|
|
3722
|
+
900,
|
|
3723
|
+
950
|
|
3724
|
+
];
|
|
3725
|
+
const count = Math.min(steps, shadeNames.length);
|
|
3726
|
+
const scale$1 = [];
|
|
3727
|
+
for (let i = 0; i < count; i++) {
|
|
3728
|
+
const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
|
|
3729
|
+
const t = i / (count - 1);
|
|
3730
|
+
const tone = isDark ? mix$1(.06, .92, t) : mix$1(.98, .1, t);
|
|
3731
|
+
const hex = oklchToHexGamut(tone, .02, warm ? 85 : 250);
|
|
3732
|
+
const name = `neutral-${shadeName}`;
|
|
3733
|
+
scale$1.push({
|
|
3734
|
+
name,
|
|
3735
|
+
hex
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
return scale$1;
|
|
3739
|
+
}
|
|
3740
|
+
function mix$1(a, b, t) {
|
|
3741
|
+
return a + (b - a) * Math.max(0, Math.min(1, t));
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
//#endregion
|
|
3745
|
+
//#region ../theme-core/src/palette/utils/textColor.ts
|
|
3746
|
+
/**
|
|
3747
|
+
* Pick the better of black/white text for a given background using actual
|
|
3748
|
+
* contrast ratios, not a simple luminance cutoff.
|
|
3749
|
+
*
|
|
3750
|
+
* @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
|
|
3751
|
+
* @param minContrast - Preferred minimum contrast ratio for body-sized text
|
|
3752
|
+
* @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
|
|
3753
|
+
*/
|
|
3754
|
+
function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
|
|
3755
|
+
const { r, g, b } = parseColorToRgb(background);
|
|
3756
|
+
const luminance = relativeLuminance(r, g, b);
|
|
3757
|
+
const blackContrast = (luminance + .05) / .05;
|
|
3758
|
+
const whiteContrast = 1.05 / (luminance + .05);
|
|
3759
|
+
if (blackContrast >= minContrast && whiteContrast < minContrast) {
|
|
3760
|
+
return "#000000";
|
|
3761
|
+
}
|
|
3762
|
+
if (whiteContrast >= minContrast && blackContrast < minContrast) {
|
|
3763
|
+
return "#FFFFFF";
|
|
3764
|
+
}
|
|
3765
|
+
return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
|
|
3766
|
+
}
|
|
3767
|
+
/**
|
|
3768
|
+
* Backwards-compatible alias for callers that want the best black/white text
|
|
3769
|
+
* color for a background.
|
|
3770
|
+
*/
|
|
3771
|
+
function getTextColorForBackground(background) {
|
|
3772
|
+
return pickBlackOrWhiteTextColor(background);
|
|
3773
|
+
}
|
|
3774
|
+
function parseColorToRgb(input) {
|
|
3775
|
+
const trimmed = input.trim();
|
|
3776
|
+
if (trimmed.startsWith("#")) {
|
|
3777
|
+
return hexToRgb(trimmed);
|
|
3778
|
+
}
|
|
3779
|
+
const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
|
|
3780
|
+
if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) {
|
|
3781
|
+
return {
|
|
3782
|
+
r: clamp255(Number(rgbMatch[1])),
|
|
3783
|
+
g: clamp255(Number(rgbMatch[2])),
|
|
3784
|
+
b: clamp255(Number(rgbMatch[3]))
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
throw new Error("Unsupported color format");
|
|
3788
|
+
}
|
|
3789
|
+
function clamp255(value) {
|
|
3790
|
+
if (Number.isNaN(value)) return 0;
|
|
3791
|
+
return Math.max(0, Math.min(255, value));
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
//#endregion
|
|
3795
|
+
//#region ../theme-core/src/palette/expandPalette.ts
|
|
3796
|
+
function colorsToMap$1(palette) {
|
|
3797
|
+
const map = {};
|
|
3798
|
+
for (const c of palette.colors) {
|
|
3799
|
+
map[c.name] = c.hex;
|
|
3800
|
+
}
|
|
3801
|
+
return map;
|
|
3802
|
+
}
|
|
3803
|
+
function isBackgroundDark(hex) {
|
|
3804
|
+
try {
|
|
3805
|
+
return featuresFromHex(hex).Y < .5;
|
|
3806
|
+
} catch {
|
|
3807
|
+
return false;
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
const nonShadeablePaletteRoleNames = new Set([
|
|
3811
|
+
"success",
|
|
3812
|
+
"warning",
|
|
3813
|
+
"destructive"
|
|
3814
|
+
]);
|
|
3815
|
+
/**
|
|
3816
|
+
* Status role aliases the persisted palette does not carry itself.
|
|
3817
|
+
*
|
|
3818
|
+
* Generators reference `--tb-error` (the site-style-owned input error border)
|
|
3819
|
+
* and `--tb-info` (`.alert-info`, `.status-info`, the field-note rule), but the
|
|
3820
|
+
* palette schema only defines `destructive` and has no `info` role at all.
|
|
3821
|
+
* Without these aliases those declarations name variables that are never
|
|
3822
|
+
* defined and browsers drop them. The SDK bridge resolves the same pair via
|
|
3823
|
+
* TOKEN_ALIASES, so both runtimes agree on what a status token means.
|
|
3824
|
+
*
|
|
3825
|
+
* Runs after the danger → destructive promotion above, so `destructive` is
|
|
3826
|
+
* already the single source of truth for the error colour.
|
|
3827
|
+
*/
|
|
3828
|
+
function statusAliasTokens(expanded) {
|
|
3829
|
+
const aliases = {};
|
|
3830
|
+
if (expanded.destructive) aliases.error = expanded.destructive;
|
|
3831
|
+
if (expanded.accent) aliases.info = expanded.accent;
|
|
3832
|
+
return aliases;
|
|
3833
|
+
}
|
|
3834
|
+
function expandPalette(palette) {
|
|
3835
|
+
const base = colorsToMap$1(palette);
|
|
3836
|
+
const expanded = { ...base };
|
|
3837
|
+
const background = base.background || base.surface || "#ffffff";
|
|
3838
|
+
const neutral$1 = generateNeutralScale({
|
|
3839
|
+
background,
|
|
3840
|
+
steps: 11,
|
|
3841
|
+
warm: false
|
|
3842
|
+
});
|
|
3843
|
+
neutral$1.forEach((n) => {
|
|
3844
|
+
expanded[n.name] = n.hex;
|
|
3845
|
+
});
|
|
3846
|
+
for (const [name, hex] of Object.entries(base)) {
|
|
3847
|
+
if (nonShadeablePaletteRoleNames.has(name)) continue;
|
|
3848
|
+
if (hex) {
|
|
3849
|
+
const shades = generateShades(hex, name);
|
|
3850
|
+
shades.forEach((shade) => {
|
|
3851
|
+
expanded[shade.name] = shade.hex;
|
|
3852
|
+
});
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
if (base.text) expanded.body = base.text;
|
|
3856
|
+
if (base.danger && !base.destructive) {
|
|
3857
|
+
expanded.destructive = base.danger;
|
|
3858
|
+
}
|
|
3859
|
+
Object.assign(expanded, statusAliasTokens(expanded));
|
|
3860
|
+
const isDark = base.background ? isBackgroundDark(base.background) : false;
|
|
3861
|
+
const isDarkMode = palette.meta?.mode === "dark" || isDark;
|
|
3862
|
+
expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
|
|
3863
|
+
for (const [token, hex] of Object.entries({ ...expanded })) {
|
|
3864
|
+
if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
|
|
3865
|
+
const onToken = onColorTokenName(token);
|
|
3866
|
+
if (expanded[onToken]) continue;
|
|
3867
|
+
expanded[onToken] = getTextColorForBackground(hex);
|
|
3868
|
+
}
|
|
3869
|
+
return expanded;
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3872
|
+
//#endregion
|
|
3873
|
+
//#region ../theme-core/src/utils/cssDeclarations.ts
|
|
3874
|
+
/** Render CSS declarations whether callers provide terminators or fragments. */
|
|
3875
|
+
function renderCssDeclarations(declarations) {
|
|
3876
|
+
return declarations.map((declaration) => declaration.endsWith(";") ? declaration : `${declaration};`).join("\n ");
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3594
3879
|
//#endregion
|
|
3595
3880
|
//#region ../theme-core/src/cards/generateCardCss.ts
|
|
3596
3881
|
const PER_CORNER_RADIUS_MAP$1 = {
|
|
@@ -3608,6 +3893,7 @@ function resolveCornerRadiiCss$1(cornerRadii, fallback) {
|
|
|
3608
3893
|
function generateCardCss(options) {
|
|
3609
3894
|
const { themeId, theme, cardSystem: providedCardSystem } = options;
|
|
3610
3895
|
const tokens = new TokenResolver(theme);
|
|
3896
|
+
const palette = expandPalette(theme.palette);
|
|
3611
3897
|
const cardSystem = providedCardSystem ?? generateDefaultCardSystem(theme);
|
|
3612
3898
|
const source = providedCardSystem ? "site-style" : "generated-baseline";
|
|
3613
3899
|
const aliasGroups = resolveRoleAliasGroups(theme, providedCardSystem ?? null);
|
|
@@ -3618,11 +3904,11 @@ function generateCardCss(options) {
|
|
|
3618
3904
|
const groupKey = roleAliasGroupKey(source, variant.id);
|
|
3619
3905
|
emittedGroupKeys.add(groupKey);
|
|
3620
3906
|
const aliasClasses = aliasGroups.get(groupKey)?.classes ?? [];
|
|
3621
|
-
cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme));
|
|
3907
|
+
cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme, palette));
|
|
3622
3908
|
}
|
|
3623
3909
|
for (const [groupKey, group] of aliasGroups) {
|
|
3624
3910
|
if (emittedGroupKeys.has(groupKey)) continue;
|
|
3625
|
-
cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme));
|
|
3911
|
+
cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme, palette));
|
|
3626
3912
|
}
|
|
3627
3913
|
return cssChunks.filter(Boolean).join("\n\n");
|
|
3628
3914
|
}
|
|
@@ -3685,7 +3971,7 @@ const MEDIA_CORNER_MAP = {
|
|
|
3685
3971
|
* This CSS-first approach eliminates runtime transforms for styling,
|
|
3686
3972
|
* keeping transforms only for data normalization and dynamic layout.
|
|
3687
3973
|
*/
|
|
3688
|
-
function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
|
|
3974
|
+
function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palette) {
|
|
3689
3975
|
const selectorsFor = (suffix) => cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}${suffix}`).join(",\n");
|
|
3690
3976
|
const containerSelectors = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}`);
|
|
3691
3977
|
const containerSelector = containerSelectors.join(",\n");
|
|
@@ -3693,21 +3979,7 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
|
|
|
3693
3979
|
const contentSelector = selectorsFor(" .card-content");
|
|
3694
3980
|
const fallbackBackgroundSelector = selectorsFor(" > .rb-bg-surface");
|
|
3695
3981
|
const rules = [];
|
|
3696
|
-
|
|
3697
|
-
const bgColor = tokens.getColor(variant.background.colorToken);
|
|
3698
|
-
rules.push(`background: ${bgColor}`);
|
|
3699
|
-
} else if (variant.background.type === "gradient") {
|
|
3700
|
-
const fromColor = tokens.getColor(variant.background.fromToken);
|
|
3701
|
-
const toColor = tokens.getColor(variant.background.toToken);
|
|
3702
|
-
const direction = GRADIENT_DIRECTION_MAP$2[variant.background.direction] || "to right";
|
|
3703
|
-
rules.push(`background: linear-gradient(${direction}, ${fromColor}, ${toColor})`);
|
|
3704
|
-
} else {
|
|
3705
|
-
rules.push(`background: transparent`);
|
|
3706
|
-
}
|
|
3707
|
-
if (variant.background.type !== "transparent") {
|
|
3708
|
-
const foreground = tokens.getColor(variant.foregroundToken ?? "text");
|
|
3709
|
-
rules.push(...surfaceForegroundDeclarations({ text: foreground }));
|
|
3710
|
-
}
|
|
3982
|
+
rules.push(...cardSurfaceRules(variant, tokens, theme, palette));
|
|
3711
3983
|
const resolveColor = (token) => tokens.getColor(token);
|
|
3712
3984
|
rules.push(...cardBorderDeclarations(variant.border ? borderDeclaration(variant.border, tokens) : null, variant.decorations, resolveColor));
|
|
3713
3985
|
const corners$1 = variant.corners ?? theme.corners;
|
|
@@ -3724,13 +3996,35 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
|
|
|
3724
3996
|
const contentRules = [`padding: ${resolveContentPadding(variant)}`];
|
|
3725
3997
|
return [
|
|
3726
3998
|
`${structureSelector} {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: visible;\n}`,
|
|
3727
|
-
`${containerSelector} {\n ${rules
|
|
3999
|
+
`${containerSelector} {\n ${renderCssDeclarations(rules)}\n}`,
|
|
3728
4000
|
...cardDecorationPseudoRules(containerSelectors, variant.decorations, resolveColor),
|
|
3729
4001
|
`${fallbackBackgroundSelector} {\n background: transparent;\n}`,
|
|
3730
4002
|
`${mediaSelector} {\n ${mediaRules.join(";\n ")};\n}`,
|
|
3731
4003
|
`${contentSelector} {\n ${contentRules.join(";\n ")};\n}`
|
|
3732
4004
|
].join("\n\n");
|
|
3733
4005
|
}
|
|
4006
|
+
function cardSurfaceRules(variant, tokens, theme, palette) {
|
|
4007
|
+
if (variant.background.type === "transparent") return ["background: transparent"];
|
|
4008
|
+
const background = variant.background.type === "solid" ? {
|
|
4009
|
+
type: "color",
|
|
4010
|
+
color: variant.background.colorToken,
|
|
4011
|
+
textColor: variant.foregroundToken ?? "text"
|
|
4012
|
+
} : {
|
|
4013
|
+
type: "gradient",
|
|
4014
|
+
textColor: variant.foregroundToken ?? "text"
|
|
4015
|
+
};
|
|
4016
|
+
const foreground = resolveSurfaceForeground(background, palette, { linkHover: theme.typography.proseLinkStyle?.hoverColorToken });
|
|
4017
|
+
return [cardBackgroundRule(variant.background, tokens), ...surfaceForegroundDeclarations(foreground.kind === "known" ? surfaceForegroundInputFromResolved(foreground, (token) => tokens.getColor(token)) : { text: tokens.getColor(variant.foregroundToken ?? "text") })];
|
|
4018
|
+
}
|
|
4019
|
+
function cardBackgroundRule(background, tokens) {
|
|
4020
|
+
if (background.type === "solid") {
|
|
4021
|
+
return `background: ${tokens.getColor(background.colorToken)}`;
|
|
4022
|
+
}
|
|
4023
|
+
const from = tokens.getColor(background.fromToken);
|
|
4024
|
+
const to = tokens.getColor(background.toToken);
|
|
4025
|
+
const direction = GRADIENT_DIRECTION_MAP$2[background.direction] || "to right";
|
|
4026
|
+
return `background: linear-gradient(${direction}, ${from}, ${to})`;
|
|
4027
|
+
}
|
|
3734
4028
|
function borderDeclaration(border$1, tokens) {
|
|
3735
4029
|
const borderColor = tokens.getColor(border$1.colorToken);
|
|
3736
4030
|
const borderWidth = border$1.widthClass === "border-4" ? "4px" : border$1.widthClass === "border-2" ? "2px" : border$1.widthClass === "border" ? "1px" : "1px";
|
|
@@ -3848,6 +4142,9 @@ function generateCardCoreCss(scopeId, variants, settings, scope = "all") {
|
|
|
3848
4142
|
* A variant's background and, when it owns one, the complete foreground
|
|
3849
4143
|
* contract that goes with it. Opaque cards are surface providers; transparent
|
|
3850
4144
|
* variants inherit the nearest outer surface atomically and declare nothing.
|
|
4145
|
+
* The SDK bridge has token references but no resolved palette or global prose
|
|
4146
|
+
* preference. It therefore publishes the conservative resting hover plus
|
|
4147
|
+
* underline through the shared contract instead of claiming measured contrast.
|
|
3851
4148
|
*/
|
|
3852
4149
|
function cardSurfaceDeclarations(variant) {
|
|
3853
4150
|
if (variant.background === "solid" && variant.backgroundToken) {
|
|
@@ -3888,7 +4185,7 @@ function generateVariantCss(scopeId, variant, settings, scope = "all") {
|
|
|
3888
4185
|
const mediaRules = mediaCornerRules(variant.cornerRadii, settings.corners);
|
|
3889
4186
|
const contentRules = [`padding: ${SPACING_MAP[variant.spacing]}`];
|
|
3890
4187
|
return [
|
|
3891
|
-
`${containerSelector} {\n ${containerRules
|
|
4188
|
+
`${containerSelector} {\n ${renderCssDeclarations(containerRules)}\n}`,
|
|
3892
4189
|
...cardDecorationPseudoRules(containerSelectors, decorations, coreColor),
|
|
3893
4190
|
`${fallbackBackgroundSelector} {\n background: transparent;\n}`,
|
|
3894
4191
|
`${mediaSelector} {\n ${mediaRules.join(";\n ")};\n}`,
|
|
@@ -12789,219 +13086,6 @@ const paletteSchema = z.object({
|
|
|
12789
13086
|
};
|
|
12790
13087
|
});
|
|
12791
13088
|
|
|
12792
|
-
//#endregion
|
|
12793
|
-
//#region ../theme-core/src/palette/generateShades.ts
|
|
12794
|
-
function generateShades(hexColor, name) {
|
|
12795
|
-
const shadeSteps = [
|
|
12796
|
-
50,
|
|
12797
|
-
100,
|
|
12798
|
-
200,
|
|
12799
|
-
300,
|
|
12800
|
-
400,
|
|
12801
|
-
500,
|
|
12802
|
-
600,
|
|
12803
|
-
700,
|
|
12804
|
-
800,
|
|
12805
|
-
900,
|
|
12806
|
-
950
|
|
12807
|
-
];
|
|
12808
|
-
const base = featuresFromHex(hexColor);
|
|
12809
|
-
const hue = base.okH;
|
|
12810
|
-
const baseChroma = base.okC;
|
|
12811
|
-
const toneMap = {
|
|
12812
|
-
50: .975,
|
|
12813
|
-
100: .945,
|
|
12814
|
-
200: .89,
|
|
12815
|
-
300: .82,
|
|
12816
|
-
400: .74,
|
|
12817
|
-
500: .66,
|
|
12818
|
-
600: .58,
|
|
12819
|
-
700: .49,
|
|
12820
|
-
800: .4,
|
|
12821
|
-
900: .28,
|
|
12822
|
-
950: .16
|
|
12823
|
-
};
|
|
12824
|
-
const shadeDefs = shadeSteps.map((step, idx) => {
|
|
12825
|
-
const t = idx / (shadeSteps.length - 1);
|
|
12826
|
-
const L = toneMap[step];
|
|
12827
|
-
const chromaFactor = .06 + .94 * Math.sin(Math.PI * t);
|
|
12828
|
-
const C = baseChroma * chromaFactor;
|
|
12829
|
-
return {
|
|
12830
|
-
name: `${name}-${step}`,
|
|
12831
|
-
hex: oklchToHexGamut(L, C, hue)
|
|
12832
|
-
};
|
|
12833
|
-
});
|
|
12834
|
-
shadeDefs.push({
|
|
12835
|
-
name,
|
|
12836
|
-
hex: hexColor
|
|
12837
|
-
});
|
|
12838
|
-
return shadeDefs;
|
|
12839
|
-
}
|
|
12840
|
-
|
|
12841
|
-
//#endregion
|
|
12842
|
-
//#region ../theme-core/src/palette/neutralScale.ts
|
|
12843
|
-
function generateNeutralScale({ background, steps = 11, warm = false }) {
|
|
12844
|
-
const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
|
|
12845
|
-
const shadeNames = [
|
|
12846
|
-
50,
|
|
12847
|
-
100,
|
|
12848
|
-
200,
|
|
12849
|
-
300,
|
|
12850
|
-
400,
|
|
12851
|
-
500,
|
|
12852
|
-
600,
|
|
12853
|
-
700,
|
|
12854
|
-
800,
|
|
12855
|
-
900,
|
|
12856
|
-
950
|
|
12857
|
-
];
|
|
12858
|
-
const count = Math.min(steps, shadeNames.length);
|
|
12859
|
-
const scale$1 = [];
|
|
12860
|
-
for (let i = 0; i < count; i++) {
|
|
12861
|
-
const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
|
|
12862
|
-
const t = i / (count - 1);
|
|
12863
|
-
const tone = isDark ? mix$1(.06, .92, t) : mix$1(.98, .1, t);
|
|
12864
|
-
const hex = oklchToHexGamut(tone, .02, warm ? 85 : 250);
|
|
12865
|
-
const name = `neutral-${shadeName}`;
|
|
12866
|
-
scale$1.push({
|
|
12867
|
-
name,
|
|
12868
|
-
hex
|
|
12869
|
-
});
|
|
12870
|
-
}
|
|
12871
|
-
return scale$1;
|
|
12872
|
-
}
|
|
12873
|
-
function mix$1(a, b, t) {
|
|
12874
|
-
return a + (b - a) * Math.max(0, Math.min(1, t));
|
|
12875
|
-
}
|
|
12876
|
-
|
|
12877
|
-
//#endregion
|
|
12878
|
-
//#region ../theme-core/src/palette/utils/textColor.ts
|
|
12879
|
-
/**
|
|
12880
|
-
* Pick the better of black/white text for a given background using actual
|
|
12881
|
-
* contrast ratios, not a simple luminance cutoff.
|
|
12882
|
-
*
|
|
12883
|
-
* @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
|
|
12884
|
-
* @param minContrast - Preferred minimum contrast ratio for body-sized text
|
|
12885
|
-
* @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
|
|
12886
|
-
*/
|
|
12887
|
-
function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
|
|
12888
|
-
const { r, g, b } = parseColorToRgb(background);
|
|
12889
|
-
const luminance = relativeLuminance(r, g, b);
|
|
12890
|
-
const blackContrast = (luminance + .05) / .05;
|
|
12891
|
-
const whiteContrast = 1.05 / (luminance + .05);
|
|
12892
|
-
if (blackContrast >= minContrast && whiteContrast < minContrast) {
|
|
12893
|
-
return "#000000";
|
|
12894
|
-
}
|
|
12895
|
-
if (whiteContrast >= minContrast && blackContrast < minContrast) {
|
|
12896
|
-
return "#FFFFFF";
|
|
12897
|
-
}
|
|
12898
|
-
return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
|
|
12899
|
-
}
|
|
12900
|
-
/**
|
|
12901
|
-
* Backwards-compatible alias for callers that want the best black/white text
|
|
12902
|
-
* color for a background.
|
|
12903
|
-
*/
|
|
12904
|
-
function getTextColorForBackground(background) {
|
|
12905
|
-
return pickBlackOrWhiteTextColor(background);
|
|
12906
|
-
}
|
|
12907
|
-
function parseColorToRgb(input) {
|
|
12908
|
-
const trimmed = input.trim();
|
|
12909
|
-
if (trimmed.startsWith("#")) {
|
|
12910
|
-
return hexToRgb(trimmed);
|
|
12911
|
-
}
|
|
12912
|
-
const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
|
|
12913
|
-
if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) {
|
|
12914
|
-
return {
|
|
12915
|
-
r: clamp255(Number(rgbMatch[1])),
|
|
12916
|
-
g: clamp255(Number(rgbMatch[2])),
|
|
12917
|
-
b: clamp255(Number(rgbMatch[3]))
|
|
12918
|
-
};
|
|
12919
|
-
}
|
|
12920
|
-
throw new Error("Unsupported color format");
|
|
12921
|
-
}
|
|
12922
|
-
function clamp255(value) {
|
|
12923
|
-
if (Number.isNaN(value)) return 0;
|
|
12924
|
-
return Math.max(0, Math.min(255, value));
|
|
12925
|
-
}
|
|
12926
|
-
|
|
12927
|
-
//#endregion
|
|
12928
|
-
//#region ../theme-core/src/palette/expandPalette.ts
|
|
12929
|
-
function colorsToMap$1(palette) {
|
|
12930
|
-
const map = {};
|
|
12931
|
-
for (const c of palette.colors) {
|
|
12932
|
-
map[c.name] = c.hex;
|
|
12933
|
-
}
|
|
12934
|
-
return map;
|
|
12935
|
-
}
|
|
12936
|
-
function isBackgroundDark(hex) {
|
|
12937
|
-
try {
|
|
12938
|
-
return featuresFromHex(hex).Y < .5;
|
|
12939
|
-
} catch {
|
|
12940
|
-
return false;
|
|
12941
|
-
}
|
|
12942
|
-
}
|
|
12943
|
-
const nonShadeablePaletteRoleNames = new Set([
|
|
12944
|
-
"success",
|
|
12945
|
-
"warning",
|
|
12946
|
-
"destructive"
|
|
12947
|
-
]);
|
|
12948
|
-
/**
|
|
12949
|
-
* Status role aliases the persisted palette does not carry itself.
|
|
12950
|
-
*
|
|
12951
|
-
* Generators reference `--tb-error` (the site-style-owned input error border)
|
|
12952
|
-
* and `--tb-info` (`.alert-info`, `.status-info`, the field-note rule), but the
|
|
12953
|
-
* palette schema only defines `destructive` and has no `info` role at all.
|
|
12954
|
-
* Without these aliases those declarations name variables that are never
|
|
12955
|
-
* defined and browsers drop them. The SDK bridge resolves the same pair via
|
|
12956
|
-
* TOKEN_ALIASES, so both runtimes agree on what a status token means.
|
|
12957
|
-
*
|
|
12958
|
-
* Runs after the danger → destructive promotion above, so `destructive` is
|
|
12959
|
-
* already the single source of truth for the error colour.
|
|
12960
|
-
*/
|
|
12961
|
-
function statusAliasTokens(expanded) {
|
|
12962
|
-
const aliases = {};
|
|
12963
|
-
if (expanded.destructive) aliases.error = expanded.destructive;
|
|
12964
|
-
if (expanded.accent) aliases.info = expanded.accent;
|
|
12965
|
-
return aliases;
|
|
12966
|
-
}
|
|
12967
|
-
function expandPalette(palette) {
|
|
12968
|
-
const base = colorsToMap$1(palette);
|
|
12969
|
-
const expanded = { ...base };
|
|
12970
|
-
const background = base.background || base.surface || "#ffffff";
|
|
12971
|
-
const neutral$1 = generateNeutralScale({
|
|
12972
|
-
background,
|
|
12973
|
-
steps: 11,
|
|
12974
|
-
warm: false
|
|
12975
|
-
});
|
|
12976
|
-
neutral$1.forEach((n) => {
|
|
12977
|
-
expanded[n.name] = n.hex;
|
|
12978
|
-
});
|
|
12979
|
-
for (const [name, hex] of Object.entries(base)) {
|
|
12980
|
-
if (nonShadeablePaletteRoleNames.has(name)) continue;
|
|
12981
|
-
if (hex) {
|
|
12982
|
-
const shades = generateShades(hex, name);
|
|
12983
|
-
shades.forEach((shade) => {
|
|
12984
|
-
expanded[shade.name] = shade.hex;
|
|
12985
|
-
});
|
|
12986
|
-
}
|
|
12987
|
-
}
|
|
12988
|
-
if (base.text) expanded.body = base.text;
|
|
12989
|
-
if (base.danger && !base.destructive) {
|
|
12990
|
-
expanded.destructive = base.danger;
|
|
12991
|
-
}
|
|
12992
|
-
Object.assign(expanded, statusAliasTokens(expanded));
|
|
12993
|
-
const isDark = base.background ? isBackgroundDark(base.background) : false;
|
|
12994
|
-
const isDarkMode = palette.meta?.mode === "dark" || isDark;
|
|
12995
|
-
expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
|
|
12996
|
-
for (const [token, hex] of Object.entries({ ...expanded })) {
|
|
12997
|
-
if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
|
|
12998
|
-
const onToken = onColorTokenName(token);
|
|
12999
|
-
if (expanded[onToken]) continue;
|
|
13000
|
-
expanded[onToken] = getTextColorForBackground(hex);
|
|
13001
|
-
}
|
|
13002
|
-
return expanded;
|
|
13003
|
-
}
|
|
13004
|
-
|
|
13005
13089
|
//#endregion
|
|
13006
13090
|
//#region ../theme-core/src/buttons/constants.ts
|
|
13007
13091
|
/**
|
|
@@ -17025,6 +17109,48 @@ const effectCompositionSchema = z.object({
|
|
|
17025
17109
|
focus: z.array(effectApplicationSchema).optional()
|
|
17026
17110
|
});
|
|
17027
17111
|
|
|
17112
|
+
//#endregion
|
|
17113
|
+
//#region ../theme-core/src/navigation/effectPolicy.ts
|
|
17114
|
+
/** Every registered effect has one meaning for surface-owned nav hover. */
|
|
17115
|
+
const NAV_EFFECT_POLICIES = {
|
|
17116
|
+
"darken-background": "other",
|
|
17117
|
+
"lighten-background": "other",
|
|
17118
|
+
"fade-background": "other",
|
|
17119
|
+
"opacity-reduce": "other",
|
|
17120
|
+
"animated-gradient": "other",
|
|
17121
|
+
"background-on-hover": "paired-hover",
|
|
17122
|
+
"background-on-hover-alpha": "paired-hover",
|
|
17123
|
+
"border-glow": "other",
|
|
17124
|
+
"drop-shadow": "other",
|
|
17125
|
+
"inner-shadow": "other",
|
|
17126
|
+
"neumorphic-shadow": "other",
|
|
17127
|
+
"outer-glow": "other",
|
|
17128
|
+
"retro-3d-shadow": "other",
|
|
17129
|
+
"scale-up": "other",
|
|
17130
|
+
"scale-down": "other",
|
|
17131
|
+
lift: "other",
|
|
17132
|
+
"press-down": "other",
|
|
17133
|
+
pop: "other",
|
|
17134
|
+
"aurora-glow": "other",
|
|
17135
|
+
"cosmic-stardust": "other",
|
|
17136
|
+
"gradient-border-fill": "other",
|
|
17137
|
+
"animated-gradient-border-fill": "other",
|
|
17138
|
+
"border-bottom-grow": "other",
|
|
17139
|
+
"gradient-text": "other",
|
|
17140
|
+
"text-color-change": "text-colour",
|
|
17141
|
+
"border-color-change": "other",
|
|
17142
|
+
"sketchy-border": "other",
|
|
17143
|
+
"pulse-animation": "other",
|
|
17144
|
+
"nav-underline": "underline",
|
|
17145
|
+
"nav-frosted-base": "other",
|
|
17146
|
+
"nav-frosted-hover": "other",
|
|
17147
|
+
"nav-frosted-active": "other"
|
|
17148
|
+
};
|
|
17149
|
+
const policiesById = NAV_EFFECT_POLICIES;
|
|
17150
|
+
function navEffectPolicy(effectId) {
|
|
17151
|
+
return policiesById[effectId] ?? null;
|
|
17152
|
+
}
|
|
17153
|
+
|
|
17028
17154
|
//#endregion
|
|
17029
17155
|
//#region ../theme-core/src/navigation/types.ts
|
|
17030
17156
|
/**
|
|
@@ -17102,6 +17228,16 @@ const navLinkStyleSchema = z.object({
|
|
|
17102
17228
|
borderRadius: navLinkBorderRadiusSchema.default("none"),
|
|
17103
17229
|
effects: effectCompositionSchema.optional()
|
|
17104
17230
|
});
|
|
17231
|
+
function navLinkHoverColorCandidate(style) {
|
|
17232
|
+
const effect = style?.effects?.hover?.find((item) => navEffectPolicy(item.effectId) === "text-colour");
|
|
17233
|
+
return parseThemeColorToken(effect?.options?.hoverColorToken ?? style?.hoverColorToken);
|
|
17234
|
+
}
|
|
17235
|
+
function navLinkOwnsHoverForeground(style) {
|
|
17236
|
+
return style?.effects?.hover?.some((effect) => navEffectPolicy(effect.effectId) === "paired-hover") ?? false;
|
|
17237
|
+
}
|
|
17238
|
+
function navLinkOwnsUnderline(style) {
|
|
17239
|
+
return style?.effects?.hover?.some((effect) => navEffectPolicy(effect.effectId) === "underline") ?? false;
|
|
17240
|
+
}
|
|
17105
17241
|
/**
|
|
17106
17242
|
* Padding values for nav link padding presets
|
|
17107
17243
|
*/
|
|
@@ -22908,6 +23044,17 @@ function reinterpretForSiteStyle(input) {
|
|
|
22908
23044
|
};
|
|
22909
23045
|
}
|
|
22910
23046
|
|
|
23047
|
+
//#endregion
|
|
23048
|
+
//#region ../theme-core/src/header/rootPaint.ts
|
|
23049
|
+
/** The nav container takes precedence when a floating header also has one. */
|
|
23050
|
+
function headerRootPaint(header) {
|
|
23051
|
+
const navContainerType = header?.navContainer?.type ?? "none";
|
|
23052
|
+
if (navContainerType === "glass" || navContainerType === "pill") {
|
|
23053
|
+
return "contained-nav";
|
|
23054
|
+
}
|
|
23055
|
+
return header?.variant === "floating" ? "overlay" : "own-surface";
|
|
23056
|
+
}
|
|
23057
|
+
|
|
22911
23058
|
//#endregion
|
|
22912
23059
|
//#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
|
|
22913
23060
|
const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
|
|
@@ -22942,8 +23089,7 @@ function canCustomizeSiteChromeBackground(theme, surface$1) {
|
|
|
22942
23089
|
if (surface$1 === "footer") return true;
|
|
22943
23090
|
const header = theme?.header;
|
|
22944
23091
|
if (!header) return false;
|
|
22945
|
-
|
|
22946
|
-
return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
|
|
23092
|
+
return headerRootPaint(header) === "own-surface";
|
|
22947
23093
|
}
|
|
22948
23094
|
function siteChromeBackgroundOverrideLabel(override) {
|
|
22949
23095
|
switch (override.kind) {
|
|
@@ -23009,19 +23155,10 @@ function siteChromeBackgroundOverrideFromBackground(background) {
|
|
|
23009
23155
|
function applyHeaderBackgroundOverride(theme, header, override) {
|
|
23010
23156
|
if (override.kind === "default") return header;
|
|
23011
23157
|
const background = siteChromeColorBackground(override.token);
|
|
23012
|
-
|
|
23158
|
+
assertChromeBackgroundHasForeground(theme, background);
|
|
23013
23159
|
return {
|
|
23014
23160
|
...header,
|
|
23015
|
-
background
|
|
23016
|
-
...background,
|
|
23017
|
-
textColor: foreground
|
|
23018
|
-
},
|
|
23019
|
-
textColor: foreground,
|
|
23020
|
-
navColor: foreground,
|
|
23021
|
-
...header.navLinkStyle ? { navLinkStyle: {
|
|
23022
|
-
...header.navLinkStyle,
|
|
23023
|
-
colorToken: foreground
|
|
23024
|
-
} } : {}
|
|
23161
|
+
background
|
|
23025
23162
|
};
|
|
23026
23163
|
}
|
|
23027
23164
|
/**
|
|
@@ -23033,34 +23170,17 @@ function applyHeaderBackgroundOverride(theme, header, override) {
|
|
|
23033
23170
|
function applyFooterBackgroundOverride(theme, footer, override) {
|
|
23034
23171
|
if (override.kind === "default") return footer;
|
|
23035
23172
|
const background = siteChromeColorBackground(override.token);
|
|
23036
|
-
|
|
23173
|
+
assertChromeBackgroundHasForeground(theme, background);
|
|
23037
23174
|
return {
|
|
23038
23175
|
...footer,
|
|
23039
|
-
background
|
|
23040
|
-
...background,
|
|
23041
|
-
textColor: foreground
|
|
23042
|
-
},
|
|
23043
|
-
navColor: foreground,
|
|
23044
|
-
...footer.navLinkStyle ? { navLinkStyle: {
|
|
23045
|
-
...footer.navLinkStyle,
|
|
23046
|
-
colorToken: foreground
|
|
23047
|
-
} } : {},
|
|
23048
|
-
...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
|
|
23049
|
-
...footer.bottomTextLinkStyle,
|
|
23050
|
-
colorToken: foreground
|
|
23051
|
-
} } : {},
|
|
23052
|
-
...footer.bottomBar ? { bottomBar: {
|
|
23053
|
-
...footer.bottomBar,
|
|
23054
|
-
textColor: foreground
|
|
23055
|
-
} } : {}
|
|
23176
|
+
background
|
|
23056
23177
|
};
|
|
23057
23178
|
}
|
|
23058
|
-
function
|
|
23179
|
+
function assertChromeBackgroundHasForeground(theme, background) {
|
|
23059
23180
|
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
23060
23181
|
if (resolved.kind === "unknown") {
|
|
23061
23182
|
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
23062
23183
|
}
|
|
23063
|
-
return resolved.roles.text;
|
|
23064
23184
|
}
|
|
23065
23185
|
function siteChromeColorBackground(color) {
|
|
23066
23186
|
return {
|
|
@@ -25209,12 +25329,261 @@ function compareCompiledHeaders(left, right) {
|
|
|
25209
25329
|
};
|
|
25210
25330
|
}
|
|
25211
25331
|
|
|
25332
|
+
//#endregion
|
|
25333
|
+
//#region ../theme-core/src/navigation/presets.ts
|
|
25334
|
+
/**
|
|
25335
|
+
* Built-in nav link style presets
|
|
25336
|
+
*
|
|
25337
|
+
* Each preset uses composable effects to achieve its look.
|
|
25338
|
+
*/
|
|
25339
|
+
const NAV_LINK_PRESETS = {
|
|
25340
|
+
minimal: {
|
|
25341
|
+
name: "Minimal",
|
|
25342
|
+
typography: "body",
|
|
25343
|
+
fontWeight: "medium",
|
|
25344
|
+
textTransform: "none",
|
|
25345
|
+
italic: false,
|
|
25346
|
+
colorToken: "text",
|
|
25347
|
+
hoverColorToken: "primary",
|
|
25348
|
+
padding: "none",
|
|
25349
|
+
borderRadius: "none",
|
|
25350
|
+
effects: { hover: [{
|
|
25351
|
+
effectId: "text-color-change",
|
|
25352
|
+
options: { hoverColorToken: "primary" }
|
|
25353
|
+
}] }
|
|
25354
|
+
},
|
|
25355
|
+
underline: {
|
|
25356
|
+
name: "Underline",
|
|
25357
|
+
typography: "body",
|
|
25358
|
+
fontWeight: "medium",
|
|
25359
|
+
textTransform: "none",
|
|
25360
|
+
italic: false,
|
|
25361
|
+
colorToken: "text",
|
|
25362
|
+
padding: "none",
|
|
25363
|
+
borderRadius: "none",
|
|
25364
|
+
effects: { hover: [{
|
|
25365
|
+
effectId: "nav-underline",
|
|
25366
|
+
options: { style: "static" }
|
|
25367
|
+
}] }
|
|
25368
|
+
},
|
|
25369
|
+
"underline-grow": {
|
|
25370
|
+
name: "Underline Grow",
|
|
25371
|
+
typography: "body",
|
|
25372
|
+
fontWeight: "medium",
|
|
25373
|
+
textTransform: "none",
|
|
25374
|
+
italic: false,
|
|
25375
|
+
colorToken: "text",
|
|
25376
|
+
padding: "none",
|
|
25377
|
+
borderRadius: "none",
|
|
25378
|
+
effects: { hover: [{
|
|
25379
|
+
effectId: "nav-underline",
|
|
25380
|
+
options: { style: "grow" }
|
|
25381
|
+
}] }
|
|
25382
|
+
},
|
|
25383
|
+
capsule: {
|
|
25384
|
+
name: "Capsule",
|
|
25385
|
+
typography: "body",
|
|
25386
|
+
fontWeight: "medium",
|
|
25387
|
+
textTransform: "none",
|
|
25388
|
+
italic: false,
|
|
25389
|
+
colorToken: "text",
|
|
25390
|
+
padding: "compact",
|
|
25391
|
+
borderRadius: "full",
|
|
25392
|
+
effects: {
|
|
25393
|
+
hover: [{
|
|
25394
|
+
effectId: "background-on-hover-alpha",
|
|
25395
|
+
options: {
|
|
25396
|
+
colorToken: "primary",
|
|
25397
|
+
opacity: 10
|
|
25398
|
+
}
|
|
25399
|
+
}],
|
|
25400
|
+
active: [{
|
|
25401
|
+
effectId: "background-on-hover-alpha",
|
|
25402
|
+
options: {
|
|
25403
|
+
colorToken: "primary",
|
|
25404
|
+
opacity: 15
|
|
25405
|
+
}
|
|
25406
|
+
}]
|
|
25407
|
+
}
|
|
25408
|
+
},
|
|
25409
|
+
solid: {
|
|
25410
|
+
name: "Solid",
|
|
25411
|
+
typography: "body",
|
|
25412
|
+
fontWeight: "medium",
|
|
25413
|
+
textTransform: "none",
|
|
25414
|
+
italic: false,
|
|
25415
|
+
colorToken: "text",
|
|
25416
|
+
padding: "compact",
|
|
25417
|
+
borderRadius: "none",
|
|
25418
|
+
effects: {
|
|
25419
|
+
hover: [{
|
|
25420
|
+
effectId: "background-on-hover",
|
|
25421
|
+
options: {
|
|
25422
|
+
colorToken: "primary",
|
|
25423
|
+
textColorToken: "primaryForeground"
|
|
25424
|
+
}
|
|
25425
|
+
}],
|
|
25426
|
+
active: [{
|
|
25427
|
+
effectId: "background-on-hover",
|
|
25428
|
+
options: {
|
|
25429
|
+
colorToken: "primary",
|
|
25430
|
+
textColorToken: "primaryForeground"
|
|
25431
|
+
}
|
|
25432
|
+
}]
|
|
25433
|
+
}
|
|
25434
|
+
},
|
|
25435
|
+
scale: {
|
|
25436
|
+
name: "Scale",
|
|
25437
|
+
typography: "body",
|
|
25438
|
+
fontWeight: "medium",
|
|
25439
|
+
textTransform: "none",
|
|
25440
|
+
italic: false,
|
|
25441
|
+
colorToken: "text",
|
|
25442
|
+
padding: "none",
|
|
25443
|
+
borderRadius: "none",
|
|
25444
|
+
effects: { hover: [{
|
|
25445
|
+
effectId: "scale-up",
|
|
25446
|
+
options: { scale: 1.05 }
|
|
25447
|
+
}] }
|
|
25448
|
+
},
|
|
25449
|
+
frosted: {
|
|
25450
|
+
name: "Frosted",
|
|
25451
|
+
typography: "body",
|
|
25452
|
+
fontWeight: "medium",
|
|
25453
|
+
textTransform: "none",
|
|
25454
|
+
italic: false,
|
|
25455
|
+
colorToken: "text",
|
|
25456
|
+
padding: "compact",
|
|
25457
|
+
borderRadius: "md",
|
|
25458
|
+
effects: {
|
|
25459
|
+
base: [{ effectId: "nav-frosted-base" }],
|
|
25460
|
+
hover: [{ effectId: "nav-frosted-hover" }],
|
|
25461
|
+
active: [{ effectId: "nav-frosted-active" }]
|
|
25462
|
+
}
|
|
25463
|
+
}
|
|
25464
|
+
};
|
|
25465
|
+
/**
|
|
25466
|
+
* Get a nav link style preset by ID
|
|
25467
|
+
*/
|
|
25468
|
+
function getNavLinkPreset(presetId) {
|
|
25469
|
+
return NAV_LINK_PRESETS[presetId];
|
|
25470
|
+
}
|
|
25471
|
+
/**
|
|
25472
|
+
* List of available preset IDs
|
|
25473
|
+
*/
|
|
25474
|
+
const NAV_LINK_PRESET_IDS = Object.keys(NAV_LINK_PRESETS);
|
|
25475
|
+
|
|
25476
|
+
//#endregion
|
|
25477
|
+
//#region ../theme-core/src/header/legacyNavLinkStyle.ts
|
|
25478
|
+
function buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize) {
|
|
25479
|
+
const navStyle = header.navStyle ?? "minimal";
|
|
25480
|
+
const basePreset = getNavLinkPreset(navStyle);
|
|
25481
|
+
if (!basePreset) return null;
|
|
25482
|
+
const style = {
|
|
25483
|
+
...basePreset,
|
|
25484
|
+
fontWeight: header.navWeight ?? basePreset.fontWeight,
|
|
25485
|
+
colorToken: (header.navColor ?? header.textColor)?.trim() || basePreset.colorToken,
|
|
25486
|
+
...contrastTreatment ? { contrastTreatment } : {},
|
|
25487
|
+
...navTextSize ? { textSize: navTextSize } : {}
|
|
25488
|
+
};
|
|
25489
|
+
if (header.navWeight === "bold" && navStyle === "solid") {
|
|
25490
|
+
style.textTransform = "uppercase";
|
|
25491
|
+
}
|
|
25492
|
+
applyLegacyGlassTreatment(style, header, navStyle);
|
|
25493
|
+
return style;
|
|
25494
|
+
}
|
|
25495
|
+
function applyLegacyGlassTreatment(style, header, navStyle) {
|
|
25496
|
+
if (header.navContainer?.type !== "glass" || navStyle !== "minimal") return;
|
|
25497
|
+
style.borderRadius = "full";
|
|
25498
|
+
style.effects = {
|
|
25499
|
+
...style.effects,
|
|
25500
|
+
hover: glassStateEffects(10),
|
|
25501
|
+
active: glassStateEffects(18)
|
|
25502
|
+
};
|
|
25503
|
+
}
|
|
25504
|
+
function glassStateEffects(opacity) {
|
|
25505
|
+
return [{
|
|
25506
|
+
effectId: "background-on-hover-alpha",
|
|
25507
|
+
options: {
|
|
25508
|
+
colorToken: "neutral-50",
|
|
25509
|
+
opacity
|
|
25510
|
+
}
|
|
25511
|
+
}, {
|
|
25512
|
+
effectId: "text-color-change",
|
|
25513
|
+
options: { hoverColorToken: "neutral-50" }
|
|
25514
|
+
}];
|
|
25515
|
+
}
|
|
25516
|
+
|
|
25517
|
+
//#endregion
|
|
25518
|
+
//#region ../theme-core/src/header/openingForegroundProjection.ts
|
|
25519
|
+
/** Page-scoped render data. It is deliberately absent from the persisted theme schema. */
|
|
25520
|
+
function withOpeningForegroundProjection(header, foreground) {
|
|
25521
|
+
const projected = {
|
|
25522
|
+
...header,
|
|
25523
|
+
__runtimeOpeningForeground: foreground
|
|
25524
|
+
};
|
|
25525
|
+
return projected;
|
|
25526
|
+
}
|
|
25527
|
+
function readOpeningForegroundProjection(header) {
|
|
25528
|
+
return header?.__runtimeOpeningForeground ?? null;
|
|
25529
|
+
}
|
|
25530
|
+
|
|
25531
|
+
//#endregion
|
|
25532
|
+
//#region ../theme-core/src/header/effectiveNavLinkStyle.ts
|
|
25533
|
+
function effectiveHeaderNavLinkStyle(header, contrastTreatment = null, navTextSize = null) {
|
|
25534
|
+
if (!header) return null;
|
|
25535
|
+
if (!header.navLinkStyle) {
|
|
25536
|
+
return buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize);
|
|
25537
|
+
}
|
|
25538
|
+
return {
|
|
25539
|
+
...header.navLinkStyle,
|
|
25540
|
+
...contrastTreatment ? { contrastTreatment } : {},
|
|
25541
|
+
...navTextSize ? { textSize: navTextSize } : {}
|
|
25542
|
+
};
|
|
25543
|
+
}
|
|
25544
|
+
function headerForegroundCandidates(header) {
|
|
25545
|
+
if (!header) return {};
|
|
25546
|
+
const style = effectiveHeaderNavLinkStyle(header);
|
|
25547
|
+
return {
|
|
25548
|
+
text: header.textColor ?? header.background?.textColor,
|
|
25549
|
+
link: style?.colorToken ?? header.navColor ?? header.textColor,
|
|
25550
|
+
linkHover: navLinkHoverColorCandidate(style)
|
|
25551
|
+
};
|
|
25552
|
+
}
|
|
25553
|
+
function headerLeafTextColor(header) {
|
|
25554
|
+
if (!header) return "text";
|
|
25555
|
+
return header.textColor ?? header.navColor ?? header.background?.textColor ?? header.navLinkStyle?.colorToken ?? "text";
|
|
25556
|
+
}
|
|
25557
|
+
/** Transparent chrome must reset the whole contract on its own root. */
|
|
25558
|
+
function transparentHeaderForegroundInput(header, palette) {
|
|
25559
|
+
const projection = readOpeningForegroundProjection(header);
|
|
25560
|
+
if (projection?.kind === "known") {
|
|
25561
|
+
return surfaceForegroundInputFromResolved(projection, rgbColorValue);
|
|
25562
|
+
}
|
|
25563
|
+
return authoredTransparentHeaderForegroundInput(header, palette);
|
|
25564
|
+
}
|
|
25565
|
+
function authoredTransparentHeaderForegroundInput(header, palette) {
|
|
25566
|
+
const text = headerLeafTextColor(header);
|
|
25567
|
+
const style = effectiveHeaderNavLinkStyle(header);
|
|
25568
|
+
const link = style?.colorToken ?? text;
|
|
25569
|
+
const restingToken = parseThemeColorToken(link) ?? parseThemeColorToken("text");
|
|
25570
|
+
if (!restingToken) throw new Error("Built-in text colour token is invalid");
|
|
25571
|
+
const hover = planForegroundHover({
|
|
25572
|
+
restingToken,
|
|
25573
|
+
candidate: navLinkOwnsHoverForeground(style) ? null : navLinkHoverColorCandidate(style),
|
|
25574
|
+
palette,
|
|
25575
|
+
backdrops: null
|
|
25576
|
+
});
|
|
25577
|
+
return {
|
|
25578
|
+
text: rgbColorValue(text),
|
|
25579
|
+
link: rgbColorValue(link),
|
|
25580
|
+
linkHover: rgbColorValue(hover.token),
|
|
25581
|
+
hoverDecoration: navLinkOwnsUnderline(style) ? "initial" : hover.decoration
|
|
25582
|
+
};
|
|
25583
|
+
}
|
|
25584
|
+
|
|
25212
25585
|
//#endregion
|
|
25213
25586
|
//#region ../theme-core/src/site-styles/headerOpeningForeground.ts
|
|
25214
|
-
/** The nav-link hover effect that is a plain colour change. */
|
|
25215
|
-
const TEXT_COLOR_CHANGE_EFFECT_ID = "text-color-change";
|
|
25216
|
-
/** The nav-link hover affordance that does not depend on a colour delta. */
|
|
25217
|
-
const NAV_UNDERLINE_EFFECT_ID = "nav-underline";
|
|
25218
25587
|
function headerStyleAvailability(recipe, context) {
|
|
25219
25588
|
if (!recipe.openingForeground) return { kind: "available" };
|
|
25220
25589
|
return context?.openingForeground?.kind === "known" ? { kind: "available" } : {
|
|
@@ -25231,17 +25600,33 @@ function headerStyleAvailabilityReasonLabel(reason) {
|
|
|
25231
25600
|
function applyOpeningForeground(theme, header, recipe, context) {
|
|
25232
25601
|
if (!recipe.openingForeground) return header;
|
|
25233
25602
|
const foreground = context?.openingForeground ?? { kind: "unknown" };
|
|
25234
|
-
if (foreground.kind === "unknown") return applyUnknownOpeningForegroundFallback(header);
|
|
25603
|
+
if (foreground.kind === "unknown") return withOpeningForegroundProjection(applyUnknownOpeningForegroundFallback(theme, header), foreground);
|
|
25604
|
+
return projectKnownOpeningForeground(theme, header, recipe, foreground);
|
|
25605
|
+
}
|
|
25606
|
+
function projectKnownOpeningForeground(theme, header, recipe, foreground) {
|
|
25235
25607
|
const withBrandForeground = {
|
|
25236
25608
|
...header,
|
|
25237
25609
|
textColor: foreground.roles.text
|
|
25238
25610
|
};
|
|
25239
25611
|
if (recipe.openingForeground === "brand-only") return withBrandForeground;
|
|
25240
25612
|
const scrollTreatment = compileHeaderScrollTreatment(theme, recipe, foreground.roles.text);
|
|
25241
|
-
|
|
25613
|
+
const style = effectiveHeaderNavLinkStyle(header);
|
|
25614
|
+
const hoverPlan = style && !navLinkOwnsHoverForeground(style) ? planOpeningNavHover(theme, foreground, style, scrollTreatment) : null;
|
|
25615
|
+
const projectedForeground = hoverPlan ? {
|
|
25616
|
+
...foreground,
|
|
25617
|
+
roles: {
|
|
25618
|
+
...foreground.roles,
|
|
25619
|
+
linkHover: hoverPlan.token
|
|
25620
|
+
},
|
|
25621
|
+
hoverDecoration: hoverPlan.decoration
|
|
25622
|
+
} : foreground;
|
|
25623
|
+
return withOpeningForegroundProjection({
|
|
25242
25624
|
...withBrandForeground,
|
|
25243
25625
|
navColor: foreground.roles.text,
|
|
25244
|
-
navLinkStyle:
|
|
25626
|
+
navLinkStyle: style ? navLinkOwnsHoverForeground(style) ? {
|
|
25627
|
+
...style,
|
|
25628
|
+
colorToken: foreground.roles.link
|
|
25629
|
+
} : projectOpeningTextNavStyle(style, foreground.roles.link) : header.navLinkStyle,
|
|
25245
25630
|
border: {
|
|
25246
25631
|
style: "solid",
|
|
25247
25632
|
width: "thin",
|
|
@@ -25249,79 +25634,52 @@ function applyOpeningForeground(theme, header, recipe, context) {
|
|
|
25249
25634
|
colorToken: parseThemeColorToken(`${foreground.roles.text}/25`) ?? foreground.roles.text
|
|
25250
25635
|
},
|
|
25251
25636
|
...scrollTreatment ? { scrollTreatment } : {}
|
|
25252
|
-
};
|
|
25253
|
-
}
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
*/
|
|
25272
|
-
function
|
|
25273
|
-
const
|
|
25274
|
-
...
|
|
25275
|
-
colorToken: linkToken
|
|
25276
|
-
};
|
|
25277
|
-
const hoverEffects = navLinkStyle.effects?.hover ?? [];
|
|
25278
|
-
const hasColorChange = hoverEffects.some(isTextColorChangeEffect);
|
|
25279
|
-
if (!hasColorChange && hoverEffects.length > 0) return adapted;
|
|
25280
|
-
if (!hasColorChange && !navLinkStyle.hoverColorToken) return adapted;
|
|
25281
|
-
const underline = {
|
|
25282
|
-
effectId: NAV_UNDERLINE_EFFECT_ID,
|
|
25637
|
+
}, projectedForeground);
|
|
25638
|
+
}
|
|
25639
|
+
function planOpeningNavHover(theme, foreground, style, scrollTreatment) {
|
|
25640
|
+
const glassBackdrop = scrollTreatment?.kind === "contrast-glass" ? worstCaseGlassBackdrop(scrollTreatment.backgroundTone) : null;
|
|
25641
|
+
const measured = foreground.provenance.kind === "measured";
|
|
25642
|
+
const candidate$1 = measured ? navLinkHoverColorCandidate(style) : foreground.hoverDecoration === "initial" ? foreground.roles.linkHover : null;
|
|
25643
|
+
const backdrops = measured ? [foreground.provenance.backdropHex, ...glassBackdrop ? [glassBackdrop] : []] : glassBackdrop ? [glassBackdrop] : null;
|
|
25644
|
+
const plan = planForegroundHover({
|
|
25645
|
+
restingToken: foreground.roles.link,
|
|
25646
|
+
candidate: candidate$1,
|
|
25647
|
+
palette: expandPalette(theme.palette),
|
|
25648
|
+
backdrops
|
|
25649
|
+
});
|
|
25650
|
+
return !measured && plan.kind === "softened" ? {
|
|
25651
|
+
kind: "resting",
|
|
25652
|
+
token: foreground.roles.link,
|
|
25653
|
+
decoration: "underline"
|
|
25654
|
+
} : plan;
|
|
25655
|
+
}
|
|
25656
|
+
/** The surface contract owns colour; the nav effect keeps its grow animation. */
|
|
25657
|
+
function projectOpeningTextNavStyle(style, linkToken, requireUnderline = true) {
|
|
25658
|
+
const hover = (style.effects?.hover ?? []).filter((effect) => navEffectPolicy(effect.effectId) !== "text-colour").map((effect) => navEffectPolicy(effect.effectId) === "underline" ? {
|
|
25659
|
+
...effect,
|
|
25283
25660
|
options: {
|
|
25661
|
+
...effect.options,
|
|
25284
25662
|
style: "grow",
|
|
25285
25663
|
colorToken: linkToken
|
|
25286
25664
|
}
|
|
25287
|
-
};
|
|
25288
|
-
const
|
|
25289
|
-
if (hoverPlan.kind === "resting") {
|
|
25290
|
-
return {
|
|
25291
|
-
...adapted,
|
|
25292
|
-
hoverColorToken: null,
|
|
25293
|
-
effects: {
|
|
25294
|
-
...adapted.effects,
|
|
25295
|
-
hover: [...withoutColorChange, underline]
|
|
25296
|
-
}
|
|
25297
|
-
};
|
|
25298
|
-
}
|
|
25299
|
-
const hoverColorToken = hoverPlan.token;
|
|
25665
|
+
} : effect);
|
|
25666
|
+
const needsUnderline = requireUnderline && !navLinkOwnsUnderline({ effects: { hover } });
|
|
25300
25667
|
return {
|
|
25301
|
-
...
|
|
25302
|
-
|
|
25668
|
+
...style,
|
|
25669
|
+
colorToken: linkToken,
|
|
25670
|
+
hoverColorToken: null,
|
|
25303
25671
|
effects: {
|
|
25304
|
-
...
|
|
25305
|
-
hover: [...
|
|
25306
|
-
|
|
25672
|
+
...style.effects,
|
|
25673
|
+
hover: needsUnderline ? [...hover, {
|
|
25674
|
+
effectId: "nav-underline",
|
|
25307
25675
|
options: {
|
|
25308
|
-
|
|
25309
|
-
|
|
25676
|
+
style: "grow",
|
|
25677
|
+
colorToken: linkToken
|
|
25310
25678
|
}
|
|
25311
|
-
} :
|
|
25679
|
+
}] : hover
|
|
25312
25680
|
}
|
|
25313
25681
|
};
|
|
25314
25682
|
}
|
|
25315
|
-
function isTextColorChangeEffect(effect) {
|
|
25316
|
-
return effect.effectId === TEXT_COLOR_CHANGE_EFFECT_ID;
|
|
25317
|
-
}
|
|
25318
|
-
function isNavUnderlineEffect(effect) {
|
|
25319
|
-
return effect.effectId === NAV_UNDERLINE_EFFECT_ID;
|
|
25320
|
-
}
|
|
25321
|
-
/** Concrete hex behind a plain palette token, or null when it is not one. */
|
|
25322
|
-
function paletteHexForToken(theme, token) {
|
|
25323
|
-
return opaquePaletteHexForToken(expandPalette(theme.palette), token);
|
|
25324
|
-
}
|
|
25325
25683
|
function compileHeaderScrollTreatment(theme, recipe, foregroundToken) {
|
|
25326
25684
|
if (recipe.scrollTreatment !== "contrast-glass") return undefined;
|
|
25327
25685
|
const tokenParts = parseColorTokenParts(foregroundToken);
|
|
@@ -25347,19 +25705,37 @@ const UNKNOWN_OPENING_FOREGROUND_CONTAINER = {
|
|
|
25347
25705
|
* keeps already-persisted selections readable without creating a second
|
|
25348
25706
|
* availability policy.
|
|
25349
25707
|
*/
|
|
25350
|
-
function applyUnknownOpeningForegroundFallback(header) {
|
|
25708
|
+
function applyUnknownOpeningForegroundFallback(theme, header) {
|
|
25709
|
+
const textToken = parseThemeColorToken("text");
|
|
25710
|
+
if (!textToken) throw new Error("Built-in text colour token is invalid");
|
|
25711
|
+
const style = effectiveHeaderNavLinkStyle(header);
|
|
25712
|
+
const plannedHover = style && !navLinkOwnsHoverForeground(style) ? planUnknownOpeningNavHover(theme, style, textToken) : null;
|
|
25351
25713
|
return {
|
|
25352
25714
|
...header,
|
|
25353
25715
|
scrollTreatment: undefined,
|
|
25354
25716
|
textColor: "text",
|
|
25355
25717
|
navColor: "text",
|
|
25356
|
-
navLinkStyle:
|
|
25357
|
-
...
|
|
25358
|
-
colorToken:
|
|
25718
|
+
navLinkStyle: style ? navLinkOwnsHoverForeground(style) ? {
|
|
25719
|
+
...style,
|
|
25720
|
+
colorToken: textToken
|
|
25721
|
+
} : {
|
|
25722
|
+
...projectOpeningTextNavStyle(style, textToken, plannedHover?.kind === "resting"),
|
|
25723
|
+
hoverColorToken: plannedHover?.kind === "resting" ? null : plannedHover?.token
|
|
25359
25724
|
} : header.navLinkStyle,
|
|
25360
25725
|
container: UNKNOWN_OPENING_FOREGROUND_CONTAINER
|
|
25361
25726
|
};
|
|
25362
25727
|
}
|
|
25728
|
+
function planUnknownOpeningNavHover(theme, style, restingToken) {
|
|
25729
|
+
const palette = expandPalette(theme.palette);
|
|
25730
|
+
const surface$1 = palette.surface;
|
|
25731
|
+
const backdrops = surface$1 ? [compositeHex(surface$1, "#000000", UNKNOWN_OPENING_FOREGROUND_CONTAINER.opacity), compositeHex(surface$1, "#ffffff", UNKNOWN_OPENING_FOREGROUND_CONTAINER.opacity)] : null;
|
|
25732
|
+
return planForegroundHover({
|
|
25733
|
+
restingToken,
|
|
25734
|
+
candidate: surface$1 ? navLinkHoverColorCandidate(style) : null,
|
|
25735
|
+
palette,
|
|
25736
|
+
backdrops
|
|
25737
|
+
});
|
|
25738
|
+
}
|
|
25363
25739
|
function applyPageOpeningForegroundToTheme(theme, recipe, context) {
|
|
25364
25740
|
if (!recipe?.openingForeground) return theme;
|
|
25365
25741
|
return {
|
|
@@ -29737,9 +30113,11 @@ const coursePolicyOverrideSchema = z.object({
|
|
|
29737
30113
|
cutoffHours: z.number().int().nonnegative().optional()
|
|
29738
30114
|
}).strict();
|
|
29739
30115
|
/**
|
|
29740
|
-
* The
|
|
29741
|
-
*
|
|
29742
|
-
*
|
|
30116
|
+
* The booking settings a course owns for every one of its runs: where it is
|
|
30117
|
+
* listed, whether passes and memberships may be used, QR tickets
|
|
30118
|
+
* (`ticketingMode`, where `inherit` follows the course's event type), guest
|
|
30119
|
+
* details (`collectAllAttendeeDetails`, where `null` follows the event type)
|
|
30120
|
+
* and the refunds and transfers policy.
|
|
29743
30121
|
*
|
|
29744
30122
|
* Listing is spelled the way the management API spells it ('public' or
|
|
29745
30123
|
* 'unlisted') rather than in the command language, which folds "not live yet"
|
|
@@ -29750,6 +30128,12 @@ const courseBookingSettingsSchema = z.object({
|
|
|
29750
30128
|
visibility: z.enum(["public", "unlisted"]),
|
|
29751
30129
|
acceptsPasses: z.boolean(),
|
|
29752
30130
|
acceptsMemberships: z.boolean(),
|
|
30131
|
+
ticketingMode: z.enum([
|
|
30132
|
+
"inherit",
|
|
30133
|
+
"enabled",
|
|
30134
|
+
"disabled"
|
|
30135
|
+
]).optional(),
|
|
30136
|
+
collectAllAttendeeDetails: z.boolean().nullable().optional(),
|
|
29753
30137
|
cancellationPolicy: z.object({
|
|
29754
30138
|
refunds: coursePolicyOverrideSchema,
|
|
29755
30139
|
transfers: coursePolicyOverrideSchema
|
|
@@ -30753,7 +31137,6 @@ const upsertEventSeriesCommandSchema = z.object({
|
|
|
30753
31137
|
occurrences: z.array(eventOccurrenceSchema).min(1).readonly(),
|
|
30754
31138
|
content: contentSummarySchema.optional(),
|
|
30755
31139
|
settings: unknownRecordSchema.optional(),
|
|
30756
|
-
presentation: unknownRecordSchema.nullable().optional(),
|
|
30757
31140
|
tags: z.array(z.string()).readonly().optional(),
|
|
30758
31141
|
blocks: entrySlotBlocksSchema.optional(),
|
|
30759
31142
|
staffRefs: z.array(staffRefSchema).readonly().optional()
|
|
@@ -30983,7 +31366,6 @@ const allowedRuntimes = [
|
|
|
30983
31366
|
const intendedExposures = [
|
|
30984
31367
|
"recipe",
|
|
30985
31368
|
"sdk_cli",
|
|
30986
|
-
"onboarding_planner",
|
|
30987
31369
|
"ai_proposal",
|
|
30988
31370
|
"internal_apply"
|
|
30989
31371
|
];
|
|
@@ -31025,21 +31407,15 @@ const allRuntimes = [
|
|
|
31025
31407
|
const standardExposure = [
|
|
31026
31408
|
"recipe",
|
|
31027
31409
|
"sdk_cli",
|
|
31028
|
-
"onboarding_planner",
|
|
31029
31410
|
"internal_apply"
|
|
31030
31411
|
];
|
|
31031
31412
|
const proposalExposure = [
|
|
31032
31413
|
"recipe",
|
|
31033
31414
|
"sdk_cli",
|
|
31034
|
-
"onboarding_planner",
|
|
31035
31415
|
"ai_proposal",
|
|
31036
31416
|
"internal_apply"
|
|
31037
31417
|
];
|
|
31038
|
-
const recipeExposure = [
|
|
31039
|
-
"recipe",
|
|
31040
|
-
"onboarding_planner",
|
|
31041
|
-
"internal_apply"
|
|
31042
|
-
];
|
|
31418
|
+
const recipeExposure = ["recipe", "internal_apply"];
|
|
31043
31419
|
const sdkExposure = ["sdk_cli", "internal_apply"];
|
|
31044
31420
|
|
|
31045
31421
|
//#endregion
|
|
@@ -31732,6 +32108,18 @@ function isIdentifiedImage(image) {
|
|
|
31732
32108
|
return typeof image === "object" && image !== null && typeof image.identifier === "string" && image.identifier.trim().length > 0;
|
|
31733
32109
|
}
|
|
31734
32110
|
|
|
32111
|
+
//#endregion
|
|
32112
|
+
//#region ../site-commands/src/planCommandOrderingDependencies.ts
|
|
32113
|
+
/** A complete reorder observes the membership after all selected page mutations. */
|
|
32114
|
+
function planCommandOrderingDependencies(command, commands, providerByRef, requiresByCommand) {
|
|
32115
|
+
const referenced = (requiresByCommand.get(command.ref) ?? []).flatMap(({ ref }) => {
|
|
32116
|
+
const provider = providerByRef.get(ref);
|
|
32117
|
+
return provider === undefined ? [] : [provider];
|
|
32118
|
+
});
|
|
32119
|
+
const membership = command.type === "reorderPageBlocks" ? commands.filter((candidate$1) => (candidate$1.type === "upsertPageBlock" || candidate$1.type === "deletePageBlock") && candidate$1.input.pageIdentifier === command.input.pageIdentifier).map((candidate$1) => candidate$1.ref) : [];
|
|
32120
|
+
return [...new Set([...referenced, ...membership])];
|
|
32121
|
+
}
|
|
32122
|
+
|
|
31735
32123
|
//#endregion
|
|
31736
32124
|
//#region ../site-commands/src/guards.ts
|
|
31737
32125
|
function isRecord(value) {
|
|
@@ -32161,13 +32549,12 @@ const unsupportedWholeSeriesPageFieldChecks = [
|
|
|
32161
32549
|
(input) => input.categoryRef !== undefined,
|
|
32162
32550
|
(input) => input.categoryIdentifier !== undefined,
|
|
32163
32551
|
(input) => input.tags !== undefined,
|
|
32164
|
-
(input) => input.presentation !== undefined,
|
|
32165
32552
|
(input) => input.content?.imageRef !== undefined,
|
|
32166
32553
|
(input) => input.content?.mediaRefs !== undefined
|
|
32167
32554
|
];
|
|
32168
32555
|
function wholeSeriesPageFieldsGap(input) {
|
|
32169
32556
|
if (!unsupportedWholeSeriesPageFieldChecks.some((hasField) => hasField(input))) return null;
|
|
32170
|
-
return staticGap("event_whole_series_page_fields_unsupported", "Whole-series page, reference
|
|
32557
|
+
return staticGap("event_whole_series_page_fields_unsupported", "Whole-series page, reference and tag fields need native page-authority mapping before mutation.");
|
|
32171
32558
|
}
|
|
32172
32559
|
function passesGap(input) {
|
|
32173
32560
|
return input.bookingOffer.pricing.acceptsPasses === true ? staticGap("event_whole_series_passes_unsupported", "Whole-series commands do not support pass redemption.") : null;
|
|
@@ -32581,10 +32968,9 @@ function orderCommands(commands, providerByRef, requiresByCommand) {
|
|
|
32581
32968
|
const outgoing = new Map();
|
|
32582
32969
|
const indegree = new Map(commands.map((command) => [command.ref, 0]));
|
|
32583
32970
|
for (const command of commands) {
|
|
32584
|
-
const
|
|
32585
|
-
for (const
|
|
32586
|
-
|
|
32587
|
-
if (provider === undefined || provider === command.ref) continue;
|
|
32971
|
+
const providers = planCommandOrderingDependencies(command, commands, providerByRef, requiresByCommand);
|
|
32972
|
+
for (const provider of providers) {
|
|
32973
|
+
if (provider === command.ref) continue;
|
|
32588
32974
|
if (!outgoing.has(provider)) outgoing.set(provider, new Set());
|
|
32589
32975
|
const dependents = outgoing.get(provider);
|
|
32590
32976
|
if (dependents !== undefined && !dependents.has(command.ref)) {
|
|
@@ -33325,4 +33711,4 @@ function parseSdkEventRunUpsertInput(input, runIndex, options) {
|
|
|
33325
33711
|
}
|
|
33326
33712
|
|
|
33327
33713
|
//#endregion
|
|
33328
|
-
export {
|
|
33714
|
+
export { getCuratedSiteStyle as $, rgbColorValue as $n, themeButtonVariantClassNames as $t, createBlockRef as A, stripStaleTransitionStyleGroup as An, offeringsPreviewCompositionTreatmentValues as Ar, INPUT_SPACING_MAP as At, createSiteCommandSiteRef as B, getTextColorForBackground as Bn, oklchToHexGamut as Br, resolvedButtonColourDeclarations as Bt, pagePathForCommand as C, boxBackgroundSchema as Cn, defineNoBlockDesignCapability as Cr, NAV_LINK_TEXT_SIZE_MAP as Ct, require_react as D, resolveTokenOrCssColorValue as Dn, headingDecorationColorTokenValues as Dr, navEffectPolicy as Dt, require_jsx_runtime as E, resolveThemeColorReferenceValue as En, featureCardCompositionTreatmentValues as Er, navLinkOwnsUnderline as Et, createEventSeriesRef as F, generateCardCoreCss as Fn, onColorTokenName as Fr, ACCORDION_TRIGGER_LETTER_SPACING as Ft, headerForegroundCandidates as G, backgroundAppearanceColorTokenNames as Gn, resolveContextualButtonRole as Gt, refSlug as H, cardRoleSchema as Hn, contextualButtonSurfaceResetStyles as Ht, createFooterRef as I, getDefaultCardVariants as In, ORDINARY_TEXT_MIN_CONTRAST_RATIO as Ir, ICON_CONFIG as It, readOpeningForegroundProjection as J, TokenResolver as Jn, generateBaseInteractiveCSS as Jt, headerLeafTextColor as K, backgroundAppearanceQuickColorTokenNames as Kn, generateDefaultButtonSystem as Kt, createFormRef as L, generateCardCss as Ln, contrastRatio as Lr, ICON_SIZE_PX as Lt, createCommandRef as M, mediaSchema as Mn, tileTreatmentValues as Mr, ACCORDION_OPEN_ACCENT_THICKNESS as Mt, createContentEntryRef as N, mediaTypeDiscriminants as Nn, visibleHeadingDecorationFamilyValues as Nr, ACCORDION_SPACING_MAP as Nt, pickShadeTowardBackground as O, pageDesignEditorBackgroundOverrideOwner as On, introCompositionTreatmentValues as Or, FONT_WEIGHT_MAP as Ot, createEventCategoryRef as P, persistableMediaSchema as Pn, isOnColorBackgroundCandidateToken as Pr, ACCORDION_TRIGGER_FONT_SCALE as Pt, headerRootPaint as Q, mergeStyles as Qn, themeButtonSizeClassName as Qt, createNavigationRef as R, renderCssDeclarations as Rn, featuresFromHex as Rr, generateButtonCss as Rt, pageIdentifierForCommand as S, boxBackgroundOverlaySchema as Sn, defineFrameCapability as Sr, NAV_LINK_PADDING_X_MAP as St, eventBookingOfferIntentSchema as T, resolveOnColorForThemeToken as Tn, eyebrowTreatmentValues as Tr, navLinkOwnsHoverForeground as Tt, slugPart as U, cardVariantRoleValues as Un, contextualButtonSurfaceStyles as Ut, createVenueRef as V, generateShadowValue$1 as Vn, __toESM as Vr, resolvedButtonFocusDeclarations as Vt, effectiveHeaderNavLinkStyle as W, normalizeCardRole as Wn, deriveButtonSurfaceContext as Wt, mergeHeaderNavTypographyPreference as X, borderColorStyle as Xn, parseThemeButtonVariantId as Xt, getNavLinkPreset as Y, backgroundColorStyle as Yn, generateInteractiveChildInheritanceCSS as Yt, readHeaderNavFontSizePreference as Z, cssVarValue as Zn, themeButtonExtraClassesWithTrailingIcon as Zt, getSiteCommandMetadata as _, composeBoundary as _n, asDesignBlockId as _r, parseBlockCustomCssRules as _t, classifySdkEventSchedule as a, gradientToCss as an, DEFAULT_SURFACE_TEXT_COLOR as ar, BREAKPOINT_LG as at, entryIdentifierForCommand as b, anglePolygonCss as bn, assertNever$1 as br, NAV_LINK_LETTER_SPACING_MAP as bt, isEventVisibility as c, parseGradientPresetId as cn, surfaceForegroundStyle as cr, BREAKPOINT_XL as ct, stableStringify as d, VARIANT_ROLES as dn, parseBlockColorToken as dr, MQ_SM_DOWN as dt, parseThemeForPersistence as en, textColorStyle as er, themeSchema as et, createDryRunReport as f, buttonVariantIdSchema as fn, parseColorTokenParts as fr, MQ_SM_UP as ft, evaluateSiteCommandExposure as g, boundaryLayout as gn, asAppearancePresetId as gr, getComponentShadowVar as gt, evaluateSiteCommandBatchExposure as h, pageDesignIntentSchemaVersion as hn, defineBlockDesignMetadata as hr, getComponentBorderWidthPx as ht, sdkEventUpsertOptionalFieldSchemas as i, footerModeSchema as in, resolveSurfaceForeground as ir, renderHeaderPaintTargetRules as it, createBlocksRef as j, mediaIdentifierSchema as jn, testimonialsCompositionTreatmentValues as jr, LABEL_FONT_SIZE_MAP as jt, generateCssVars as k, siteChromeBackgroundOverrideOwner as kn, makeBoundaryId as kr, INPUT_FONT_SIZE_MAP as kt, venueManagementFields as l, buttonStyleSelectionSchema as ln, surfaceForegroundValue as lr, MQ_LG_UP as lt, mapEventCommandContent as m, pageDesignBoundaryTreatmentSchema as mn, defaultThemeTokens as mr, headerVariantValues as mt, sdkCourseBookingSettingsSchema as n, formatButtonColourContractError as nn, selectLogoVariant as nr, generateMobileOverlayBackdropFilterFix as nt, classifySdkEventBookingOffer as o, GRADIENT_PRESET_IDS as on, surfaceForegroundDeclarations as or, BREAKPOINT_MD as ot, parseAndPlanCommandBatchForTypes as p, unsafeAsOpaqueButtonColourToken as pn, buildThemeTokens as pr, MQ_TABLET_ONLY as pt, transparentHeaderForegroundInput as q, parseBackgroundAppearanceColorToken as qn, generateEffectsCSS as qt, sdkEventOccurrenceInputSchema as r, generateShadowValue as rn, surfaceToneFromForegroundToken as rr, getHeaderPaintTargets as rt, eventVisibilitySchema as s, getGradientPreset as sn, surfaceForegroundInputFromResolved as sr, BREAKPOINT_SM as st, classifySdkEventTiming as t, compileButtonColourStates as tn, textColorValue as tr, generateHeaderScrollTreatmentStyles as tt, sortRecord as u, PUBLIC_BUTTON_ROLES as un, parseThemeColorToken as ur, MQ_MD_DOWN as ut, blockIdentifierForCommand as v, sectionFactsForContent as vn, asPageFlowPresetId as vr, parseCustomCssRules as vt, hasConfiguredBookingVertical as w, isCssColorLiteral as wn, eventListTreatmentValues as wr, navLinkHoverColorCandidate as wt, pageIdentifierForBlockCommand as x, transitionCatalogueEntries as xn, defineBlockDesignCapability as xr, NAV_LINK_PADDING_MAP as xt, blockOrderIdentifiersForCommand as y, boundaryRuntimeMetricsForDecorativeTransition as yn, asSiteStyleId as yr, NAV_LINK_BORDER_RADIUS_MAP as yt, createPageRef as z, expandPalette as zn, hexToRgb as zr, RESOLVED_BUTTON_DISABLED_DECLARATIONS as zt };
|