@riverbankcms/sdk 0.108.0 → 0.109.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.
Files changed (189) hide show
  1. package/README.md +21 -1
  2. package/dist/{PreviewEditorSidebar-BBT-kZAM.mjs → PreviewEditorSidebar-DWqwfy3_.mjs} +7 -22
  3. package/dist/{PreviewEditorUI-D69xNKT2.mjs → PreviewEditorUI-B7OL4sfR.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-RRu7wyQ5.mjs → PreviewQueryProvider-D0y2l8JN.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-CuDjXH6o.mjs → SdkPreviewModeRuntime-D8Gt9Gbe.mjs} +212 -153
  6. package/dist/{SiteChromeCustomizeContext-BQUY2dEk.mjs → SiteChromeCustomizeContext-DUpalEdL.mjs} +1919 -1032
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +11244 -10741
  8. package/dist/_dts/api/src/bookingAdjustments.d.ts +5 -0
  9. package/dist/_dts/api/src/bookingOperations.d.ts +4 -0
  10. package/dist/_dts/api/src/bookingRecords.d.ts +2 -17
  11. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +6 -6
  12. package/dist/_dts/api/src/common/envelope.d.ts +7 -0
  13. package/dist/_dts/api/src/courses.d.ts +1 -0
  14. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +5 -5
  15. package/dist/_dts/api/src/endpoints.d.ts +35 -0
  16. package/dist/_dts/api/src/index.d.ts +2 -2
  17. package/dist/_dts/api/src/participantMessaging.d.ts +18 -12
  18. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +1 -0
  19. package/dist/_dts/api/src/sdkContracts.d.ts +247 -1
  20. package/dist/_dts/api/src/siteInfrastructure.d.ts +13 -0
  21. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +1 -5
  22. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +29 -1
  23. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +33 -27
  24. package/dist/_dts/block-form/src/widgets/BackgroundGradientWidget/BackgroundGradientField.d.ts +9 -0
  25. package/dist/_dts/block-form/src/widgets/BackgroundGradientWidget/BackgroundGradientWidget.d.ts +3 -4
  26. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -4
  27. package/dist/_dts/blocks/src/index.d.ts +1 -1
  28. package/dist/_dts/blocks/src/system/blocks/audio-player.d.ts +3 -0
  29. package/dist/_dts/blocks/src/system/blocks/body-text.d.ts +3 -0
  30. package/dist/_dts/blocks/src/system/blocks/contact-newsletter.d.ts +3 -0
  31. package/dist/_dts/blocks/src/system/blocks/course-registration.d.ts +1 -22
  32. package/dist/_dts/blocks/src/system/blocks/cta-full.d.ts +3 -0
  33. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -22
  34. package/dist/_dts/blocks/src/system/blocks/file-download.d.ts +3 -0
  35. package/dist/_dts/blocks/src/system/blocks/form.d.ts +3 -0
  36. package/dist/_dts/blocks/src/system/blocks/image-gallery.d.ts +3 -0
  37. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +3 -0
  38. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +3 -0
  39. package/dist/_dts/blocks/src/system/blocks/site-header.d.ts +3 -0
  40. package/dist/_dts/blocks/src/system/blocks/team-members.d.ts +3 -0
  41. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +3 -0
  42. package/dist/_dts/blocks/src/system/blocks/video-grid.d.ts +3 -0
  43. package/dist/_dts/blocks/src/system/constants/background.d.ts +1 -1
  44. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +16 -6
  45. package/dist/_dts/blocks/src/system/defineBlock.d.ts +9 -1
  46. package/dist/_dts/blocks/src/system/fields/ctas.d.ts +8 -0
  47. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +31 -0
  48. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +6 -6
  49. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +3 -3
  50. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -1
  51. package/dist/_dts/content-model/src/content-types/entryValidation.d.ts +31 -0
  52. package/dist/_dts/content-model/src/content-types/scaffold.d.ts +58 -0
  53. package/dist/_dts/content-model/src/content-types/sentinels.d.ts +36 -0
  54. package/dist/_dts/content-model/src/index.d.ts +3 -0
  55. package/dist/_dts/core/src/booking-date-time.d.ts +8 -0
  56. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +3071 -3157
  57. package/dist/_dts/media-core/src/fileMimeDetection.d.ts +16 -0
  58. package/dist/_dts/media-core/src/fileStorage.d.ts +8 -0
  59. package/dist/_dts/media-core/src/storagePathDomain.d.ts +2 -0
  60. package/dist/_dts/media-core/src/uploadVerificationContract.d.ts +3 -0
  61. package/dist/_dts/preview-next/src/client/blocks/BlockToolbar.d.ts +2 -0
  62. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.state.d.ts +1 -0
  63. package/dist/_dts/preview-next/src/client/preview/styleConfiguratorApplyPayload.d.ts +1 -1
  64. package/dist/_dts/sdk/src/cli/commands/audit.d.ts +1 -1
  65. package/dist/_dts/sdk/src/cli/commands/compare.d.ts +1 -1
  66. package/dist/_dts/sdk/src/cli/commands/deploy.d.ts +1 -1
  67. package/dist/_dts/sdk/src/cli/commands/drafts.d.ts +18 -0
  68. package/dist/_dts/sdk/src/cli/commands/entry.d.ts +15 -0
  69. package/dist/_dts/sdk/src/cli/commands/init-docs.d.ts +2 -2
  70. package/dist/_dts/sdk/src/cli/commands/publish-all.d.ts +1 -1
  71. package/dist/_dts/sdk/src/cli/commands/pull.d.ts +1 -0
  72. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +1 -1
  73. package/dist/_dts/sdk/src/cli/commands/push/options.d.ts +3 -0
  74. package/dist/_dts/sdk/src/cli/commands/push-execute.d.ts +12 -1
  75. package/dist/_dts/sdk/src/cli/commands/setup.d.ts +1 -0
  76. package/dist/_dts/sdk/src/cli/commands/verify.d.ts +1 -0
  77. package/dist/_dts/sdk/src/cli/helpers.d.ts +22 -1
  78. package/dist/_dts/sdk/src/cli/index.d.ts +4 -0
  79. package/dist/_dts/sdk/src/cli/output.d.ts +88 -0
  80. package/dist/_dts/sdk/src/cli/program.d.ts +29 -0
  81. package/dist/_dts/sdk/src/cli/push-config.d.ts +1 -1
  82. package/dist/_dts/sdk/src/client/management/catalog.d.ts +10 -0
  83. package/dist/_dts/sdk/src/client/management/http.d.ts +7 -1
  84. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  85. package/dist/_dts/sdk/src/client/management/types.d.ts +43 -19
  86. package/dist/_dts/sdk/src/date-time/index.d.ts +68 -0
  87. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -0
  88. package/dist/_dts/sdk/src/version.d.ts +1 -1
  89. package/dist/_dts/site-commands/src/commands.d.ts +893 -823
  90. package/dist/_dts/theme-core/src/accordions/types.d.ts +1 -1
  91. package/dist/_dts/theme-core/src/buttons/core.d.ts +15 -46
  92. package/dist/_dts/theme-core/src/buttons/effectClassification.d.ts +147 -0
  93. package/dist/_dts/theme-core/src/buttons/effects/types.d.ts +2 -3
  94. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +1 -1
  95. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  96. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +44 -32
  97. package/dist/_dts/theme-core/src/buttons/presets.d.ts +4 -25
  98. package/dist/_dts/theme-core/src/buttons/resolveButtonColourStates.d.ts +100 -0
  99. package/dist/_dts/theme-core/src/buttons/resolvedButtonCss.d.ts +16 -0
  100. package/dist/_dts/theme-core/src/buttons/types.d.ts +101 -83
  101. package/dist/_dts/theme-core/src/cards/types.d.ts +16 -16
  102. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  103. package/dist/_dts/theme-core/src/gradients/presets.d.ts +200 -8
  104. package/dist/_dts/theme-core/src/gradients/utils.d.ts +6 -2
  105. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +87 -126
  106. package/dist/_dts/theme-core/src/mock-themes/modern-minimal.d.ts +1 -1
  107. package/dist/_dts/theme-core/src/navigation/types.d.ts +2 -2
  108. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +2 -2
  109. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +5 -8
  110. package/dist/_dts/theme-core/src/palette/index.d.ts +0 -1
  111. package/dist/_dts/theme-core/src/schema.d.ts +112 -138
  112. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  113. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +10 -10
  114. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +3 -2
  115. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +13 -6
  116. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +29 -8
  117. package/dist/_dts/theme-core/src/site-styles/styleTags.d.ts +2 -2
  118. package/dist/cli/index.mjs +67956 -66745
  119. package/dist/cli/init-docs/content/cli-reference.md +50 -7
  120. package/dist/cli/init-docs/content/content-management.md +7 -4
  121. package/dist/cli/init-docs/content/workflow-publish.md +5 -1
  122. package/dist/client/{DiscountCodeDisclosure-D6TOf6Hi.mjs → DiscountCodeDisclosure-DU1ygxU2.mjs} +1 -1
  123. package/dist/client/{EventCombined.client-Dao3zxRk.mjs → EventCombined.client-DB5zagCo.mjs} +3 -3
  124. package/dist/client/{EventListing.client-B2NNt4y1.mjs → EventListing.client-CTX9JHBL.mjs} +2 -2
  125. package/dist/client/{LeafletStaticMap-BhwFo16w.mjs → LeafletStaticMap-BYImhgiB.mjs} +1 -1
  126. package/dist/client/{MagicLinkForm-BWnB7cMR.mjs → MagicLinkForm-CRkw_M3l.mjs} +2 -2
  127. package/dist/client/{StepIndicator-TzRiQ0BY.mjs → StepIndicator-BnmVBJJh.mjs} +2 -2
  128. package/dist/client/{accordion-BPy-AJ4_.mjs → accordion-CeGreumd.mjs} +1 -1
  129. package/dist/client/{bookingForm-BL4PGZds.mjs → bookingForm-CSzFNrpN.mjs} +6 -6
  130. package/dist/client/bookings.mjs +512 -252
  131. package/dist/client/{buttons-EBjyslaX.mjs → buttons-DoFV7KDL.mjs} +493 -230
  132. package/dist/client/{carousel-C2b7Cn9-.mjs → carousel-B2jDtogf.mjs} +1 -1
  133. package/dist/client/{cart-BQcbXIVD.mjs → cart-D-jZ-vaY.mjs} +3 -3
  134. package/dist/client/{checkout-DEmpGRJu.mjs → checkout-Dza-LA7N.mjs} +3 -3
  135. package/dist/client/client.mjs +1673 -785
  136. package/dist/client/{colorStyles-jZS6KfkP.mjs → colorStyles-CrgzoXsm.mjs} +1 -30
  137. package/dist/client/{courseRegistration-BqRZPmsq.mjs → courseRegistration-TYkNychb.mjs} +7 -7
  138. package/dist/client/{eventCalendar-B8N_tSl5.mjs → eventCalendar-BI_90SsT.mjs} +5 -5
  139. package/dist/client/{eventCombined-Gjn6gtq7.mjs → eventCombined-mNrpryE9.mjs} +2 -2
  140. package/dist/client/{eventDetails-M-VCxA-r.mjs → eventDetails-D4GVlCIY.mjs} +3 -3
  141. package/dist/client/{eventListing-DR19hqYi.mjs → eventListing-YhKN018y.mjs} +2 -2
  142. package/dist/client/{eventRegistration-BR02a0k3.mjs → eventRegistration-CdHGtilr.mjs} +7 -7
  143. package/dist/client/foregroundContract-xICPq59x.mjs +31 -0
  144. package/dist/client/{form-LY2xIbwg.mjs → form-AcAL45rA.mjs} +2 -2
  145. package/dist/client/{headerSection-CYwwXJfh.mjs → headerSection-uEC52ueD.mjs} +3 -3
  146. package/dist/client/hooks.mjs +890 -497
  147. package/dist/client/{locationMap-DWnBXrFq.mjs → locationMap-_wJDSUYe.mjs} +3 -3
  148. package/dist/client/{media-DHtSxS5V.mjs → media-BK0O3b-U.mjs} +3 -2
  149. package/dist/client/{newsletterForm-D9s-fQ2P.mjs → newsletterForm-DrArsb2B.mjs} +2 -2
  150. package/dist/client/{pricing-DDq80oYc.mjs → pricing-BSYVghwo.mjs} +1 -1
  151. package/dist/client/{productDetail-B0SbWd45.mjs → productDetail-Dyk90sMs.mjs} +3 -3
  152. package/dist/client/{productList-PwYlZn0y.mjs → productList-BCKThwAI.mjs} +3 -3
  153. package/dist/client/{propsDecoder-BRWBiH7i.mjs → propsDecoder-CkXxkdSR.mjs} +63 -24
  154. package/dist/client/rendering/client.mjs +1034 -486
  155. package/dist/client/rendering/islands.mjs +19 -19
  156. package/dist/client/rendering.mjs +1832 -760
  157. package/dist/client/{shop-CMwZPYME.mjs → shop-DeltVH10.mjs} +6 -6
  158. package/dist/client/{shop-commerce-BuxsFate.mjs → shop-commerce-BD9Jsm2Z.mjs} +6 -6
  159. package/dist/client/{shop-commerce-store-boYlEJ91.mjs → shop-commerce-store-CmXD6FDS.mjs} +1 -1
  160. package/dist/client/{shopShared-D2naaOes.mjs → shopShared-B8v-lH_F.mjs} +2 -2
  161. package/dist/client/{useDiscountCodeUi-D9RHpVoj.mjs → useDiscountCodeUi-DbWJN8ld.mjs} +1 -1
  162. package/dist/client/{useEventFilters-igGiK1re.mjs → useEventFilters-EJIMUnqT.mjs} +46 -69
  163. package/dist/client/{usePolling-CI7MbsFB.mjs → usePolling-Cmh14fc7.mjs} +2 -2
  164. package/dist/preview-next/before-render.mjs +35 -0
  165. package/dist/preview-next/client/runtime.mjs +4 -4
  166. package/dist/preview-next/middleware.mjs +35 -0
  167. package/dist/{previewAuthStore-a_cBD28m.mjs → previewAuthStore-CTO7CRPk.mjs} +35 -0
  168. package/dist/{sdk-runtime-MBzD-GmP.mjs → sdk-runtime-Dou0GAav.mjs} +2 -2
  169. package/dist/{separator-5ahRBC85.mjs → separator-CI5wZNXT.mjs} +1 -1
  170. package/dist/server/components.mjs +1832 -784
  171. package/dist/server/config-validation.mjs +890 -497
  172. package/dist/server/config.mjs +890 -497
  173. package/dist/server/data.mjs +890 -497
  174. package/dist/server/date-time.mjs +386 -0
  175. package/dist/server/index.mjs +36 -1
  176. package/dist/server/next.mjs +1868 -785
  177. package/dist/server/page-converter.mjs +849 -285
  178. package/dist/server/prebuild.mjs +1 -1
  179. package/dist/server/rendering/server.mjs +1553 -718
  180. package/dist/server/rendering.mjs +1832 -784
  181. package/dist/server/routing.mjs +933 -522
  182. package/dist/server/server.mjs +929 -501
  183. package/dist/server/theme-bridge.mjs +1408 -392
  184. package/dist/server/theme.mjs +471 -228
  185. package/dist/styles/index.css +5 -0
  186. package/package.json +7 -1
  187. package/dist/_dts/theme-core/src/buttons/presets/types.d.ts +0 -88
  188. package/dist/_dts/theme-core/src/buttons/utils/contrast.d.ts +0 -38
  189. package/dist/_dts/theme-core/src/palette/themePaletteCompatibility.d.ts +0 -57
@@ -58,6 +58,111 @@ const mediaSchema = z.object({
58
58
  transform: transformSchema.optional()
59
59
  });
60
60
  //#endregion
61
+ //#region ../theme-core/src/buttons/effectClassification.ts
62
+ /**
63
+ * Exhaustive live policy for every registered effect id.
64
+ *
65
+ * The resolver consumes the transition payload directly, so adding an effect
66
+ * cannot update classification without also defining its runtime behavior.
67
+ */
68
+ const BUTTON_EFFECT_POLICIES = {
69
+ "darken-background": {
70
+ classification: "colour-transition",
71
+ transition: {
72
+ kind: "adjust-shade",
73
+ direction: "darker"
74
+ }
75
+ },
76
+ "lighten-background": {
77
+ classification: "colour-transition",
78
+ transition: {
79
+ kind: "adjust-shade",
80
+ direction: "lighter"
81
+ }
82
+ },
83
+ "background-on-hover": {
84
+ classification: "colour-transition",
85
+ transition: { kind: "replace-background" }
86
+ },
87
+ "background-on-hover-alpha": {
88
+ classification: "unsupported-colour",
89
+ reason: "alpha_requires_surface"
90
+ },
91
+ "fade-background": {
92
+ classification: "unsupported-colour",
93
+ reason: "changes_element_opacity"
94
+ },
95
+ "opacity-reduce": {
96
+ classification: "unsupported-colour",
97
+ reason: "changes_element_opacity"
98
+ },
99
+ "gradient-text": {
100
+ classification: "unsupported-colour",
101
+ reason: "changes_foreground"
102
+ },
103
+ "text-color-change": {
104
+ classification: "unsupported-colour",
105
+ reason: "changes_foreground"
106
+ },
107
+ "border-color-change": {
108
+ classification: "unsupported-colour",
109
+ reason: "changes_border"
110
+ },
111
+ "aurora-glow": {
112
+ classification: "unsupported-background-overlay",
113
+ reason: "unresolved_background_overlay"
114
+ },
115
+ "cosmic-stardust": {
116
+ classification: "unsupported-background-overlay",
117
+ reason: "unresolved_background_overlay"
118
+ },
119
+ "gradient-border-fill": {
120
+ classification: "unsupported-background-overlay",
121
+ reason: "unresolved_background_overlay"
122
+ },
123
+ "animated-gradient-border-fill": {
124
+ classification: "unsupported-background-overlay",
125
+ reason: "unresolved_background_overlay"
126
+ },
127
+ "nav-underline": {
128
+ classification: "not-button",
129
+ reason: "not_button_effect"
130
+ },
131
+ "nav-frosted-base": {
132
+ classification: "not-button",
133
+ reason: "not_button_effect"
134
+ },
135
+ "nav-frosted-hover": {
136
+ classification: "not-button",
137
+ reason: "not_button_effect"
138
+ },
139
+ "nav-frosted-active": {
140
+ classification: "not-button",
141
+ reason: "not_button_effect"
142
+ },
143
+ "animated-gradient": { classification: "non-colour" },
144
+ "border-glow": { classification: "non-colour" },
145
+ "drop-shadow": { classification: "non-colour" },
146
+ "inner-shadow": { classification: "non-colour" },
147
+ "neumorphic-shadow": { classification: "non-colour" },
148
+ "outer-glow": { classification: "non-colour" },
149
+ "retro-3d-shadow": { classification: "non-colour" },
150
+ "scale-up": { classification: "non-colour" },
151
+ "scale-down": { classification: "non-colour" },
152
+ lift: { classification: "non-colour" },
153
+ "press-down": { classification: "non-colour" },
154
+ pop: { classification: "non-colour" },
155
+ "border-bottom-grow": { classification: "non-colour" },
156
+ "sketchy-border": { classification: "non-colour" },
157
+ "pulse-animation": { classification: "non-colour" }
158
+ };
159
+ function getButtonEffectPolicy(effectId) {
160
+ return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
161
+ }
162
+ function classifyButtonEffectId(effectId) {
163
+ return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
164
+ }
165
+ //#endregion
61
166
  //#region ../theme-core/src/tokens/colorTokenParts.ts
62
167
  function parseOpacityPercent(source) {
63
168
  const trimmed = source.trim();
@@ -189,6 +294,10 @@ function isThemeColorToken(value) {
189
294
  const parts = parseColorTokenParts(value);
190
295
  return parts.kind === "token" && isThemeColorTokenBase(parts.token);
191
296
  }
297
+ function parseThemeColorToken(value) {
298
+ if (!isThemeColorToken(value)) return null;
299
+ return value.trim();
300
+ }
192
301
  const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
193
302
  //#endregion
194
303
  //#region ../theme-core/src/buttons/types.ts
@@ -207,6 +316,20 @@ const VARIANT_ROLES = [
207
316
  "tertiary",
208
317
  "accent"
209
318
  ];
319
+ function parseOpaqueButtonColourToken(value) {
320
+ const token = parseThemeColorToken(value);
321
+ if (token === null) return null;
322
+ const parts = parseColorTokenParts(token);
323
+ return parts.kind === "token" && parts.opacityPercent === null ? token : null;
324
+ }
325
+ function unsafeAsOpaqueButtonColourToken(value) {
326
+ return value;
327
+ }
328
+ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => parseOpaqueButtonColourToken(value) !== null, "Button colour tokens must be opaque").transform((value) => {
329
+ const token = parseOpaqueButtonColourToken(value);
330
+ if (token === null) throw new Error("Invariant: opaque button token passed its schema");
331
+ return token;
332
+ });
210
333
  const cornerStyleSchema = z$1.enum([
211
334
  "square",
212
335
  "rounded",
@@ -308,7 +431,7 @@ const buttonSizesSchema = z$1.object({
308
431
  lg,
309
432
  xl: xl ?? lg
310
433
  }));
311
- const buttonGlobalSettingsSchema = z$1.object({
434
+ const canonicalButtonGlobalSettingsSchema = z$1.object({
312
435
  cornerStyle: cornerStyleSchema,
313
436
  cornerRadius: z$1.number().min(0).max(24).optional(),
314
437
  /**
@@ -339,11 +462,12 @@ const buttonGlobalSettingsSchema = z$1.object({
339
462
  paddingPreset: buttonPaddingPresetSchema.optional(),
340
463
  hoverTransform: hoverTransformSchema,
341
464
  hoverColor: hoverColorSchema,
342
- hoverColorToken: themeColorTokenSchema.optional(),
465
+ hoverColorToken: opaqueButtonColourTokenSchema.optional(),
343
466
  gradientStyle: gradientStyleSchema.optional(),
344
467
  gradientAngle: z$1.number().min(0).max(360).optional(),
345
468
  gradientSharpness: gradientSharpnessSchema.optional()
346
469
  });
470
+ const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
347
471
  const gradientDirectionSchema$1 = z$1.enum([
348
472
  "to-r",
349
473
  "to-l",
@@ -357,27 +481,33 @@ const gradientDirectionSchema$1 = z$1.enum([
357
481
  const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
358
482
  z$1.object({
359
483
  type: z$1.literal("solid"),
360
- colorToken: themeColorTokenSchema
484
+ colorToken: opaqueButtonColourTokenSchema
361
485
  }),
362
486
  z$1.object({
363
487
  type: z$1.literal("gradient"),
364
- stops: z$1.array(themeColorTokenSchema).min(2),
365
- direction: gradientDirectionSchema$1
488
+ stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
489
+ const [first, second, ...rest] = stops;
490
+ if (first === void 0 || second === void 0) throw new Error("Invariant: gradient stops passed the minimum length schema");
491
+ return [
492
+ first,
493
+ second,
494
+ ...rest
495
+ ];
496
+ }),
497
+ direction: gradientDirectionSchema$1,
498
+ foregroundToken: opaqueButtonColourTokenSchema
366
499
  }),
367
- z$1.object({ type: z$1.literal("transparent") })
500
+ z$1.object({
501
+ type: z$1.literal("surface"),
502
+ role: z$1.literal("link")
503
+ })
368
504
  ]);
369
505
  const effectApplicationSchema$1 = z$1.object({
370
506
  effectId: z$1.string(),
371
- options: z$1.record(z$1.string(), z$1.any()).optional()
372
- });
373
- const buttonBorderSchema = z$1.object({
374
- widthClass: z$1.string(),
375
- colorToken: themeColorTokenSchema
376
- });
377
- const buttonVariantAffordanceSchema = z$1.object({
378
- iconId: z$1.string().min(1),
379
- colorToken: themeColorTokenSchema.optional()
507
+ options: z$1.record(z$1.string(), z$1.json()).optional()
380
508
  });
509
+ const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
510
+ const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
381
511
  const variantShadowSchema = z$1.object({
382
512
  elevation: z$1.enum([
383
513
  "none",
@@ -418,27 +548,738 @@ const variantSizeOverridesSchema = z$1.object({
418
548
  * default (e.g. link-style variants declaring zero horizontal padding at
419
549
  * every size).
420
550
  */
421
- const buttonVariantSchema = z$1.object({
551
+ const canonicalButtonVariantSchema = z$1.object({
422
552
  id: z$1.string(),
423
553
  name: z$1.string().min(1).max(20),
424
554
  enabled: z$1.boolean(),
425
555
  priority: prioritySchema,
426
556
  background: buttonBackgroundSchema,
427
- textColorToken: themeColorTokenSchema,
428
557
  /** Tailwind radius token (e.g. `'rounded-md'`). Omit to inherit from `global.cornerStyle`. */
429
558
  borderRadius: z$1.string().optional(),
430
559
  shadow: variantShadowSchema.optional(),
431
560
  border: buttonBorderSchema.optional(),
432
- hoverBackgroundToken: z$1.string().optional(),
561
+ hoverBackgroundToken: opaqueButtonColourTokenSchema.optional(),
433
562
  effects: variantEffectsSchema.optional(),
434
563
  sizes: variantSizeOverridesSchema.optional(),
435
564
  affordance: buttonVariantAffordanceSchema.optional()
436
565
  });
566
+ /**
567
+ * Temporary persisted-theme admission boundary for issue #1206.
568
+ *
569
+ * Production inventory on 2026-07-22 found 22 rows using the old independent
570
+ * `textColorToken`/border colour shape. New source and all downstream code use
571
+ * only the canonical relationship. Delete this preprocessor after the reviewed
572
+ * production rewrite reports zero legacy rows.
573
+ */
574
+ const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
437
575
  const buttonSystemSchema = z$1.object({
438
576
  global: buttonGlobalSettingsSchema,
439
577
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
440
578
  sizes: buttonSizesSchema.optional()
441
579
  });
580
+ function normalizeLegacyButtonVariantInput(input) {
581
+ if (!isUnknownRecord(input)) return input;
582
+ const background = input.background;
583
+ if (!isUnknownRecord(background)) return input;
584
+ if (!(background.type === "transparent" || Object.prototype.hasOwnProperty.call(input, "textColorToken") || isUnknownRecord(input.border) && Object.prototype.hasOwnProperty.call(input.border, "colorToken") || hasLegacyButtonEffects(input.effects) || hasLegacyAlphaToken(input, background))) return input;
585
+ const { textColorToken, ...withoutLegacyText } = input;
586
+ const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
587
+ const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
588
+ const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
589
+ const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
590
+ if (background.type === "transparent") return {
591
+ ...withoutLegacyText,
592
+ background: {
593
+ type: "surface",
594
+ role: "link"
595
+ },
596
+ border: normalizedBorder,
597
+ affordance: normalizedAffordance,
598
+ effects: normalizedEffects,
599
+ hoverBackgroundToken
600
+ };
601
+ if (background.type === "gradient") {
602
+ const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
603
+ return {
604
+ ...withoutLegacyText,
605
+ background: {
606
+ ...background,
607
+ stops,
608
+ foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
609
+ },
610
+ border: normalizedBorder,
611
+ affordance: normalizedAffordance,
612
+ effects: normalizedEffects,
613
+ hoverBackgroundToken
614
+ };
615
+ }
616
+ return {
617
+ ...withoutLegacyText,
618
+ background: background.type === "solid" ? {
619
+ ...background,
620
+ colorToken: stripLegacyTokenAlpha(background.colorToken)
621
+ } : background,
622
+ border: normalizedBorder,
623
+ affordance: normalizedAffordance,
624
+ effects: normalizedEffects,
625
+ hoverBackgroundToken
626
+ };
627
+ }
628
+ function normalizeLegacyButtonGlobalInput(input) {
629
+ if (!isUnknownRecord(input)) return input;
630
+ const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
631
+ return hoverColorToken === input.hoverColorToken ? input : {
632
+ ...input,
633
+ hoverColorToken
634
+ };
635
+ }
636
+ function hasLegacyAlphaToken(input, background) {
637
+ const stops = isUnknownArray(background.stops) ? background.stops : [];
638
+ return [
639
+ input.hoverBackgroundToken,
640
+ background.colorToken,
641
+ background.foregroundToken,
642
+ ...stops
643
+ ].some((token) => stripLegacyTokenAlpha(token) !== token);
644
+ }
645
+ function stripLegacyTokenAlpha(value) {
646
+ if (typeof value !== "string") return value;
647
+ const slashIndex = value.indexOf("/");
648
+ return slashIndex < 0 ? value : value.slice(0, slashIndex);
649
+ }
650
+ function hasLegacyButtonEffects(input) {
651
+ if (!isUnknownRecord(input)) return false;
652
+ return [
653
+ "base",
654
+ "hover",
655
+ "active",
656
+ "focus"
657
+ ].some((state) => {
658
+ const effects = input[state];
659
+ return Array.isArray(effects) && effects.some((effect) => {
660
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
661
+ const classification = classifyButtonEffectId(effect.effectId);
662
+ if (classification !== "colour-transition" && classification !== "non-colour") return true;
663
+ return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
664
+ });
665
+ });
666
+ }
667
+ function normalizeLegacyButtonEffects(input, backgroundType) {
668
+ if (!isUnknownRecord(input)) return input;
669
+ const normalized = {};
670
+ for (const state of [
671
+ "base",
672
+ "hover",
673
+ "active",
674
+ "focus"
675
+ ]) {
676
+ const effects = input[state];
677
+ if (!Array.isArray(effects)) continue;
678
+ const safeEffects = effects.flatMap((effect) => {
679
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
680
+ const classification = classifyButtonEffectId(effect.effectId);
681
+ if (classification === "non-colour") return [effect];
682
+ if (classification !== "colour-transition") return [];
683
+ if (effect.effectId === "background-on-hover") {
684
+ const options = isUnknownRecord(effect.options) ? effect.options : null;
685
+ const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
686
+ return colorToken === null ? [] : [{
687
+ ...effect,
688
+ options: {
689
+ ...options,
690
+ colorToken
691
+ }
692
+ }];
693
+ }
694
+ if (backgroundType !== "solid") return [];
695
+ const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
696
+ const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
697
+ return [{
698
+ ...effect,
699
+ options: {
700
+ ...isUnknownRecord(effect.options) ? effect.options : {},
701
+ amount
702
+ }
703
+ }];
704
+ });
705
+ if (safeEffects.length > 0) normalized[state] = safeEffects;
706
+ }
707
+ return Object.keys(normalized).length === 0 ? void 0 : normalized;
708
+ }
709
+ function isUnknownRecord(value) {
710
+ return typeof value === "object" && value !== null && !Array.isArray(value);
711
+ }
712
+ function isUnknownArray(value) {
713
+ return Array.isArray(value);
714
+ }
715
+ //#endregion
716
+ //#region ../theme-core/src/buttons/constants.ts
717
+ /**
718
+ * Shared constants for button CSS generation
719
+ *
720
+ * These mappings convert Tailwind-style tokens to actual CSS values.
721
+ * Centralized here to eliminate duplication across multiple files.
722
+ */
723
+ /**
724
+ * Map Tailwind-style gradient direction tokens to CSS gradient directions
725
+ */
726
+ const GRADIENT_DIRECTION_MAP$1 = {
727
+ "to-r": "to right",
728
+ "to-l": "to left",
729
+ "to-t": "to top",
730
+ "to-b": "to bottom",
731
+ "to-br": "to bottom right",
732
+ "to-bl": "to bottom left",
733
+ "to-tr": "to top right",
734
+ "to-tl": "to top left"
735
+ };
736
+ //#endregion
737
+ //#region ../theme-core/src/palette/utils/colorConversion.ts
738
+ function hexToRgb(hex) {
739
+ const h = hex.replace(/^#/, "");
740
+ const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
741
+ if (m.length !== 6) throw new Error("Bad hex");
742
+ const int = parseInt(m, 16);
743
+ return {
744
+ r: int >> 16 & 255,
745
+ g: int >> 8 & 255,
746
+ b: int & 255
747
+ };
748
+ }
749
+ function srgbToLinear(u) {
750
+ const x = u / 255;
751
+ return x <= .04045 ? x / 12.92 : Math.pow((x + .055) / 1.055, 2.4);
752
+ }
753
+ //#endregion
754
+ //#region ../theme-core/src/palette/utils/contrast.ts
755
+ function relativeLuminance(r, g, b) {
756
+ const R = srgbToLinear(r), G = srgbToLinear(g), B = srgbToLinear(b);
757
+ return .2126 * R + .7152 * G + .0722 * B;
758
+ }
759
+ function contrastRatio(hex1, hex2) {
760
+ const { r: r1, g: g1, b: b1 } = hexToRgb(hex1);
761
+ const { r: r2, g: g2, b: b2 } = hexToRgb(hex2);
762
+ const L1 = relativeLuminance(r1, g1, b1);
763
+ const L2 = relativeLuminance(r2, g2, b2);
764
+ const [hi, lo] = L1 >= L2 ? [L1, L2] : [L2, L1];
765
+ return (hi + .05) / (lo + .05);
766
+ }
767
+ //#endregion
768
+ //#region ../theme-core/src/palette/utils/textColor.ts
769
+ /**
770
+ * Pick the better of black/white text for a given background using actual
771
+ * contrast ratios, not a simple luminance cutoff.
772
+ *
773
+ * @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
774
+ * @param minContrast - Preferred minimum contrast ratio for body-sized text
775
+ * @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
776
+ */
777
+ function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
778
+ const { r, g, b } = parseColorToRgb(background);
779
+ const luminance = relativeLuminance(r, g, b);
780
+ const blackContrast = (luminance + .05) / .05;
781
+ const whiteContrast = 1.05 / (luminance + .05);
782
+ if (blackContrast >= minContrast && whiteContrast < minContrast) return "#000000";
783
+ if (whiteContrast >= minContrast && blackContrast < minContrast) return "#FFFFFF";
784
+ return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
785
+ }
786
+ /**
787
+ * Backwards-compatible alias for callers that want the best black/white text
788
+ * color for a background.
789
+ */
790
+ function getTextColorForBackground(background) {
791
+ return pickBlackOrWhiteTextColor(background);
792
+ }
793
+ function parseColorToRgb(input) {
794
+ const trimmed = input.trim();
795
+ if (trimmed.startsWith("#")) return hexToRgb(trimmed);
796
+ 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);
797
+ if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) return {
798
+ r: clamp255(Number(rgbMatch[1])),
799
+ g: clamp255(Number(rgbMatch[2])),
800
+ b: clamp255(Number(rgbMatch[3]))
801
+ };
802
+ throw new Error("Unsupported color format");
803
+ }
804
+ function clamp255(value) {
805
+ if (Number.isNaN(value)) return 0;
806
+ return Math.max(0, Math.min(255, value));
807
+ }
808
+ //#endregion
809
+ //#region ../theme-core/src/palette/utils/onColor.ts
810
+ const BACKGROUND_ROLE_NAMES = /* @__PURE__ */ new Set([
811
+ "primary",
812
+ "secondary",
813
+ "accent",
814
+ "success",
815
+ "warning",
816
+ "destructive",
817
+ "info",
818
+ "background",
819
+ "surface",
820
+ "surfaceAlt",
821
+ "muted",
822
+ "neutral"
823
+ ]);
824
+ function onColorTokenName(backgroundToken) {
825
+ return `on${backgroundToken.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
826
+ }
827
+ function isOnColorBackgroundCandidateToken(token) {
828
+ const role = token.match(/^(.*?)(?:-\d{2,3})?$/)?.[1] ?? token;
829
+ return BACKGROUND_ROLE_NAMES.has(role);
830
+ }
831
+ //#endregion
832
+ //#region ../theme-core/src/surfaces/foregroundRoles.ts
833
+ const SURFACE_FOREGROUND_ROLES = [
834
+ "text",
835
+ "heading",
836
+ "link",
837
+ "supporting"
838
+ ];
839
+ //#endregion
840
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
841
+ const SURFACE_FOREGROUND_CSS_VARS = {
842
+ text: "--rb-surface-text-color",
843
+ heading: "--rb-surface-heading-color",
844
+ link: "--rb-surface-link-color",
845
+ supporting: "--rb-surface-supporting-color"
846
+ };
847
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
848
+ const DEFAULT_ROLE_FALLBACKS = {
849
+ text: DEFAULT_SURFACE_TEXT_COLOR,
850
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
851
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
852
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
853
+ };
854
+ function requireSurfaceColorValue(role, value) {
855
+ const normalized = value.trim();
856
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
857
+ return normalized;
858
+ }
859
+ function completeSurfaceForegrounds(input) {
860
+ const text = requireSurfaceColorValue("text", input.text);
861
+ return {
862
+ text,
863
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
864
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
865
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
866
+ };
867
+ }
868
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
869
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
870
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
871
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
872
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
873
+ }
874
+ function surfaceForegroundDeclarations(input) {
875
+ const foregrounds = completeSurfaceForegrounds(input);
876
+ return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
877
+ }
878
+ //#endregion
879
+ //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
880
+ const BUTTON_VISUAL_STATES = [
881
+ "base",
882
+ "hover",
883
+ "focus",
884
+ "active",
885
+ "disabled"
886
+ ];
887
+ const BUTTON_CONTRAST_POLICY = {
888
+ textMinimumRatio: 4.5,
889
+ nonTextMinimumRatio: 3,
890
+ gradientSamplesPerSegment: 20,
891
+ alphaBackgrounds: "reject-without-known-surface",
892
+ disabled: "retain-full-colour-contract"
893
+ };
894
+ function compileButtonColourStates(input) {
895
+ const baseTransition = applyColourEffects({
896
+ variantId: input.variant.id,
897
+ state: "base",
898
+ treatment: input.variant.background,
899
+ effects: input.variant.effects?.base,
900
+ palette: input.palette
901
+ });
902
+ if (!baseTransition.ok) return baseTransition;
903
+ const stateTreatments = {
904
+ base: baseTransition.treatment,
905
+ hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
906
+ focus: baseTransition.treatment,
907
+ active: baseTransition.treatment,
908
+ disabled: baseTransition.treatment
909
+ };
910
+ const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
911
+ for (const state of [
912
+ "hover",
913
+ "focus",
914
+ "active"
915
+ ]) {
916
+ const transition = applyColourEffects({
917
+ variantId: input.variant.id,
918
+ state,
919
+ treatment: stateTreatments[state],
920
+ effects: input.variant.effects?.[state],
921
+ palette: input.palette
922
+ });
923
+ if (!transition.ok) return transition;
924
+ stateTreatments[state] = transition.treatment;
925
+ if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
926
+ }
927
+ const resolved = {};
928
+ for (const state of BUTTON_VISUAL_STATES) {
929
+ const result = resolveTreatment({
930
+ variantId: input.variant.id,
931
+ state,
932
+ treatment: stateTreatments[state],
933
+ palette: input.palette
934
+ });
935
+ if (!result.ok) return result;
936
+ resolved[state] = result.colour;
937
+ }
938
+ return {
939
+ ok: true,
940
+ states: resolved,
941
+ focusIndicator: resolveFocusIndicator(resolved.focus),
942
+ nonColourEffects
943
+ };
944
+ }
945
+ function formatButtonColourContractError(error) {
946
+ switch (error.tag) {
947
+ case "unknown_palette_token": return `Button "${error.variantId}" ${error.state} state uses unknown palette token "${error.token}".`;
948
+ case "missing_on_colour": return `Button "${error.variantId}" ${error.state} state cannot derive ${error.foregroundToken} from ${error.backgroundToken}.`;
949
+ case "insufficient_contrast": return `Button "${error.variantId}" ${error.state} state has ${error.ratio.toFixed(2)}:1 contrast (${error.requiredRatio}:1 required).`;
950
+ case "unsupported_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" is not contrast-safe (${error.reason}).`;
951
+ case "invalid_colour_effect_options": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" has an invalid ${error.option} option.`;
952
+ case "inapplicable_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" cannot apply to a ${error.treatment} treatment.`;
953
+ case "unknown_button_effect": return `Button "${error.variantId}" ${error.state} uses unknown effect "${error.effectId}".`;
954
+ }
955
+ }
956
+ function hoverTreatment(variant, global, fallback) {
957
+ const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
958
+ return token === void 0 ? fallback : {
959
+ type: "solid",
960
+ colorToken: token
961
+ };
962
+ }
963
+ function applyColourEffects(input) {
964
+ let treatment = input.treatment;
965
+ const nonColourEffects = [];
966
+ for (const effect of input.effects ?? []) {
967
+ const policy = getButtonEffectPolicy(effect.effectId);
968
+ if (policy === null) return unknownEffect(input, effect);
969
+ switch (policy.classification) {
970
+ case "non-colour":
971
+ nonColourEffects.push(effect);
972
+ break;
973
+ case "unsupported-colour":
974
+ case "unsupported-background-overlay":
975
+ case "not-button": return unsupportedEffect(input, effect, policy.reason);
976
+ case "colour-transition":
977
+ if (policy.transition.kind === "adjust-shade") {
978
+ if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
979
+ const amount = readShadeAmount(effect);
980
+ if (amount === null) return invalidEffectOption(input, effect, "amount");
981
+ const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
982
+ treatment = {
983
+ type: "solid",
984
+ colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
985
+ };
986
+ break;
987
+ }
988
+ if (policy.transition.kind === "replace-background") {
989
+ const colorToken = readOpaqueToken(effect, "colorToken", "neutral-800");
990
+ if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
991
+ treatment = {
992
+ type: "solid",
993
+ colorToken
994
+ };
995
+ break;
996
+ }
997
+ return unknownEffect(input, effect);
998
+ }
999
+ }
1000
+ return {
1001
+ ok: true,
1002
+ treatment,
1003
+ nonColourEffects
1004
+ };
1005
+ }
1006
+ function resolveTreatment(input) {
1007
+ const treatment = input.treatment;
1008
+ switch (treatment.type) {
1009
+ case "surface": return {
1010
+ ok: true,
1011
+ colour: {
1012
+ kind: "surface",
1013
+ role: treatment.role,
1014
+ backgroundCss: "transparent",
1015
+ foregroundCss: surfaceForegroundValue(treatment.role)
1016
+ }
1017
+ };
1018
+ case "solid": return resolveSolid(input, treatment.colorToken);
1019
+ case "gradient": return resolveGradient(input, treatment);
1020
+ }
1021
+ }
1022
+ function resolveFocusIndicator(colour) {
1023
+ switch (colour.kind) {
1024
+ case "solid": return {
1025
+ kind: "two-tone",
1026
+ innerCss: colour.foregroundCss,
1027
+ outerCss: colour.backgroundCss,
1028
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
1029
+ };
1030
+ case "gradient": return {
1031
+ kind: "two-tone",
1032
+ innerCss: colour.foregroundCss,
1033
+ outerCss: tokenCss(colour.backgroundTokens[0]),
1034
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
1035
+ };
1036
+ case "surface": return {
1037
+ kind: "surface-role",
1038
+ colorCss: colour.foregroundCss,
1039
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
1040
+ };
1041
+ }
1042
+ }
1043
+ function resolveSolid(input, backgroundToken) {
1044
+ const backgroundHex = paletteHex(input, backgroundToken);
1045
+ if (!backgroundHex.ok) return backgroundHex;
1046
+ const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
1047
+ const foregroundHex = input.palette[foregroundToken];
1048
+ if (typeof foregroundHex !== "string") return {
1049
+ ok: false,
1050
+ error: {
1051
+ tag: "missing_on_colour",
1052
+ variantId: input.variantId,
1053
+ state: input.state,
1054
+ backgroundToken,
1055
+ foregroundToken
1056
+ }
1057
+ };
1058
+ const contrast = validateContrast({
1059
+ ...input,
1060
+ background: backgroundToken,
1061
+ backgroundHex: backgroundHex.hex,
1062
+ foreground: foregroundToken,
1063
+ foregroundHex
1064
+ });
1065
+ if (!contrast.ok) return contrast;
1066
+ return {
1067
+ ok: true,
1068
+ colour: {
1069
+ kind: "solid",
1070
+ backgroundToken,
1071
+ foregroundToken,
1072
+ backgroundCss: tokenCss(backgroundToken),
1073
+ foregroundCss: tokenCss(foregroundToken)
1074
+ }
1075
+ };
1076
+ }
1077
+ function resolveGradient(input, treatment) {
1078
+ const foregroundHex = paletteHex(input, treatment.foregroundToken);
1079
+ if (!foregroundHex.ok) return foregroundHex;
1080
+ const stopHexes = [];
1081
+ for (const token of treatment.stops) {
1082
+ const stop = paletteHex(input, token);
1083
+ if (!stop.ok) return stop;
1084
+ stopHexes.push(stop.hex);
1085
+ }
1086
+ for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
1087
+ const start = stopHexes[segment];
1088
+ const end = stopHexes[segment + 1];
1089
+ const startToken = treatment.stops[segment];
1090
+ const endToken = treatment.stops[segment + 1];
1091
+ if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
1092
+ for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
1093
+ const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
1094
+ const sampleHex = interpolateHex(start, end, position);
1095
+ const contrast = validateContrast({
1096
+ ...input,
1097
+ background: `${startToken}→${endToken}@${position.toFixed(2)}`,
1098
+ backgroundHex: sampleHex,
1099
+ foreground: treatment.foregroundToken,
1100
+ foregroundHex: foregroundHex.hex
1101
+ });
1102
+ if (!contrast.ok) return contrast;
1103
+ }
1104
+ }
1105
+ const direction = GRADIENT_DIRECTION_MAP$1[treatment.direction] ?? "to right";
1106
+ return {
1107
+ ok: true,
1108
+ colour: {
1109
+ kind: "gradient",
1110
+ backgroundTokens: treatment.stops,
1111
+ foregroundToken: treatment.foregroundToken,
1112
+ backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
1113
+ foregroundCss: tokenCss(treatment.foregroundToken)
1114
+ }
1115
+ };
1116
+ }
1117
+ function paletteHex(input, token) {
1118
+ const hex = input.palette[token];
1119
+ return typeof hex === "string" ? {
1120
+ ok: true,
1121
+ hex
1122
+ } : {
1123
+ ok: false,
1124
+ error: {
1125
+ tag: "unknown_palette_token",
1126
+ variantId: input.variantId,
1127
+ state: input.state,
1128
+ token
1129
+ }
1130
+ };
1131
+ }
1132
+ function validateContrast(input) {
1133
+ const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
1134
+ return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
1135
+ ok: false,
1136
+ error: {
1137
+ tag: "insufficient_contrast",
1138
+ variantId: input.variantId,
1139
+ state: input.state,
1140
+ background: input.background,
1141
+ foreground: input.foreground,
1142
+ ratio,
1143
+ requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
1144
+ }
1145
+ };
1146
+ }
1147
+ function tokenCss(token) {
1148
+ return `rgb(var(--tb-${token}))`;
1149
+ }
1150
+ function adjustShade(token, amount, palette) {
1151
+ const match = token.match(/^(.*)-(\d{2,3})$/);
1152
+ const base = match?.[1] ?? token;
1153
+ const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
1154
+ const availableShades = [
1155
+ 50,
1156
+ 100,
1157
+ 200,
1158
+ 300,
1159
+ 400,
1160
+ 500,
1161
+ 600,
1162
+ 700,
1163
+ 800,
1164
+ 900,
1165
+ 950
1166
+ ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
1167
+ if (availableShades.length === 0) return token;
1168
+ const firstShade = availableShades[0];
1169
+ if (firstShade === void 0) return token;
1170
+ const exactIndex = availableShades.indexOf(currentShade);
1171
+ const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
1172
+ const stepCount = Math.abs(amount) / 100;
1173
+ const direction = amount < 0 ? -1 : 1;
1174
+ const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
1175
+ return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
1176
+ }
1177
+ function readShadeAmount(effect) {
1178
+ const value = effect.options?.amount ?? 100;
1179
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
1180
+ }
1181
+ function readOpaqueToken(effect, key, fallback) {
1182
+ return parseOpaqueButtonColourToken(effect.options?.[key] ?? fallback);
1183
+ }
1184
+ function unsupportedEffect(input, effect, reason) {
1185
+ return {
1186
+ ok: false,
1187
+ error: {
1188
+ tag: "unsupported_colour_effect",
1189
+ variantId: input.variantId,
1190
+ state: input.state,
1191
+ effectId: effect.effectId,
1192
+ reason
1193
+ }
1194
+ };
1195
+ }
1196
+ function invalidEffectOption(input, effect, option) {
1197
+ return {
1198
+ ok: false,
1199
+ error: {
1200
+ tag: "invalid_colour_effect_options",
1201
+ variantId: input.variantId,
1202
+ state: input.state,
1203
+ effectId: effect.effectId,
1204
+ option
1205
+ }
1206
+ };
1207
+ }
1208
+ function inapplicableEffect(input, effect, treatment) {
1209
+ return {
1210
+ ok: false,
1211
+ error: {
1212
+ tag: "inapplicable_colour_effect",
1213
+ variantId: input.variantId,
1214
+ state: input.state,
1215
+ effectId: effect.effectId,
1216
+ treatment: treatment.type
1217
+ }
1218
+ };
1219
+ }
1220
+ function unknownEffect(input, effect) {
1221
+ return {
1222
+ ok: false,
1223
+ error: {
1224
+ tag: "unknown_button_effect",
1225
+ variantId: input.variantId,
1226
+ state: input.state,
1227
+ effectId: effect.effectId
1228
+ }
1229
+ };
1230
+ }
1231
+ function interpolateHex(startHex, endHex, position) {
1232
+ const start = hexToRgb(startHex);
1233
+ const end = hexToRgb(endHex);
1234
+ const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
1235
+ return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
1236
+ }
1237
+ //#endregion
1238
+ //#region ../theme-core/src/buttons/resolvedButtonCss.ts
1239
+ /**
1240
+ * Canonical colour declaration mapping shared by full themes and ThemeBridge.
1241
+ * Both background properties are always emitted so transitions cannot retain
1242
+ * a stale gradient or solid layer.
1243
+ */
1244
+ function resolvedButtonColourDeclarations(colour) {
1245
+ switch (colour.kind) {
1246
+ case "solid": return [
1247
+ ["background-color", colour.backgroundCss],
1248
+ ["background-image", "none"],
1249
+ ["color", colour.foregroundCss]
1250
+ ];
1251
+ case "gradient": return [
1252
+ ["background-color", "transparent"],
1253
+ ["background-image", colour.backgroundCss],
1254
+ ["color", colour.foregroundCss]
1255
+ ];
1256
+ case "surface": return [
1257
+ ["background-color", colour.backgroundCss],
1258
+ ["background-image", "none"],
1259
+ ["color", colour.foregroundCss]
1260
+ ];
1261
+ }
1262
+ }
1263
+ function resolvedButtonFocusDeclarations(indicator) {
1264
+ return indicator.kind === "two-tone" ? [
1265
+ ["outline", `2px solid ${indicator.innerCss}`],
1266
+ ["outline-offset", "2px"],
1267
+ ["box-shadow", `0 0 0 4px ${indicator.outerCss}`]
1268
+ ] : [["outline", `3px solid ${indicator.colorCss}`], ["outline-offset", "3px"]];
1269
+ }
1270
+ /**
1271
+ * Disabled buttons retain their validated colour pair and add a non-layout
1272
+ * visual affordance. `currentColor` keeps the cue inside the same contrast
1273
+ * relationship, while retaining pointer events lets the not-allowed cursor
1274
+ * remain observable.
1275
+ */
1276
+ const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
1277
+ ["cursor", "not-allowed"],
1278
+ ["transform", "none"],
1279
+ ["box-shadow", "none"],
1280
+ ["outline", "2px dashed currentColor"],
1281
+ ["outline-offset", "-4px"]
1282
+ ];
442
1283
  buttonSizeNameSchema.options;
443
1284
  //#endregion
444
1285
  //#region ../theme-core/src/site-styles/styleTags.ts
@@ -498,7 +1339,7 @@ function defineButtonPersonality(input) {
498
1339
  name: input.name,
499
1340
  description: input.description,
500
1341
  tags: input.tags,
501
- buttonSystem: input.buttonSystem
1342
+ buttonSystem: buttonSystemSchema.parse(input.buttonSystem)
502
1343
  };
503
1344
  }
504
1345
  //#endregion
@@ -551,19 +1392,15 @@ const brushedWash = defineButtonPersonality({
551
1392
  background: {
552
1393
  type: "gradient",
553
1394
  stops: ["primary", "accent"],
554
- direction: "to-r"
1395
+ direction: "to-r",
1396
+ foregroundToken: "primaryForeground"
555
1397
  },
556
- textColorToken: "primaryForeground",
557
1398
  borderRadius: "rounded-lg",
558
1399
  shadow: {
559
1400
  elevation: "low",
560
1401
  softness: "soft",
561
1402
  position: "bottom"
562
- },
563
- effects: { hover: [{
564
- effectId: "opacity-reduce",
565
- options: { reduction: -15 }
566
- }] }
1403
+ }
567
1404
  },
568
1405
  {
569
1406
  id: "secondary",
@@ -573,32 +1410,27 @@ const brushedWash = defineButtonPersonality({
573
1410
  background: {
574
1411
  type: "gradient",
575
1412
  stops: ["secondary", "accent"],
576
- direction: "to-br"
1413
+ direction: "to-br",
1414
+ foregroundToken: "primaryForeground"
577
1415
  },
578
- textColorToken: "primaryForeground",
579
1416
  borderRadius: "rounded-lg",
580
1417
  shadow: {
581
1418
  elevation: "low",
582
1419
  softness: "soft",
583
1420
  position: "bottom"
584
- },
585
- effects: { hover: [{
586
- effectId: "opacity-reduce",
587
- options: { reduction: -15 }
588
- }] }
1421
+ }
589
1422
  },
590
1423
  {
591
1424
  id: "outline",
592
1425
  name: "Outline",
593
1426
  enabled: true,
594
1427
  priority: 3,
595
- background: { type: "transparent" },
596
- textColorToken: "primary",
597
- borderRadius: "rounded-lg",
598
- border: {
599
- widthClass: "border",
600
- colorToken: "primary"
1428
+ background: {
1429
+ type: "surface",
1430
+ role: "link"
601
1431
  },
1432
+ borderRadius: "rounded-lg",
1433
+ border: { widthClass: "border" },
602
1434
  effects: { hover: [{
603
1435
  effectId: "background-on-hover",
604
1436
  options: { colorToken: "surface" }
@@ -609,8 +1441,10 @@ const brushedWash = defineButtonPersonality({
609
1441
  name: "Ghost",
610
1442
  enabled: true,
611
1443
  priority: 3,
612
- background: { type: "transparent" },
613
- textColorToken: "text",
1444
+ background: {
1445
+ type: "surface",
1446
+ role: "link"
1447
+ },
614
1448
  borderRadius: "rounded-lg",
615
1449
  effects: { hover: [{
616
1450
  effectId: "background-on-hover",
@@ -656,7 +1490,6 @@ const confidentChip = defineButtonPersonality({
656
1490
  type: "solid",
657
1491
  colorToken: "primary"
658
1492
  },
659
- textColorToken: "primaryForeground",
660
1493
  borderRadius: "rounded-md",
661
1494
  shadow: {
662
1495
  elevation: "medium",
@@ -666,7 +1499,7 @@ const confidentChip = defineButtonPersonality({
666
1499
  effects: {
667
1500
  hover: [{
668
1501
  effectId: "darken-background",
669
- options: { amount: 60 }
1502
+ options: { amount: 100 }
670
1503
  }],
671
1504
  active: [{
672
1505
  effectId: "scale-down",
@@ -683,12 +1516,8 @@ const confidentChip = defineButtonPersonality({
683
1516
  type: "solid",
684
1517
  colorToken: "surface"
685
1518
  },
686
- textColorToken: "text",
687
1519
  borderRadius: "rounded-md",
688
- border: {
689
- widthClass: "border",
690
- colorToken: "border"
691
- },
1520
+ border: { widthClass: "border" },
692
1521
  effects: { hover: [{
693
1522
  effectId: "background-on-hover",
694
1523
  options: { colorToken: "background" }
@@ -699,13 +1528,12 @@ const confidentChip = defineButtonPersonality({
699
1528
  name: "Outline",
700
1529
  enabled: true,
701
1530
  priority: 3,
702
- background: { type: "transparent" },
703
- textColorToken: "primary",
704
- borderRadius: "rounded-md",
705
- border: {
706
- widthClass: "border",
707
- colorToken: "primary"
1531
+ background: {
1532
+ type: "surface",
1533
+ role: "link"
708
1534
  },
1535
+ borderRadius: "rounded-md",
1536
+ border: { widthClass: "border" },
709
1537
  effects: { hover: [{
710
1538
  effectId: "background-on-hover",
711
1539
  options: { colorToken: "surface" }
@@ -716,8 +1544,10 @@ const confidentChip = defineButtonPersonality({
716
1544
  name: "Ghost",
717
1545
  enabled: true,
718
1546
  priority: 3,
719
- background: { type: "transparent" },
720
- textColorToken: "primary",
1547
+ background: {
1548
+ type: "surface",
1549
+ role: "link"
1550
+ },
721
1551
  borderRadius: "rounded-md",
722
1552
  effects: { hover: [{
723
1553
  effectId: "background-on-hover",
@@ -780,7 +1610,6 @@ const earthPill = defineButtonPersonality({
780
1610
  type: "solid",
781
1611
  colorToken: "primary"
782
1612
  },
783
- textColorToken: "primaryForeground",
784
1613
  borderRadius: "rounded-full",
785
1614
  shadow: {
786
1615
  elevation: "low",
@@ -806,13 +1635,12 @@ const earthPill = defineButtonPersonality({
806
1635
  name: "Secondary",
807
1636
  enabled: true,
808
1637
  priority: 2,
809
- background: { type: "transparent" },
810
- textColorToken: "primary",
811
- borderRadius: "rounded-full",
812
- border: {
813
- widthClass: "border",
814
- colorToken: "primary"
1638
+ background: {
1639
+ type: "surface",
1640
+ role: "link"
815
1641
  },
1642
+ borderRadius: "rounded-full",
1643
+ border: { widthClass: "border" },
816
1644
  effects: { hover: [{
817
1645
  effectId: "background-on-hover",
818
1646
  options: { colorToken: "muted" }
@@ -823,13 +1651,12 @@ const earthPill = defineButtonPersonality({
823
1651
  name: "Outline",
824
1652
  enabled: true,
825
1653
  priority: 3,
826
- background: { type: "transparent" },
827
- textColorToken: "text",
828
- borderRadius: "rounded-full",
829
- border: {
830
- widthClass: "border",
831
- colorToken: "border"
1654
+ background: {
1655
+ type: "surface",
1656
+ role: "link"
832
1657
  },
1658
+ borderRadius: "rounded-full",
1659
+ border: { widthClass: "border" },
833
1660
  effects: { hover: [{
834
1661
  effectId: "background-on-hover",
835
1662
  options: { colorToken: "surfaceAlt" }
@@ -840,8 +1667,10 @@ const earthPill = defineButtonPersonality({
840
1667
  name: "Ghost",
841
1668
  enabled: true,
842
1669
  priority: 3,
843
- background: { type: "transparent" },
844
- textColorToken: "primary",
1670
+ background: {
1671
+ type: "surface",
1672
+ role: "link"
1673
+ },
845
1674
  borderRadius: "rounded-full",
846
1675
  shadow: {
847
1676
  elevation: "none",
@@ -862,11 +1691,7 @@ const earthPill = defineButtonPersonality({
862
1691
  padding: "0.625rem 0",
863
1692
  fontSize: "text-xl"
864
1693
  }
865
- },
866
- effects: { hover: [{
867
- effectId: "opacity-reduce",
868
- options: { reduction: 25 }
869
- }] }
1694
+ }
870
1695
  }
871
1696
  ]
872
1697
  }
@@ -902,8 +1727,10 @@ const editorialLink = defineButtonPersonality({
902
1727
  name: "Primary",
903
1728
  enabled: true,
904
1729
  priority: 1,
905
- background: { type: "transparent" },
906
- textColorToken: "text",
1730
+ background: {
1731
+ type: "surface",
1732
+ role: "link"
1733
+ },
907
1734
  borderRadius: "rounded-none",
908
1735
  sizes: {
909
1736
  sm: { padding: "0.25rem 0" },
@@ -925,8 +1752,10 @@ const editorialLink = defineButtonPersonality({
925
1752
  name: "Secondary",
926
1753
  enabled: true,
927
1754
  priority: 2,
928
- background: { type: "transparent" },
929
- textColorToken: "mutedText",
1755
+ background: {
1756
+ type: "surface",
1757
+ role: "link"
1758
+ },
930
1759
  borderRadius: "rounded-none",
931
1760
  sizes: {
932
1761
  sm: { padding: "0.25rem 0" },
@@ -948,8 +1777,10 @@ const editorialLink = defineButtonPersonality({
948
1777
  name: "Outline",
949
1778
  enabled: true,
950
1779
  priority: 3,
951
- background: { type: "transparent" },
952
- textColorToken: "accent",
1780
+ background: {
1781
+ type: "surface",
1782
+ role: "link"
1783
+ },
953
1784
  borderRadius: "rounded-none",
954
1785
  sizes: {
955
1786
  sm: { padding: "0.25rem 0" },
@@ -971,19 +1802,17 @@ const editorialLink = defineButtonPersonality({
971
1802
  name: "Ghost",
972
1803
  enabled: true,
973
1804
  priority: 3,
974
- background: { type: "transparent" },
975
- textColorToken: "mutedText",
1805
+ background: {
1806
+ type: "surface",
1807
+ role: "link"
1808
+ },
976
1809
  borderRadius: "rounded-none",
977
1810
  sizes: {
978
1811
  sm: { padding: "0.25rem 0" },
979
1812
  md: { padding: "0.5rem 0" },
980
1813
  lg: { padding: "0.625rem 0" },
981
1814
  xl: { padding: "0.75rem 0" }
982
- },
983
- effects: { hover: [{
984
- effectId: "opacity-reduce",
985
- options: { reduction: -20 }
986
- }] }
1815
+ }
987
1816
  }
988
1817
  ]
989
1818
  }
@@ -1045,12 +1874,8 @@ const flatInk = defineButtonPersonality({
1045
1874
  type: "solid",
1046
1875
  colorToken: "primary"
1047
1876
  },
1048
- textColorToken: "primaryForeground",
1049
1877
  borderRadius: "rounded-lg",
1050
- affordance: {
1051
- iconId: "ArrowRight",
1052
- colorToken: "accent"
1053
- },
1878
+ affordance: { iconId: "ArrowRight" },
1054
1879
  effects: { hover: [{
1055
1880
  effectId: "lift",
1056
1881
  options: { translateY: "-2px" }
@@ -1064,13 +1889,12 @@ const flatInk = defineButtonPersonality({
1064
1889
  name: "Secondary",
1065
1890
  enabled: true,
1066
1891
  priority: 2,
1067
- background: { type: "transparent" },
1068
- textColorToken: "primary",
1069
- borderRadius: "rounded-lg",
1070
- border: {
1071
- widthClass: "border",
1072
- colorToken: "border"
1892
+ background: {
1893
+ type: "surface",
1894
+ role: "link"
1073
1895
  },
1896
+ borderRadius: "rounded-lg",
1897
+ border: { widthClass: "border" },
1074
1898
  effects: { hover: [{
1075
1899
  effectId: "lift",
1076
1900
  options: { translateY: "-2px" }
@@ -1088,12 +1912,8 @@ const flatInk = defineButtonPersonality({
1088
1912
  type: "solid",
1089
1913
  colorToken: "accent"
1090
1914
  },
1091
- textColorToken: "accentForeground",
1092
1915
  borderRadius: "rounded-lg",
1093
- affordance: {
1094
- iconId: "ArrowRight",
1095
- colorToken: "accentForeground"
1096
- },
1916
+ affordance: { iconId: "ArrowRight" },
1097
1917
  effects: { hover: [{
1098
1918
  effectId: "lift",
1099
1919
  options: { translateY: "-2px" }
@@ -1156,7 +1976,6 @@ const inkStamp = defineButtonPersonality({
1156
1976
  type: "solid",
1157
1977
  colorToken: "primary"
1158
1978
  },
1159
- textColorToken: "primaryForeground",
1160
1979
  borderRadius: "rounded-md",
1161
1980
  effects: {
1162
1981
  hover: [{
@@ -1177,13 +1996,12 @@ const inkStamp = defineButtonPersonality({
1177
1996
  name: "Secondary",
1178
1997
  enabled: true,
1179
1998
  priority: 2,
1180
- background: { type: "transparent" },
1181
- textColorToken: "primary",
1182
- borderRadius: "rounded-md",
1183
- border: {
1184
- widthClass: "border-2",
1185
- colorToken: "primary"
1999
+ background: {
2000
+ type: "surface",
2001
+ role: "link"
1186
2002
  },
2003
+ borderRadius: "rounded-md",
2004
+ border: { widthClass: "border-2" },
1187
2005
  effects: { hover: [{
1188
2006
  effectId: "background-on-hover",
1189
2007
  options: { colorToken: "surface" }
@@ -1194,13 +2012,12 @@ const inkStamp = defineButtonPersonality({
1194
2012
  name: "Outline",
1195
2013
  enabled: true,
1196
2014
  priority: 3,
1197
- background: { type: "transparent" },
1198
- textColorToken: "text",
1199
- borderRadius: "rounded-md",
1200
- border: {
1201
- widthClass: "border-2",
1202
- colorToken: "border"
2015
+ background: {
2016
+ type: "surface",
2017
+ role: "link"
1203
2018
  },
2019
+ borderRadius: "rounded-md",
2020
+ border: { widthClass: "border-2" },
1204
2021
  effects: { hover: [{
1205
2022
  effectId: "background-on-hover",
1206
2023
  options: { colorToken: "surface" }
@@ -1211,8 +2028,10 @@ const inkStamp = defineButtonPersonality({
1211
2028
  name: "Ghost",
1212
2029
  enabled: true,
1213
2030
  priority: 3,
1214
- background: { type: "transparent" },
1215
- textColorToken: "accent",
2031
+ background: {
2032
+ type: "surface",
2033
+ role: "link"
2034
+ },
1216
2035
  borderRadius: "rounded-none",
1217
2036
  sizes: {
1218
2037
  sm: {
@@ -1305,7 +2124,6 @@ const joyStamp = defineButtonPersonality({
1305
2124
  type: "solid",
1306
2125
  colorToken: "primary"
1307
2126
  },
1308
- textColorToken: "primaryForeground",
1309
2127
  borderRadius: "rounded-full",
1310
2128
  shadow: {
1311
2129
  elevation: "high",
@@ -1319,13 +2137,12 @@ const joyStamp = defineButtonPersonality({
1319
2137
  name: "Secondary",
1320
2138
  enabled: true,
1321
2139
  priority: 2,
1322
- background: { type: "transparent" },
1323
- textColorToken: "text",
1324
- borderRadius: "rounded-full",
1325
- border: {
1326
- widthClass: "border-2",
1327
- colorToken: "text"
2140
+ background: {
2141
+ type: "surface",
2142
+ role: "link"
1328
2143
  },
2144
+ borderRadius: "rounded-full",
2145
+ border: { widthClass: "border-2" },
1329
2146
  shadow: {
1330
2147
  elevation: "high",
1331
2148
  softness: "hard",
@@ -1390,12 +2207,8 @@ const ornateRitual = defineButtonPersonality({
1390
2207
  type: "solid",
1391
2208
  colorToken: "primary"
1392
2209
  },
1393
- textColorToken: "primaryForeground",
1394
2210
  borderRadius: "rounded-md",
1395
- border: {
1396
- widthClass: "border",
1397
- colorToken: "primary"
1398
- },
2211
+ border: { widthClass: "border" },
1399
2212
  effects: {
1400
2213
  hover: [{
1401
2214
  effectId: "background-on-hover",
@@ -1416,12 +2229,8 @@ const ornateRitual = defineButtonPersonality({
1416
2229
  type: "solid",
1417
2230
  colorToken: "secondary"
1418
2231
  },
1419
- textColorToken: "primaryForeground",
1420
2232
  borderRadius: "rounded-md",
1421
- border: {
1422
- widthClass: "border",
1423
- colorToken: "primary"
1424
- },
2233
+ border: { widthClass: "border" },
1425
2234
  effects: { hover: [{
1426
2235
  effectId: "background-on-hover",
1427
2236
  options: { colorToken: "surface" }
@@ -1432,13 +2241,12 @@ const ornateRitual = defineButtonPersonality({
1432
2241
  name: "Outline",
1433
2242
  enabled: true,
1434
2243
  priority: 3,
1435
- background: { type: "transparent" },
1436
- textColorToken: "primary",
1437
- borderRadius: "rounded-md",
1438
- border: {
1439
- widthClass: "border",
1440
- colorToken: "primary"
2244
+ background: {
2245
+ type: "surface",
2246
+ role: "link"
1441
2247
  },
2248
+ borderRadius: "rounded-md",
2249
+ border: { widthClass: "border" },
1442
2250
  effects: { hover: [{
1443
2251
  effectId: "background-on-hover",
1444
2252
  options: { colorToken: "muted" }
@@ -1449,8 +2257,10 @@ const ornateRitual = defineButtonPersonality({
1449
2257
  name: "Ghost",
1450
2258
  enabled: true,
1451
2259
  priority: 3,
1452
- background: { type: "transparent" },
1453
- textColorToken: "accent",
2260
+ background: {
2261
+ type: "surface",
2262
+ role: "link"
2263
+ },
1454
2264
  borderRadius: "rounded-none",
1455
2265
  sizes: {
1456
2266
  sm: {
@@ -1532,14 +2342,10 @@ const pebble = defineButtonPersonality({
1532
2342
  type: "solid",
1533
2343
  colorToken: "primary"
1534
2344
  },
1535
- textColorToken: "primaryForeground",
1536
2345
  borderRadius: "rounded-full",
1537
2346
  effects: { hover: [{
1538
- effectId: "background-on-hover-alpha",
1539
- options: {
1540
- colorToken: "primary",
1541
- opacity: 88
1542
- }
2347
+ effectId: "darken-background",
2348
+ options: { amount: 100 }
1543
2349
  }] }
1544
2350
  },
1545
2351
  {
@@ -1547,13 +2353,12 @@ const pebble = defineButtonPersonality({
1547
2353
  name: "Secondary",
1548
2354
  enabled: true,
1549
2355
  priority: 2,
1550
- background: { type: "transparent" },
1551
- textColorToken: "primary",
1552
- borderRadius: "rounded-full",
1553
- border: {
1554
- widthClass: "border-2",
1555
- colorToken: "accent"
2356
+ background: {
2357
+ type: "surface",
2358
+ role: "link"
1556
2359
  },
2360
+ borderRadius: "rounded-full",
2361
+ border: { widthClass: "border-2" },
1557
2362
  effects: { hover: [{
1558
2363
  effectId: "background-on-hover",
1559
2364
  options: { colorToken: "accent" }
@@ -1564,13 +2369,12 @@ const pebble = defineButtonPersonality({
1564
2369
  name: "Outline",
1565
2370
  enabled: true,
1566
2371
  priority: 3,
1567
- background: { type: "transparent" },
1568
- textColorToken: "text",
1569
- borderRadius: "rounded-full",
1570
- border: {
1571
- widthClass: "border",
1572
- colorToken: "border"
2372
+ background: {
2373
+ type: "surface",
2374
+ role: "link"
1573
2375
  },
2376
+ borderRadius: "rounded-full",
2377
+ border: { widthClass: "border" },
1574
2378
  effects: { hover: [{
1575
2379
  effectId: "background-on-hover",
1576
2380
  options: { colorToken: "surface" }
@@ -1581,8 +2385,10 @@ const pebble = defineButtonPersonality({
1581
2385
  name: "Ghost",
1582
2386
  enabled: true,
1583
2387
  priority: 3,
1584
- background: { type: "transparent" },
1585
- textColorToken: "primary",
2388
+ background: {
2389
+ type: "surface",
2390
+ role: "link"
2391
+ },
1586
2392
  borderRadius: "rounded-full",
1587
2393
  sizes: {
1588
2394
  sm: {
@@ -1598,11 +2404,7 @@ const pebble = defineButtonPersonality({
1598
2404
  padding: "0.75rem 1.5rem",
1599
2405
  fontSize: "text-xl"
1600
2406
  }
1601
- },
1602
- effects: { hover: [{
1603
- effectId: "opacity-reduce",
1604
- options: { reduction: 30 }
1605
- }] }
2407
+ }
1606
2408
  }
1607
2409
  ]
1608
2410
  }
@@ -1664,15 +2466,11 @@ const quietArrow = defineButtonPersonality({
1664
2466
  type: "solid",
1665
2467
  colorToken: "primary"
1666
2468
  },
1667
- textColorToken: "primaryForeground",
1668
2469
  borderRadius: "rounded-xl",
1669
2470
  affordance: { iconId: "ArrowRight" },
1670
2471
  effects: { hover: [{
1671
- effectId: "background-on-hover-alpha",
1672
- options: {
1673
- colorToken: "primary",
1674
- opacity: 90
1675
- }
2472
+ effectId: "darken-background",
2473
+ options: { amount: 100 }
1676
2474
  }] }
1677
2475
  },
1678
2476
  {
@@ -1680,13 +2478,12 @@ const quietArrow = defineButtonPersonality({
1680
2478
  name: "Secondary",
1681
2479
  enabled: true,
1682
2480
  priority: 2,
1683
- background: { type: "transparent" },
1684
- textColorToken: "primary",
1685
- borderRadius: "rounded-xl",
1686
- border: {
1687
- widthClass: "border",
1688
- colorToken: "primary"
2481
+ background: {
2482
+ type: "surface",
2483
+ role: "link"
1689
2484
  },
2485
+ borderRadius: "rounded-xl",
2486
+ border: { widthClass: "border" },
1690
2487
  affordance: { iconId: "ArrowRight" },
1691
2488
  effects: { hover: [{
1692
2489
  effectId: "background-on-hover",
@@ -1698,21 +2495,22 @@ const quietArrow = defineButtonPersonality({
1698
2495
  name: "Outline",
1699
2496
  enabled: true,
1700
2497
  priority: 3,
1701
- background: { type: "transparent" },
1702
- textColorToken: "primary",
2498
+ background: {
2499
+ type: "surface",
2500
+ role: "link"
2501
+ },
1703
2502
  borderRadius: "rounded-xl",
1704
- border: {
1705
- widthClass: "border",
1706
- colorToken: "primary"
1707
- }
2503
+ border: { widthClass: "border" }
1708
2504
  },
1709
2505
  {
1710
2506
  id: "ghost",
1711
2507
  name: "Ghost",
1712
2508
  enabled: true,
1713
2509
  priority: 3,
1714
- background: { type: "transparent" },
1715
- textColorToken: "primary",
2510
+ background: {
2511
+ type: "surface",
2512
+ role: "link"
2513
+ },
1716
2514
  borderRadius: "rounded-xl"
1717
2515
  }
1718
2516
  ]
@@ -1776,7 +2574,6 @@ const showtimePill = defineButtonPersonality({
1776
2574
  type: "solid",
1777
2575
  colorToken: "primary"
1778
2576
  },
1779
- textColorToken: "background",
1780
2577
  borderRadius: "rounded-full",
1781
2578
  effects: { hover: [{ effectId: "pop" }] }
1782
2579
  },
@@ -1785,13 +2582,12 @@ const showtimePill = defineButtonPersonality({
1785
2582
  name: "Secondary",
1786
2583
  enabled: true,
1787
2584
  priority: 2,
1788
- background: { type: "transparent" },
1789
- textColorToken: "primary",
1790
- borderRadius: "rounded-full",
1791
- border: {
1792
- widthClass: "border-2",
1793
- colorToken: "primary"
2585
+ background: {
2586
+ type: "surface",
2587
+ role: "link"
1794
2588
  },
2589
+ borderRadius: "rounded-full",
2590
+ border: { widthClass: "border-2" },
1795
2591
  effects: { hover: [{ effectId: "pop" }] }
1796
2592
  },
1797
2593
  {
@@ -1803,7 +2599,6 @@ const showtimePill = defineButtonPersonality({
1803
2599
  type: "solid",
1804
2600
  colorToken: "secondary"
1805
2601
  },
1806
- textColorToken: "primary",
1807
2602
  borderRadius: "rounded-full",
1808
2603
  effects: { hover: [{ effectId: "pop" }] }
1809
2604
  },
@@ -1816,12 +2611,8 @@ const showtimePill = defineButtonPersonality({
1816
2611
  type: "solid",
1817
2612
  colorToken: "background"
1818
2613
  },
1819
- textColorToken: "primary",
1820
2614
  borderRadius: "rounded-full",
1821
- border: {
1822
- widthClass: "border-2",
1823
- colorToken: "primary"
1824
- },
2615
+ border: { widthClass: "border-2" },
1825
2616
  effects: { hover: [{
1826
2617
  effectId: "background-on-hover",
1827
2618
  options: { colorToken: "surface" }
@@ -1832,13 +2623,12 @@ const showtimePill = defineButtonPersonality({
1832
2623
  name: "Outline",
1833
2624
  enabled: true,
1834
2625
  priority: 3,
1835
- background: { type: "transparent" },
1836
- textColorToken: "primary",
1837
- borderRadius: "rounded-full",
1838
- border: {
1839
- widthClass: "border-2",
1840
- colorToken: "primary"
2626
+ background: {
2627
+ type: "surface",
2628
+ role: "link"
1841
2629
  },
2630
+ borderRadius: "rounded-full",
2631
+ border: { widthClass: "border-2" },
1842
2632
  effects: { hover: [{
1843
2633
  effectId: "background-on-hover",
1844
2634
  options: { colorToken: "background" }
@@ -1849,8 +2639,10 @@ const showtimePill = defineButtonPersonality({
1849
2639
  name: "Ghost",
1850
2640
  enabled: true,
1851
2641
  priority: 3,
1852
- background: { type: "transparent" },
1853
- textColorToken: "primary",
2642
+ background: {
2643
+ type: "surface",
2644
+ role: "link"
2645
+ },
1854
2646
  borderRadius: "rounded-full",
1855
2647
  effects: { hover: [{
1856
2648
  effectId: "background-on-hover",
@@ -1896,7 +2688,6 @@ const softPill = defineButtonPersonality({
1896
2688
  type: "solid",
1897
2689
  colorToken: "primary"
1898
2690
  },
1899
- textColorToken: "primaryForeground",
1900
2691
  borderRadius: "rounded-full",
1901
2692
  shadow: {
1902
2693
  elevation: "low",
@@ -1919,13 +2710,12 @@ const softPill = defineButtonPersonality({
1919
2710
  name: "Secondary",
1920
2711
  enabled: true,
1921
2712
  priority: 2,
1922
- background: { type: "transparent" },
1923
- textColorToken: "primary",
1924
- borderRadius: "rounded-full",
1925
- border: {
1926
- widthClass: "border-2",
1927
- colorToken: "primary"
2713
+ background: {
2714
+ type: "surface",
2715
+ role: "link"
1928
2716
  },
2717
+ borderRadius: "rounded-full",
2718
+ border: { widthClass: "border-2" },
1929
2719
  effects: { hover: [{
1930
2720
  effectId: "background-on-hover",
1931
2721
  options: { colorToken: "surface" }
@@ -1936,13 +2726,12 @@ const softPill = defineButtonPersonality({
1936
2726
  name: "Outline",
1937
2727
  enabled: true,
1938
2728
  priority: 3,
1939
- background: { type: "transparent" },
1940
- textColorToken: "text",
1941
- borderRadius: "rounded-full",
1942
- border: {
1943
- widthClass: "border",
1944
- colorToken: "border"
2729
+ background: {
2730
+ type: "surface",
2731
+ role: "link"
1945
2732
  },
2733
+ borderRadius: "rounded-full",
2734
+ border: { widthClass: "border" },
1946
2735
  effects: { hover: [{
1947
2736
  effectId: "background-on-hover",
1948
2737
  options: { colorToken: "surface" }
@@ -1953,8 +2742,10 @@ const softPill = defineButtonPersonality({
1953
2742
  name: "Ghost",
1954
2743
  enabled: true,
1955
2744
  priority: 3,
1956
- background: { type: "transparent" },
1957
- textColorToken: "text",
2745
+ background: {
2746
+ type: "surface",
2747
+ role: "link"
2748
+ },
1958
2749
  borderRadius: "rounded-full",
1959
2750
  effects: { hover: [{
1960
2751
  effectId: "background-on-hover",
@@ -1973,9 +2764,8 @@ const solidSecondary = {
1973
2764
  priority: 2,
1974
2765
  background: {
1975
2766
  type: "solid",
1976
- colorToken: "secondary"
2767
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
1977
2768
  },
1978
- textColorToken: "onSecondary",
1979
2769
  borderRadius: "rounded-full",
1980
2770
  shadow: {
1981
2771
  elevation: "none",
@@ -2023,6 +2813,27 @@ const buttonPersonalities = [
2023
2813
  quietArrow
2024
2814
  ];
2025
2815
  new Map(buttonPersonalities.map((p) => [p.id, p]));
2816
+ buttonPersonalities.map((personality) => ({
2817
+ id: personality.id,
2818
+ name: personality.name,
2819
+ description: personality.description,
2820
+ category: presetCategory(personality),
2821
+ buttonSystem: personality.buttonSystem,
2822
+ compatibility: {
2823
+ ...personality.tags.tone ? { tones: [personality.tags.tone] } : {},
2824
+ ...personality.tags.energy ? { energies: [personality.tags.energy] } : {}
2825
+ }
2826
+ }));
2827
+ function presetCategory(personality) {
2828
+ switch (personality.tags.era) {
2829
+ case "retro": return "retro";
2830
+ case "editorial": return "minimal";
2831
+ case "organic": return "artistic";
2832
+ case "modern": return personality.tags.tone === "bold" ? "modern" : "technical";
2833
+ case "technical": return "technical";
2834
+ case void 0: return "modern";
2835
+ }
2836
+ }
2026
2837
  //#endregion
2027
2838
  //#region ../theme-core/src/buttons/core.ts
2028
2839
  const CORNER_RADIUS_MAP$2 = {
@@ -2037,125 +2848,101 @@ const BORDER_WIDTH_MAP$2 = {
2037
2848
  medium: "2px",
2038
2849
  thick: "4px"
2039
2850
  };
2040
- const GRADIENT_DIRECTION_MAP$1 = {
2041
- "to-r": "to right",
2042
- "to-l": "to left",
2043
- "to-t": "to top",
2044
- "to-b": "to bottom",
2045
- "to-tr": "to top right",
2046
- "to-tl": "to top left",
2047
- "to-br": "to bottom right",
2048
- "to-bl": "to bottom left"
2049
- };
2050
2851
  const SHADOW_VALUES$2 = {
2051
2852
  none: "none",
2052
2853
  low: "0 1px 2px rgba(0,0,0,0.06)",
2053
2854
  medium: "0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1)",
2054
2855
  high: "0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)"
2055
2856
  };
2056
- /**
2057
- * Generate button CSS from core variant definitions.
2058
- *
2059
- * This is a pure function with no dependencies on Theme type.
2060
- * It generates CSS that uses rgb(var(--tb-{token})) for colors.
2061
- *
2062
- * @param scopeId - Theme scope ID for selector (e.g., 'sdk' -> [data-theme-scope="sdk"])
2063
- * @param variants - Button variant definitions
2064
- * @param settings - Global button settings
2065
- * @returns CSS string
2066
- */
2067
- function generateButtonCoreCss(scopeId, variants, settings) {
2068
- const cssChunks = [];
2069
- for (const variant of variants) {
2070
- const css = generateVariantCss$1(scopeId, variant, settings);
2071
- cssChunks.push(css);
2072
- }
2073
- return cssChunks.join("\n\n");
2074
- }
2075
- /**
2076
- * Generate CSS for a single button variant
2077
- *
2078
- * Selectors use :where() for zero specificity, allowing site styles to override easily.
2079
- */
2080
- function generateVariantCss$1(scopeId, variant, settings) {
2857
+ const RESOLVER_GLOBAL = {
2858
+ cornerStyle: "rounded",
2859
+ textTransform: "none",
2860
+ fontWeight: 500,
2861
+ borderWidth: 1,
2862
+ hoverTransform: "none",
2863
+ hoverColor: "none"
2864
+ };
2865
+ function generateButtonCoreCss(scopeId, variants, settings, palette) {
2866
+ return variants.map((variant) => generateVariantCss$1(scopeId, variant, settings, palette)).join("\n\n");
2867
+ }
2868
+ function generateVariantCss$1(scopeId, variant, settings, palette) {
2869
+ const borderWidth = BORDER_WIDTH_MAP$2[variant.borderWidth ?? "none"];
2870
+ const result = compileButtonColourStates({
2871
+ variant: {
2872
+ id: variant.id,
2873
+ name: variant.id,
2874
+ enabled: true,
2875
+ priority: 1,
2876
+ background: variant.treatment,
2877
+ ...borderWidth === "0" ? {} : { border: { widthClass: borderWidth } }
2878
+ },
2879
+ global: RESOLVER_GLOBAL,
2880
+ palette
2881
+ });
2882
+ if (!result.ok) throw new Error(`[ThemeBridge] ${formatButtonColourContractError(result.error)}`);
2081
2883
  const selector = `:where([data-theme-scope="${scopeId}"]) .button-${variant.id}`;
2082
- const rules = [];
2083
- const borderWidthValue = variant.borderWidth ? BORDER_WIDTH_MAP$2[variant.borderWidth] : variant.borderToken ? BORDER_WIDTH_MAP$2[settings.borderWidth] : "0";
2084
- const hasBorder = borderWidthValue !== "0";
2085
- const basePadding = {
2086
- vertical: "0.5rem",
2087
- horizontal: "1rem"
2088
- };
2089
- if (hasBorder) {
2090
- rules.push(`padding-top: calc(${basePadding.vertical} - ${borderWidthValue});`);
2091
- rules.push(`padding-bottom: calc(${basePadding.vertical} - ${borderWidthValue});`);
2092
- rules.push(`padding-left: calc(${basePadding.horizontal} - ${borderWidthValue});`);
2093
- rules.push(`padding-right: calc(${basePadding.horizontal} - ${borderWidthValue});`);
2094
- } else rules.push(`padding: ${basePadding.vertical} ${basePadding.horizontal};`);
2095
- rules.push(`font-weight: ${settings.fontWeight ?? 500};`);
2096
- if (settings.textTransform && settings.textTransform !== "none") rules.push(`text-transform: ${settings.textTransform};`);
2097
- rules.push("cursor: pointer;");
2098
- rules.push("transition: all 200ms ease-in-out;");
2099
- rules.push("display: inline-flex;");
2100
- rules.push("align-items: center;");
2101
- rules.push("justify-content: center;");
2102
- rules.push(`border-radius: ${CORNER_RADIUS_MAP$2[settings.corners]};`);
2103
- rules.push(`box-shadow: ${SHADOW_VALUES$2[settings.shadow]};`);
2104
- if (variant.background === "solid" && variant.backgroundToken) rules.push(`background-color: rgb(var(--tb-${variant.backgroundToken}));`);
2105
- else if (variant.background === "gradient" && variant.gradientStops) {
2106
- const colors = variant.gradientStops.map((token) => `rgb(var(--tb-${token}))`).join(", ");
2107
- const direction = GRADIENT_DIRECTION_MAP$1[variant.gradientDirection ?? "to-r"];
2108
- rules.push(`background-image: linear-gradient(${direction}, ${colors});`);
2109
- } else rules.push("background-color: transparent;");
2110
- const textColor = variant.background === "transparent" ? `var(--rb-surface-link-color, rgb(var(--tb-${variant.textToken})))` : `rgb(var(--tb-${variant.textToken}))`;
2111
- rules.push(`color: ${textColor};`);
2112
- if (variant.borderToken) {
2113
- const borderColor = variant.background === "transparent" ? `var(--rb-surface-link-color, rgb(var(--tb-${variant.borderToken})))` : `rgb(var(--tb-${variant.borderToken}))`;
2114
- rules.push(`border: ${borderWidthValue} solid ${borderColor};`);
2115
- } else rules.push("border: none;");
2116
2884
  return [
2117
- `${selector} {\n ${rules.join("\n ")}\n}`,
2118
- `${selector}:hover {\n opacity: 0.9;\n}`,
2119
- `${selector}:active {\n opacity: 0.8;\n transform: scale(0.98);\n}`,
2120
- `${selector}:disabled,
2121
- ${selector}:disabled:hover,
2122
- ${selector}:disabled:active {
2123
- opacity: 0.5;
2124
- cursor: not-allowed;
2125
- pointer-events: none;
2126
- transform: none;
2127
- }`
2885
+ rule(selector, [
2886
+ ...borderWidth === "0" ? ["padding: 0.5rem 1rem;"] : [
2887
+ `padding-top: calc(0.5rem - ${borderWidth});`,
2888
+ `padding-bottom: calc(0.5rem - ${borderWidth});`,
2889
+ `padding-left: calc(1rem - ${borderWidth});`,
2890
+ `padding-right: calc(1rem - ${borderWidth});`
2891
+ ],
2892
+ `font-weight: ${settings.fontWeight ?? 500};`,
2893
+ ...settings.textTransform && settings.textTransform !== "none" ? [`text-transform: ${settings.textTransform};`] : [],
2894
+ "cursor: pointer;",
2895
+ "transition: all 200ms ease-in-out;",
2896
+ "display: inline-flex;",
2897
+ "align-items: center;",
2898
+ "justify-content: center;",
2899
+ `border-radius: ${CORNER_RADIUS_MAP$2[settings.corners]};`,
2900
+ `box-shadow: ${SHADOW_VALUES$2[settings.shadow]};`,
2901
+ ...declarationStrings(resolvedButtonColourDeclarations(result.states.base)),
2902
+ borderWidth === "0" ? "border: none;" : `border: ${borderWidth} solid currentColor;`
2903
+ ]),
2904
+ rule(`${selector}:hover`, declarationStrings(resolvedButtonColourDeclarations(result.states.hover))),
2905
+ rule(`${selector}:active`, [...declarationStrings(resolvedButtonColourDeclarations(result.states.active)), "transform: scale(0.98);"]),
2906
+ rule(`${selector}:focus-visible`, declarationStrings(resolvedButtonFocusDeclarations(result.focusIndicator))),
2907
+ rule(`${selector}:disabled,\n${selector}:disabled:hover,\n${selector}:disabled:active`, [...declarationStrings(resolvedButtonColourDeclarations(result.states.disabled)), ...declarationStrings(RESOLVED_BUTTON_DISABLED_DECLARATIONS)])
2128
2908
  ].join("\n\n");
2129
2909
  }
2130
- /**
2131
- * Get default button variants for SDK usage.
2132
- * These create primary, secondary, outline, and ghost buttons.
2133
- */
2910
+ function declarationStrings(declarations) {
2911
+ return declarations.map(([property, value]) => `${property}: ${value};`);
2912
+ }
2913
+ function rule(selector, declarations) {
2914
+ return `${selector} {\n ${declarations.join("\n ")}\n}`;
2915
+ }
2134
2916
  function getDefaultButtonVariants() {
2135
2917
  return [
2136
2918
  {
2137
2919
  id: "primary",
2138
- background: "solid",
2139
- backgroundToken: "primary",
2140
- textToken: "white"
2920
+ treatment: {
2921
+ type: "solid",
2922
+ colorToken: unsafeAsOpaqueButtonColourToken("primary")
2923
+ }
2141
2924
  },
2142
2925
  {
2143
2926
  id: "secondary",
2144
- background: "solid",
2145
- backgroundToken: "secondary",
2146
- textToken: "white"
2927
+ treatment: {
2928
+ type: "solid",
2929
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
2930
+ }
2147
2931
  },
2148
2932
  {
2149
2933
  id: "outline",
2150
- background: "transparent",
2151
- borderToken: "primary",
2152
- textToken: "primary",
2934
+ treatment: {
2935
+ type: "surface",
2936
+ role: "link"
2937
+ },
2153
2938
  borderWidth: "thin"
2154
2939
  },
2155
2940
  {
2156
2941
  id: "ghost",
2157
- background: "transparent",
2158
- textToken: "primary"
2942
+ treatment: {
2943
+ type: "surface",
2944
+ role: "link"
2945
+ }
2159
2946
  }
2160
2947
  ];
2161
2948
  }
@@ -2271,53 +3058,6 @@ const cardVariantSchema = z$1.object({
2271
3058
  });
2272
3059
  const cardSystemSchema = z$1.object({ variants: z$1.array(cardVariantSchema).min(1).max(3) });
2273
3060
  //#endregion
2274
- //#region ../theme-core/src/surfaces/foregroundRoles.ts
2275
- const SURFACE_FOREGROUND_ROLES = [
2276
- "text",
2277
- "heading",
2278
- "link",
2279
- "supporting"
2280
- ];
2281
- //#endregion
2282
- //#region ../theme-core/src/surfaces/foregroundContract.ts
2283
- const SURFACE_FOREGROUND_CSS_VARS = {
2284
- text: "--rb-surface-text-color",
2285
- heading: "--rb-surface-heading-color",
2286
- link: "--rb-surface-link-color",
2287
- supporting: "--rb-surface-supporting-color"
2288
- };
2289
- const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
2290
- const DEFAULT_ROLE_FALLBACKS = {
2291
- text: DEFAULT_SURFACE_TEXT_COLOR,
2292
- heading: DEFAULT_SURFACE_TEXT_COLOR,
2293
- link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
2294
- supporting: "rgb(var(--tb-mutedText, 100 116 139))"
2295
- };
2296
- function requireSurfaceColorValue(role, value) {
2297
- const normalized = value.trim();
2298
- if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
2299
- return normalized;
2300
- }
2301
- function completeSurfaceForegrounds(input) {
2302
- const text = requireSurfaceColorValue("text", input.text);
2303
- return {
2304
- text,
2305
- heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
2306
- link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
2307
- supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
2308
- };
2309
- }
2310
- function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
2311
- const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
2312
- const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
2313
- if (role === "text") return `var(${roleVariable}, ${fallback})`;
2314
- return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
2315
- }
2316
- function surfaceForegroundDeclarations(input) {
2317
- const foregrounds = completeSurfaceForegrounds(input);
2318
- return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
2319
- }
2320
- //#endregion
2321
3061
  //#region ../theme-core/src/cards/core.ts
2322
3062
  /**
2323
3063
  * Core Card CSS Generator
@@ -4279,19 +5019,6 @@ z$1.object({
4279
5019
  themes: z$1.array(themeSchema).length(3)
4280
5020
  });
4281
5021
  //#endregion
4282
- //#region ../theme-core/src/palette/utils/colorConversion.ts
4283
- function hexToRgb(hex) {
4284
- const h = hex.replace(/^#/, "");
4285
- const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
4286
- if (m.length !== 6) throw new Error("Bad hex");
4287
- const int = parseInt(m, 16);
4288
- return {
4289
- r: int >> 16 & 255,
4290
- g: int >> 8 & 255,
4291
- b: int & 255
4292
- };
4293
- }
4294
- //#endregion
4295
5022
  //#region ../theme-core/src/styles/shadow.ts
4296
5023
  function generateShadowCssVars(theme) {
4297
5024
  const level = theme.shadow.elevation;
@@ -6197,6 +6924,214 @@ const originalJoyCardSystem = { variants: [{
6197
6924
  const serifDisplayQuote = { style: "serif-display" };
6198
6925
  /** Punchy quotation mark in the secondary accent colour. Bold / accent styles. */
6199
6926
  const accentGlyphQuote = { style: "accent-glyph" };
6927
+ /**
6928
+ * Curated library of gradient presets.
6929
+ * All gradients use theme color tokens, so they adapt to the site's palette.
6930
+ */
6931
+ const GRADIENT_PRESETS = [
6932
+ {
6933
+ id: "primary-secondary",
6934
+ name: "Primary to Secondary",
6935
+ description: "Blend your primary and secondary brand colors",
6936
+ type: "linear",
6937
+ angle: 135,
6938
+ stops: [{
6939
+ color: "primary-500",
6940
+ position: 0
6941
+ }, {
6942
+ color: "secondary-500",
6943
+ position: 100
6944
+ }]
6945
+ },
6946
+ {
6947
+ id: "primary-accent",
6948
+ name: "Primary to Accent",
6949
+ description: "Energetic gradient from primary to accent",
6950
+ type: "linear",
6951
+ angle: 135,
6952
+ stops: [{
6953
+ color: "primary-500",
6954
+ position: 0
6955
+ }, {
6956
+ color: "accent-500",
6957
+ position: 100
6958
+ }]
6959
+ },
6960
+ {
6961
+ id: "secondary-accent",
6962
+ name: "Secondary to Accent",
6963
+ description: "Vibrant secondary to accent blend",
6964
+ type: "linear",
6965
+ angle: 135,
6966
+ stops: [{
6967
+ color: "secondary-500",
6968
+ position: 0
6969
+ }, {
6970
+ color: "accent-500",
6971
+ position: 100
6972
+ }]
6973
+ },
6974
+ {
6975
+ id: "primary-light-dark",
6976
+ name: "Primary Light to Dark",
6977
+ description: "Subtle primary color fade",
6978
+ type: "linear",
6979
+ angle: 180,
6980
+ stops: [{
6981
+ color: "primary-500",
6982
+ position: 0
6983
+ }, {
6984
+ color: "primary-900",
6985
+ position: 100
6986
+ }]
6987
+ },
6988
+ {
6989
+ id: "primary-dark-light",
6990
+ name: "Primary Dark to Light",
6991
+ description: "Reverse primary fade",
6992
+ type: "linear",
6993
+ angle: 180,
6994
+ stops: [{
6995
+ color: "primary-900",
6996
+ position: 0
6997
+ }, {
6998
+ color: "primary-50",
6999
+ position: 100
7000
+ }]
7001
+ },
7002
+ {
7003
+ id: "secondary-light-dark",
7004
+ name: "Secondary Light to Dark",
7005
+ description: "Subtle secondary color fade",
7006
+ type: "linear",
7007
+ angle: 180,
7008
+ stops: [{
7009
+ color: "secondary-500",
7010
+ position: 0
7011
+ }, {
7012
+ color: "secondary-900",
7013
+ position: 100
7014
+ }]
7015
+ },
7016
+ {
7017
+ id: "accent-light-dark",
7018
+ name: "Accent Light to Dark",
7019
+ description: "Accent color depth fade",
7020
+ type: "linear",
7021
+ angle: 180,
7022
+ stops: [{
7023
+ color: "accent-500",
7024
+ position: 0
7025
+ }, {
7026
+ color: "accent-900",
7027
+ position: 100
7028
+ }]
7029
+ },
7030
+ {
7031
+ id: "background-surface",
7032
+ name: "Background to Surface",
7033
+ description: "Subtle depth using base colors",
7034
+ type: "linear",
7035
+ angle: 180,
7036
+ stops: [{
7037
+ color: "background",
7038
+ position: 0
7039
+ }, {
7040
+ color: "surface",
7041
+ position: 100
7042
+ }]
7043
+ },
7044
+ {
7045
+ id: "neutral-fade",
7046
+ name: "Neutral Gradient",
7047
+ description: "Elegant neutral color fade",
7048
+ type: "linear",
7049
+ angle: 180,
7050
+ stops: [{
7051
+ color: "neutral-900",
7052
+ position: 0
7053
+ }, {
7054
+ color: "neutral-50",
7055
+ position: 100
7056
+ }]
7057
+ },
7058
+ {
7059
+ id: "primary-glow",
7060
+ name: "Primary glow",
7061
+ description: "A soft top-centred glow using your primary palette",
7062
+ type: "radial",
7063
+ shape: "ellipse",
7064
+ position: "top center",
7065
+ stops: [{
7066
+ color: "primary-900",
7067
+ position: 0
7068
+ }, {
7069
+ color: "primary-950",
7070
+ position: 100
7071
+ }],
7072
+ foreground: {
7073
+ textColor: "onPrimary900",
7074
+ headingColor: "onPrimary900",
7075
+ linkColor: "onPrimary900",
7076
+ supportingColor: "onPrimary900"
7077
+ }
7078
+ },
7079
+ {
7080
+ id: "accent-glow",
7081
+ name: "Accent glow",
7082
+ description: "A soft top-centred glow using your accent palette",
7083
+ type: "radial",
7084
+ shape: "ellipse",
7085
+ position: "top center",
7086
+ stops: [{
7087
+ color: "accent-900",
7088
+ position: 0
7089
+ }, {
7090
+ color: "accent-950",
7091
+ position: 100
7092
+ }],
7093
+ foreground: {
7094
+ textColor: "onAccent900",
7095
+ headingColor: "onAccent900",
7096
+ linkColor: "onAccent900",
7097
+ supportingColor: "onAccent900"
7098
+ }
7099
+ },
7100
+ {
7101
+ id: "primary-secondary-accent",
7102
+ name: "Brand Trio",
7103
+ description: "All three brand colors in harmony",
7104
+ type: "linear",
7105
+ angle: 135,
7106
+ stops: [
7107
+ {
7108
+ color: "primary-500",
7109
+ position: 0
7110
+ },
7111
+ {
7112
+ color: "secondary-500",
7113
+ position: 50
7114
+ },
7115
+ {
7116
+ color: "accent-500",
7117
+ position: 100
7118
+ }
7119
+ ]
7120
+ }
7121
+ ].map((preset) => ({
7122
+ ...preset,
7123
+ id: preset.id,
7124
+ stops: preset.stops
7125
+ }));
7126
+ const GRADIENT_PRESET_IDS = GRADIENT_PRESETS.map((preset) => preset.id);
7127
+ const gradientPresetIdSet = new Set(GRADIENT_PRESET_IDS);
7128
+ Object.fromEntries(GRADIENT_PRESETS.map((preset) => [preset.id, preset]));
7129
+ function parseGradientPresetId(value) {
7130
+ if (typeof value !== "string") return null;
7131
+ const normalized = value.trim();
7132
+ if (!gradientPresetIdSet.has(normalized)) return null;
7133
+ return normalized;
7134
+ }
6200
7135
  //#endregion
6201
7136
  //#region ../theme-core/src/site-styles/headerCtaVariants.ts
6202
7137
  function hasButtonVariant(theme, variantId) {
@@ -8282,6 +9217,19 @@ asBoundaryOptionId("break:angle-balanced");
8282
9217
  asBoundaryOptionId("break:angle-steep");
8283
9218
  //#endregion
8284
9219
  //#region ../theme-core/src/site-styles/runtimeCompiler.ts
9220
+ function defineSectionForegroundRuntimeTokens(input) {
9221
+ return {
9222
+ textColor: requireThemeColorToken(input.textColor),
9223
+ headingColor: requireThemeColorToken(input.headingColor),
9224
+ linkColor: requireThemeColorToken(input.linkColor),
9225
+ supportingColor: requireThemeColorToken(input.supportingColor)
9226
+ };
9227
+ }
9228
+ function requireThemeColorToken(value) {
9229
+ const token = parseThemeColorToken(value);
9230
+ if (!token) throw new Error(`Invalid section foreground Theme color token: ${String(value)}`);
9231
+ return token;
9232
+ }
8285
9233
  function defineDesignRuntimeRules(rules) {
8286
9234
  return rules;
8287
9235
  }
@@ -8529,24 +9477,24 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
8529
9477
  type: "color",
8530
9478
  color: "muted"
8531
9479
  },
8532
- foreground: {
9480
+ foreground: defineSectionForegroundRuntimeTokens({
8533
9481
  textColor: "text",
8534
9482
  headingColor: "primary",
8535
9483
  linkColor: "primary",
8536
9484
  supportingColor: "text"
8537
- }
9485
+ })
8538
9486
  },
8539
9487
  "brand-depth-band": {
8540
9488
  background: {
8541
9489
  type: "color",
8542
9490
  color: "primary-800"
8543
9491
  },
8544
- foreground: {
9492
+ foreground: defineSectionForegroundRuntimeTokens({
8545
9493
  textColor: "text",
8546
9494
  headingColor: "primary",
8547
9495
  linkColor: "primary",
8548
9496
  supportingColor: "text"
8549
- }
9497
+ })
8550
9498
  },
8551
9499
  "hero-image": {
8552
9500
  background: {
@@ -8554,12 +9502,32 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
8554
9502
  opacity: 1,
8555
9503
  position: "center center"
8556
9504
  },
8557
- foreground: {
9505
+ foreground: defineSectionForegroundRuntimeTokens({
8558
9506
  textColor: "text",
8559
9507
  headingColor: "primary",
8560
9508
  linkColor: "primary",
8561
9509
  supportingColor: "text"
8562
- }
9510
+ })
9511
+ }
9512
+ }
9513
+ });
9514
+ const primaryGlowGradientPresetId = parseGradientPresetId("primary-glow");
9515
+ if (!primaryGlowGradientPresetId) throw new Error("Expected primary-glow in the Theme Core gradient catalog");
9516
+ const fulcrumDesignRuntimeRules = defineDesignRuntimeRules({
9517
+ ...defaultDesignRuntimeRules,
9518
+ sectionSurfaces: {
9519
+ ...defaultDesignRuntimeRules.sectionSurfaces,
9520
+ "accent-band": {
9521
+ background: {
9522
+ type: "gradient",
9523
+ gradientPresetId: primaryGlowGradientPresetId
9524
+ },
9525
+ foreground: defineSectionForegroundRuntimeTokens({
9526
+ textColor: "primaryForeground",
9527
+ headingColor: "primaryForeground",
9528
+ linkColor: "primaryForeground",
9529
+ supportingColor: "primaryForeground"
9530
+ })
8563
9531
  }
8564
9532
  }
8565
9533
  });
@@ -9947,6 +10915,8 @@ const curatedSiteStyles = [
9947
10915
  },
9948
10916
  compositionBudget: quietBudget,
9949
10917
  typography: fulcrumTypography,
10918
+ designRuntimeRules: fulcrumDesignRuntimeRules,
10919
+ recommendedAppearancePresetIds: ["cta-accent-band"],
9950
10920
  themeOverrides: {
9951
10921
  shadow: { colorToken: "primary" },
9952
10922
  accordions: {
@@ -18538,6 +19508,11 @@ const containerResponsiveThemeCss = `/*
18538
19508
  flex-wrap: wrap;
18539
19509
  }
18540
19510
 
19511
+ /* Text-style CTA actions belong to the section foreground contract. */
19512
+ :where(.theme-scope) [data-block="cta-full"] .cta-row .button-link {
19513
+ color: var(--rb-surface-link-color, var(--rb-surface-text-color, rgb(var(--tb-primary))));
19514
+ }
19515
+
18541
19516
  /* =============================================================================
18542
19517
  * Block: Body Text (content block, Phase 2)
18543
19518
  *
@@ -24371,8 +25346,12 @@ function buildThemeBridgeRuntime(config) {
24371
25346
  };
24372
25347
  }
24373
25348
  function buildBridgeCssVars(config) {
25349
+ const tokens = resolveBridgeTokens(config.tokens);
24374
25350
  return {
24375
- ...buildColorCssVars(resolveBridgeTokens(config.tokens)),
25351
+ ...buildColorCssVars({
25352
+ ...tokens,
25353
+ ...deriveConcreteBridgeOnTokens(tokens)
25354
+ }),
24376
25355
  ...buildTypographyCssVars(config.typography),
24377
25356
  ...buildFontSizeCssVars(),
24378
25357
  ...buildTextRhythmCssVars(),
@@ -24509,6 +25488,41 @@ function resolveBridgeTokens(tokens) {
24509
25488
  for (const [name, value] of Object.entries(BRIDGE_DEFAULT_TOKENS)) if (!isPresentTokenValue(resolved[name])) resolved[name] = value;
24510
25489
  return resolved;
24511
25490
  }
25491
+ function deriveConcreteBridgeOnTokens(tokens) {
25492
+ const derived = {};
25493
+ for (const [name, value] of Object.entries(tokens)) {
25494
+ if (!isOnColorBackgroundCandidateToken(name)) continue;
25495
+ const hex = rgbChannelsToOpaqueHex(value);
25496
+ if (hex === null) continue;
25497
+ const onToken = onColorTokenName(name);
25498
+ if (tokens[onToken] === void 0) derived[onToken] = hexToRgbChannels(getTextColorForBackground(hex));
25499
+ }
25500
+ return derived;
25501
+ }
25502
+ function buildBridgeButtonPalette(tokens) {
25503
+ const palette = {};
25504
+ for (const [name, value] of Object.entries(tokens)) {
25505
+ const hex = rgbChannelsToOpaqueHex(value);
25506
+ if (hex !== null) palette[name] = hex;
25507
+ }
25508
+ for (const [name, value] of Object.entries(deriveConcreteBridgeOnTokens(tokens))) {
25509
+ const hex = rgbChannelsToOpaqueHex(value);
25510
+ if (hex !== null) palette[name] = hex;
25511
+ }
25512
+ return {
25513
+ ...palette,
25514
+ meta: { mode: "light" }
25515
+ };
25516
+ }
25517
+ function rgbChannelsToOpaqueHex(value) {
25518
+ const match = value.match(RGB_CHANNELS_PATTERN);
25519
+ if (!match || match[4] !== void 0) return null;
25520
+ return `#${[
25521
+ Number(match[1]),
25522
+ Number(match[2]),
25523
+ Number(match[3])
25524
+ ].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
25525
+ }
24512
25526
  function isPresentTokenValue(value) {
24513
25527
  return typeof value === "string" && value.trim().length > 0;
24514
25528
  }
@@ -24586,13 +25600,15 @@ function generateButtonsCss(config, corners, shadows) {
24586
25600
  const requestedIds = new Set(buttonConfig.variants);
24587
25601
  variants = variants.filter((variant) => requestedIds.has(variant.id));
24588
25602
  }
24589
- return generateButtonCoreCss("sdk", variants, {
25603
+ const settings = {
24590
25604
  corners,
24591
25605
  shadow: shadows,
24592
25606
  borderWidth: "thin",
24593
25607
  fontWeight: 500,
24594
25608
  textTransform: "none"
24595
- });
25609
+ };
25610
+ const tokens = resolveBridgeTokens(config.tokens);
25611
+ return generateButtonCoreCss("sdk", variants, settings, buildBridgeButtonPalette(tokens));
24596
25612
  }
24597
25613
  function generateCardsCss(config, corners, shadows) {
24598
25614
  let variants = getDefaultCardVariants();