@riverbankcms/sdk 0.109.0 → 0.118.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 (294) hide show
  1. package/dist/{PreviewEditorSidebar-DWqwfy3_.mjs → PreviewEditorSidebar-BBcK1DjD.mjs} +178 -117
  2. package/dist/{PreviewEditorUI-B7OL4sfR.mjs → PreviewEditorUI-CuBFoYes.mjs} +3 -4
  3. package/dist/{PreviewQueryProvider-D0y2l8JN.mjs → PreviewQueryProvider-nKUT4TL-.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-D8Gt9Gbe.mjs → SdkPreviewModeRuntime-BWIO_Dsi.mjs} +2789 -34525
  5. package/dist/{SiteChromeCustomizeContext-DUpalEdL.mjs → SiteChromeCustomizeContext-CciG60M5.mjs} +88526 -57212
  6. package/dist/_dts/ai/src/contracts/proposals.d.ts +6939 -727
  7. package/dist/_dts/ai/src/designer/themePatch.d.ts +1 -1
  8. package/dist/_dts/api/src/bookingAdjustments.d.ts +30 -36
  9. package/dist/_dts/api/src/bookingRecords.d.ts +6 -74
  10. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +3 -3
  11. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +5 -5
  12. package/dist/_dts/api/src/contentRuntime.d.ts +9 -2
  13. package/dist/_dts/api/src/courses.d.ts +30 -15
  14. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +4 -4
  15. package/dist/_dts/api/src/endpoints.d.ts +49 -4
  16. package/dist/_dts/api/src/event-presentation.d.ts +4 -0
  17. package/dist/_dts/api/src/index.d.ts +3 -3
  18. package/dist/_dts/api/src/navigation/dashboardSettingsNav.d.ts +2 -2
  19. package/dist/_dts/api/src/sdk-event-upsert.d.ts +2 -2
  20. package/dist/_dts/api/src/sdkContracts.d.ts +108 -18
  21. package/dist/_dts/api/src/siteBanner.d.ts +8 -8
  22. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +5 -1
  23. package/dist/_dts/api/src/siteOperations.d.ts +12 -20
  24. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +32 -4
  25. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +6 -6
  26. package/dist/_dts/api/src/webhooks.d.ts +98 -1
  27. package/dist/_dts/billing/src/site-arrangements.d.ts +3 -0
  28. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldDefaults.d.ts +3 -12
  29. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldSchema.d.ts +1 -5
  30. package/dist/_dts/blocks/src/bindings/index.d.ts +1 -10
  31. package/dist/_dts/blocks/src/client/course-registration.d.ts +4 -1
  32. package/dist/_dts/blocks/src/client.d.ts +4 -1
  33. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -4
  34. package/dist/_dts/blocks/src/index.d.ts +6 -7
  35. package/dist/_dts/blocks/src/primitives/content/primitives.d.ts +2 -2
  36. package/dist/_dts/blocks/src/system/blocks/body-text.d.ts +2 -2
  37. package/dist/_dts/blocks/src/system/blocks/course-registration.d.ts +2 -1
  38. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +2 -1
  39. package/dist/_dts/blocks/src/system/blocks/cta-full.d.ts +2 -4
  40. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +2 -1
  41. package/dist/_dts/blocks/src/system/blocks/events/event-listing.d.ts +2 -1
  42. package/dist/_dts/blocks/src/system/blocks/events/event-spotlight.d.ts +2 -1
  43. package/dist/_dts/blocks/src/system/blocks/events/recurring-schedule.d.ts +5 -4
  44. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +2 -8
  45. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +2 -1
  46. package/dist/_dts/blocks/src/system/blocks/feature-card-icons.d.ts +2 -2
  47. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +3 -3
  48. package/dist/_dts/blocks/src/system/blocks/file-download.d.ts +2 -1
  49. package/dist/_dts/blocks/src/system/blocks/form.d.ts +2 -15
  50. package/dist/_dts/blocks/src/system/blocks/intro.d.ts +2 -2
  51. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +66 -66
  52. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +4 -17
  53. package/dist/_dts/blocks/src/system/blocks/shop.d.ts +2 -1
  54. package/dist/_dts/blocks/src/system/blocks/single-button.d.ts +2 -1
  55. package/dist/_dts/blocks/src/system/blocks/site-footer.d.ts +0 -3
  56. package/dist/_dts/blocks/src/system/constants/buttonRoles.d.ts +35 -0
  57. package/dist/_dts/blocks/src/system/fields/button.d.ts +10 -21
  58. package/dist/_dts/blocks/src/system/fields/ctas.d.ts +2 -1
  59. package/dist/_dts/blocks/src/system/fragments/index.d.ts +0 -1
  60. package/dist/_dts/blocks/src/system/fragments/library/heroCtaRow.d.ts +9 -5
  61. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +19 -2
  62. package/dist/_dts/blocks/src/system/manifest/richTextSchema.d.ts +8 -70
  63. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +1 -0
  64. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +6 -0
  65. package/dist/_dts/blocks/src/system/node/schema.d.ts +4 -4
  66. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +18 -18
  67. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +9 -9
  68. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessWholeSeriesRegistrationFlow.d.ts +29 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +1 -10
  70. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +5 -21
  71. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/ErrorStep.d.ts +2 -1
  72. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/PaymentFailedStep.d.ts +2 -1
  73. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/VerifyingTimeoutStep.d.ts +2 -1
  74. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/types.d.ts +21 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.d.ts +4 -4
  76. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/whole-series-registration.headless.contract.d.ts +326 -0
  77. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/whole-series-registration.headless.planning.d.ts +27 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration.interactive.d.ts +5 -4
  79. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +3 -2
  80. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +2 -1
  81. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +5 -4
  82. package/dist/_dts/blocks/src/system/runtime/nodes/file-download.d.ts +2 -3
  83. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +2 -1
  84. package/dist/_dts/blocks/src/system/runtime/nodes/richText-coerce.d.ts +14 -2
  85. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +6 -3
  86. package/dist/_dts/blocks/src/system/runtime/nodes/shop.interactive.d.ts +7 -6
  87. package/dist/_dts/blocks/src/system/runtime/nodes/shop.types.d.ts +2 -1
  88. package/dist/_dts/blocks/src/system/runtime/shared/types.d.ts +2 -1
  89. package/dist/_dts/blocks/src/system/transforms/typed.d.ts +1 -1
  90. package/dist/_dts/content-editor/src/FooterComposerPanel.d.ts +4 -2
  91. package/dist/_dts/content-editor/src/controller/seedDraftBaselinesFromBlocks.d.ts +1 -1
  92. package/dist/_dts/content-editor/src/hooks/useBlockAutosave.d.ts +17 -1
  93. package/dist/_dts/content-editor/src/hooks/useDebouncedLatestSave.d.ts +1 -1
  94. package/dist/_dts/content-editor/src/hooks/useSimpleAutosave.d.ts +1 -1
  95. package/dist/_dts/content-editor/src/index.d.ts +12 -12
  96. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +3 -2
  97. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +19 -4
  98. package/dist/_dts/content-editor/src/ui/stores/autosaveStore.d.ts +1 -1
  99. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +1 -1
  100. package/dist/_dts/content-model/src/content-types/builtins.d.ts +3 -15
  101. package/dist/_dts/content-model/src/content-types/entryValidation.d.ts +2 -0
  102. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  103. package/dist/_dts/content-model/src/templates/defaults.d.ts +1 -21
  104. package/dist/_dts/core/src/editor-state.d.ts +14 -1
  105. package/dist/_dts/core/src/footer-composition.d.ts +2 -1
  106. package/dist/_dts/core/src/index.d.ts +1 -0
  107. package/dist/_dts/core/src/rich-text.d.ts +16 -0
  108. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +367 -69
  109. package/dist/_dts/editor/src/index.d.ts +1 -1
  110. package/dist/_dts/editor/src/primitives/rich-text/utils.d.ts +3 -5
  111. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +1 -1
  112. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +2 -1
  113. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +2 -1
  114. package/dist/_dts/editor-blocks/src/block-editor/blockDraftBaselineSeed.d.ts +15 -0
  115. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockBaselineSync.d.ts +3 -7
  116. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockDuplication.d.ts +12 -0
  117. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +1 -0
  118. package/dist/_dts/editor-blocks/src/block-editor/utils.d.ts +1 -1
  119. package/dist/_dts/editor-blocks/src/index.d.ts +6 -5
  120. package/dist/_dts/editor-blocks/src/widgets/button/context.d.ts +3 -2
  121. package/dist/_dts/editor-blocks/src/widgets/rich-text/RichTextField.d.ts +2 -2
  122. package/dist/_dts/media-core/src/contentMediaAudit.d.ts +104 -0
  123. package/dist/_dts/media-core/src/contentMediaAuditFieldAdapter.d.ts +8 -0
  124. package/dist/_dts/media-core/src/contentMediaSnapshotAudit.d.ts +22 -0
  125. package/dist/_dts/media-core/src/index.d.ts +3 -1
  126. package/dist/_dts/preview-next/src/client/index.d.ts +3 -2
  127. package/dist/_dts/preview-next/src/client/preview/ButtonStylePicker.d.ts +16 -0
  128. package/dist/_dts/preview-next/src/client/preview/PageBlockEditorPanel.d.ts +2 -3
  129. package/dist/_dts/preview-next/src/client/preview/PreviewDesignOpenRequestContext.d.ts +7 -0
  130. package/dist/_dts/preview-next/src/client/preview/PreviewEditorSidebar.d.ts +5 -11
  131. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +5 -6
  132. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +6 -7
  133. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +7 -9
  134. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.d.ts +4 -11
  135. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.state.d.ts +7 -7
  136. package/dist/_dts/preview-next/src/client/preview/sidebarTabPreference.d.ts +0 -8
  137. package/dist/_dts/preview-next/src/client/preview/siteChromeLookRows.d.ts +1 -11
  138. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +1 -5
  139. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +2 -3
  140. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +3 -4
  141. package/dist/_dts/preview-next/src/client/preview/styleConfiguratorSnapshot.d.ts +3 -5
  142. package/dist/_dts/sdk/src/bookings/index.d.ts +5 -4
  143. package/dist/_dts/sdk/src/bookings/types.d.ts +3 -3
  144. package/dist/_dts/sdk/src/cli/commands/event.d.ts +18 -1
  145. package/dist/_dts/sdk/src/cli/commands/push/execute/theme.d.ts +2 -16
  146. package/dist/_dts/sdk/src/cli/commands/style.d.ts +3 -2
  147. package/dist/_dts/sdk/src/cli/content/reader.d.ts +8 -5
  148. package/dist/_dts/sdk/src/client/management/events.d.ts +2 -1
  149. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  150. package/dist/_dts/sdk/src/client/management/types.d.ts +38 -10
  151. package/dist/_dts/sdk/src/config/typed-entries.d.ts +7 -7
  152. package/dist/_dts/sdk/src/config/validation.d.ts +7 -7
  153. package/dist/_dts/sdk/src/contracts/content.d.ts +5 -15
  154. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  155. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +24 -40
  156. package/dist/_dts/sdk/src/contracts/theme.d.ts +2 -18
  157. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +1 -1
  158. package/dist/_dts/sdk/src/manifest/three-pass-converter.d.ts +4 -4
  159. package/dist/_dts/sdk/src/public-api/contracts.d.ts +1 -1
  160. package/dist/_dts/sdk/src/rendering/helpers/bindings.d.ts +1 -2
  161. package/dist/_dts/sdk/src/rendering/helpers/index.d.ts +1 -1
  162. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +2 -13
  163. package/dist/_dts/sdk/src/theme/index.d.ts +0 -29
  164. package/dist/_dts/sdk/src/version.d.ts +1 -1
  165. package/dist/_dts/sdk/src/webhooks/index.d.ts +1 -1
  166. package/dist/_dts/sdk/src/webhooks/verify.d.ts +3 -3
  167. package/dist/_dts/site-commands/src/commands.d.ts +160 -24
  168. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +2 -2
  169. package/dist/_dts/theme-core/src/buttons/core.d.ts +0 -1
  170. package/dist/_dts/theme-core/src/buttons/effectClassification.d.ts +22 -9
  171. package/dist/_dts/theme-core/src/buttons/effects/presets/background.d.ts +1 -2
  172. package/dist/_dts/theme-core/src/buttons/effects/presets/border.d.ts +1 -2
  173. package/dist/_dts/theme-core/src/buttons/effects/presets/overlay.d.ts +3 -1
  174. package/dist/_dts/theme-core/src/buttons/effects/presets/shadow.d.ts +2 -2
  175. package/dist/_dts/theme-core/src/buttons/effects/types.d.ts +8 -18
  176. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +2 -2
  177. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +5 -5
  178. package/dist/_dts/theme-core/src/buttons/index.d.ts +2 -5
  179. package/dist/_dts/theme-core/src/buttons/parseThemeForPersistence.d.ts +36 -0
  180. package/dist/_dts/theme-core/src/buttons/styles/domain.d.ts +80 -0
  181. package/dist/_dts/theme-core/src/buttons/styles/index.d.ts +3 -0
  182. package/dist/_dts/theme-core/src/buttons/styles/recipes/defineStaticButtonStyleRecipe.d.ts +49 -0
  183. package/dist/_dts/theme-core/src/buttons/styles/recipes/quietArrow.d.ts +67 -0
  184. package/dist/_dts/theme-core/src/buttons/styles/recipes/staticRecipes.d.ts +649 -0
  185. package/dist/_dts/theme-core/src/buttons/styles/registry.d.ts +753 -0
  186. package/dist/_dts/theme-core/src/buttons/styles/selection.d.ts +69 -0
  187. package/dist/_dts/theme-core/src/buttons/types.d.ts +1638 -93
  188. package/dist/_dts/theme-core/src/cards/types.d.ts +13 -13
  189. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +969 -69
  190. package/dist/_dts/theme-core/src/navigation/types.d.ts +2 -2
  191. package/dist/_dts/theme-core/src/recommend/generateThemes.d.ts +0 -1
  192. package/dist/_dts/theme-core/src/schema.d.ts +1100 -116
  193. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  194. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +26 -24
  195. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +14 -14
  196. package/dist/_dts/theme-core/src/site-styles/footerLooks.d.ts +19 -153
  197. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +2 -2
  198. package/dist/_dts/theme-core/src/site-styles/headerCtaVariants.d.ts +3 -1
  199. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  200. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +8 -9
  201. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +4 -4
  202. package/dist/_dts/ui/src/palette/hex.d.ts +3 -3
  203. package/dist/cli/index.mjs +7368 -6668
  204. package/dist/cli/init-docs/content/cli-reference.md +40 -1
  205. package/dist/cli/init-docs/content/content-management.md +6 -0
  206. package/dist/cli/init-docs/content/workflow-isr-revalidation.md +1 -1
  207. package/dist/client/{DiscountCodeDisclosure-DU1ygxU2.mjs → DiscountCodeDisclosure-DIWj-P9v.mjs} +1 -1
  208. package/dist/client/{EventCombined.client-DB5zagCo.mjs → EventCombined.client-De8YWBSr.mjs} +2 -3
  209. package/dist/client/{EventListing.client-CTX9JHBL.mjs → EventListing.client-seHTjimY.mjs} +2 -2
  210. package/dist/client/{LeafletStaticMap-BYImhgiB.mjs → LeafletStaticMap-BmQxFcJW.mjs} +1 -1
  211. package/dist/client/{MagicLinkForm-CRkw_M3l.mjs → MagicLinkForm-OoJAIUIj.mjs} +1 -2
  212. package/dist/client/{StepIndicator-BnmVBJJh.mjs → StepIndicator-CiZ8kdPk.mjs} +2 -2
  213. package/dist/client/{accordion-CeGreumd.mjs → accordion-6lnN__XJ.mjs} +1 -1
  214. package/dist/client/{bookingForm-CSzFNrpN.mjs → bookingForm-5uyqhONU.mjs} +6 -7
  215. package/dist/client/bookings.mjs +9179 -5865
  216. package/dist/client/{carousel-B2jDtogf.mjs → carousel-Dtnifil_.mjs} +1 -1
  217. package/dist/client/{cart-D-jZ-vaY.mjs → cart-5Tc5S7NV.mjs} +3 -3
  218. package/dist/client/{checkout-Dza-LA7N.mjs → checkout-CYKKN5Hr.mjs} +3 -3
  219. package/dist/client/client.mjs +3252 -3361
  220. package/dist/client/{colorTokenParts-CMugwvX_.mjs → colorStyles-BWpj_h4C.mjs} +118 -1
  221. package/dist/client/{courseRegistration-TYkNychb.mjs → courseRegistration-CxCBybB4.mjs} +100 -37
  222. package/dist/client/{eventCalendar-BI_90SsT.mjs → eventCalendar-Xj2jZS9X.mjs} +4 -5
  223. package/dist/client/{eventCombined-mNrpryE9.mjs → eventCombined-CgcMoOgM.mjs} +2 -2
  224. package/dist/client/{eventDetails-D4GVlCIY.mjs → eventDetails-CEAH8Edc.mjs} +3 -3
  225. package/dist/client/{eventListing-YhKN018y.mjs → eventListing-qyPVQ0wP.mjs} +2 -2
  226. package/dist/client/{eventRegistration-CdHGtilr.mjs → eventRegistration-DDu79CtL.mjs} +6 -7
  227. package/dist/client/{form-AcAL45rA.mjs → form-Bc7JKI7I.mjs} +1 -2
  228. package/dist/client/{headerSection-uEC52ueD.mjs → headerSection-vTku_LEl.mjs} +2 -3
  229. package/dist/client/hooks.mjs +1774 -2273
  230. package/dist/client/{locationMap-_wJDSUYe.mjs → locationMap-Bg7X4Jna.mjs} +8 -5
  231. package/dist/client/{media-BK0O3b-U.mjs → media-CNE1Z-wb.mjs} +1 -2
  232. package/dist/client/{newsletterForm-DrArsb2B.mjs → newsletterForm-lpylVdp5.mjs} +1 -2
  233. package/dist/client/{pricing-BSYVghwo.mjs → pricing-DE8fjJiT.mjs} +1 -1
  234. package/dist/client/{productDetail-Dyk90sMs.mjs → productDetail-D7Bo2RMH.mjs} +3 -3
  235. package/dist/client/{productList-BCKThwAI.mjs → productList-VjnZqiqN.mjs} +3 -3
  236. package/dist/client/{propsDecoder-CkXxkdSR.mjs → propsDecoder-DreFXjNP.mjs} +4256 -443
  237. package/dist/client/rendering/client.mjs +21776 -21970
  238. package/dist/client/rendering/islands.mjs +20 -20
  239. package/dist/client/rendering.mjs +3092 -3322
  240. package/dist/client/{shop-DeltVH10.mjs → shop-IRsHHbf7.mjs} +5 -6
  241. package/dist/client/{shop-commerce-BD9Jsm2Z.mjs → shop-commerce-gnI8PieZ.mjs} +27 -11
  242. package/dist/client/{shop-commerce-store-CmXD6FDS.mjs → shop-commerce-store-BTbrhObn.mjs} +1 -1
  243. package/dist/client/{shopShared-B8v-lH_F.mjs → shopShared-D6KS2_qk.mjs} +3 -3
  244. package/dist/client/{useDiscountCodeUi-DbWJN8ld.mjs → useDiscountCodeUi-DCccGCxV.mjs} +1 -1
  245. package/dist/client/{useEventFilters-EJIMUnqT.mjs → useEventFilters-eGs3rPZQ.mjs} +24 -126
  246. package/dist/client/{usePolling-Cmh14fc7.mjs → usePolling-CvSU2sur.mjs} +2 -2
  247. package/dist/preview-next/before-render.mjs +94 -6
  248. package/dist/preview-next/client/runtime.mjs +4 -4
  249. package/dist/preview-next/middleware.mjs +94 -6
  250. package/dist/{previewAuthStore-CTO7CRPk.mjs → previewAuthStore-DN7vcRsv.mjs} +94 -6
  251. package/dist/{sdk-runtime-Dou0GAav.mjs → sdk-runtime-vW8QbyPN.mjs} +2 -2
  252. package/dist/{separator-CI5wZNXT.mjs → separator-DJ4bkeY3.mjs} +1 -1
  253. package/dist/server/components.mjs +5194 -5568
  254. package/dist/server/config-template-validation.mjs +4 -23
  255. package/dist/server/config-validation.mjs +1774 -2273
  256. package/dist/server/config.mjs +2043 -2508
  257. package/dist/server/data.mjs +1774 -2273
  258. package/dist/server/index.mjs +95 -7
  259. package/dist/server/next/revalidate.mjs +53 -1
  260. package/dist/server/next.mjs +5337 -5632
  261. package/dist/server/page-converter.mjs +1833 -2307
  262. package/dist/server/prebuild.mjs +1 -1
  263. package/dist/server/rendering/server.mjs +6713 -4874
  264. package/dist/server/rendering.mjs +5203 -5586
  265. package/dist/server/routing.mjs +2199 -2698
  266. package/dist/server/server.mjs +1885 -2355
  267. package/dist/server/theme-bridge.mjs +1986 -2222
  268. package/dist/server/theme.mjs +2367 -1581
  269. package/dist/server/webhooks.mjs +54 -2
  270. package/package.json +1 -1
  271. package/dist/_dts/blocks/src/primitives/content/components/Button.d.ts +0 -40
  272. package/dist/_dts/blocks/src/system/fragments/library/footerBottomText.d.ts +0 -19
  273. package/dist/_dts/media-core/src/legacyMediaValue.d.ts +0 -11
  274. package/dist/_dts/theme-core/src/buttons/personalities/brushed-wash.d.ts +0 -16
  275. package/dist/_dts/theme-core/src/buttons/personalities/confident-chip.d.ts +0 -16
  276. package/dist/_dts/theme-core/src/buttons/personalities/earth-pill.d.ts +0 -16
  277. package/dist/_dts/theme-core/src/buttons/personalities/editorial-link.d.ts +0 -16
  278. package/dist/_dts/theme-core/src/buttons/personalities/flat-ink.d.ts +0 -20
  279. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +0 -172
  280. package/dist/_dts/theme-core/src/buttons/personalities/ink-stamp.d.ts +0 -16
  281. package/dist/_dts/theme-core/src/buttons/personalities/joy-stamp.d.ts +0 -22
  282. package/dist/_dts/theme-core/src/buttons/personalities/ornate-ritual.d.ts +0 -16
  283. package/dist/_dts/theme-core/src/buttons/personalities/pebble.d.ts +0 -16
  284. package/dist/_dts/theme-core/src/buttons/personalities/quiet-arrow.d.ts +0 -17
  285. package/dist/_dts/theme-core/src/buttons/personalities/showtime-pill.d.ts +0 -17
  286. package/dist/_dts/theme-core/src/buttons/personalities/soft-pill.d.ts +0 -16
  287. package/dist/_dts/theme-core/src/buttons/personalities/two-tone-pill.d.ts +0 -17
  288. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +0 -285
  289. package/dist/_dts/theme-core/src/styles/buttons.d.ts +0 -27
  290. package/dist/_dts/theme-core/src/styles/buttonsLibrary.d.ts +0 -2
  291. package/dist/_dts/theme-core/src/styles/glow.d.ts +0 -3
  292. package/dist/client/buttons-DoFV7KDL.mjs +0 -2115
  293. package/dist/client/colorStyles-CrgzoXsm.mjs +0 -99
  294. package/dist/client/foregroundContract-xICPq59x.mjs +0 -31
@@ -2715,151 +2715,6 @@ z$1.discriminatedUnion("kind", [
2715
2715
  })
2716
2716
  ]);
2717
2717
  //#endregion
2718
- //#region ../theme-core/src/surfaces/foregroundContract.ts
2719
- const SURFACE_FOREGROUND_CSS_VARS = {
2720
- text: "--rb-surface-text-color",
2721
- heading: "--rb-surface-heading-color",
2722
- link: "--rb-surface-link-color",
2723
- supporting: "--rb-surface-supporting-color"
2724
- };
2725
- const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
2726
- const DEFAULT_ROLE_FALLBACKS = {
2727
- text: DEFAULT_SURFACE_TEXT_COLOR,
2728
- heading: DEFAULT_SURFACE_TEXT_COLOR,
2729
- link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
2730
- supporting: "rgb(var(--tb-mutedText, 100 116 139))"
2731
- };
2732
- function requireSurfaceColorValue(role, value) {
2733
- const normalized = value.trim();
2734
- if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
2735
- return normalized;
2736
- }
2737
- function completeSurfaceForegrounds(input) {
2738
- const text = requireSurfaceColorValue("text", input.text);
2739
- return {
2740
- text,
2741
- heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
2742
- link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
2743
- supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
2744
- };
2745
- }
2746
- function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
2747
- const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
2748
- const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
2749
- if (role === "text") return `var(${roleVariable}, ${fallback})`;
2750
- return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
2751
- }
2752
- function surfaceForegroundValueWithOpacity(role, opacityPercent) {
2753
- if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
2754
- return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
2755
- }
2756
- function surfaceForegroundStyle(input) {
2757
- const foregrounds = completeSurfaceForegrounds(input);
2758
- return {
2759
- color: foregrounds.text,
2760
- [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
2761
- [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
2762
- [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
2763
- [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
2764
- };
2765
- }
2766
- //#endregion
2767
- //#region ../theme-core/src/utils/colorStyles.ts
2768
- const COLOR_VAR_PREFIX = "--tb-";
2769
- function parseToken(source) {
2770
- const parts = parseColorTokenParts(source);
2771
- if (parts.kind === "raw") return {
2772
- token: source,
2773
- raw: parts.value
2774
- };
2775
- if (parts.kind === "invalid") return { token: source };
2776
- return {
2777
- token: parts.token,
2778
- opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
2779
- };
2780
- }
2781
- const SURFACE_TEXT_ROLE_BY_TOKEN = {
2782
- text: "text",
2783
- heading: "heading",
2784
- mutedText: "supporting"
2785
- };
2786
- function surfaceRoleForTextToken(token) {
2787
- return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
2788
- }
2789
- /**
2790
- * Converts a theme color token to a CSS rgb value.
2791
- * This is the primary function for applying theme colors to CSS properties.
2792
- *
2793
- * @param token - Theme color token (semantic, with opacity, or raw)
2794
- * @returns CSS color value (rgb with optional alpha, or raw CSS value)
2795
- *
2796
- * @example
2797
- * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
2798
- * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
2799
- * rgbColorValue('raw:transparent') // => 'transparent'
2800
- */
2801
- function rgbColorValue(token) {
2802
- const { token: baseToken, opacityPercent, raw } = parseToken(token);
2803
- if (raw) return raw;
2804
- const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
2805
- if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
2806
- return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
2807
- }
2808
- /**
2809
- * Creates a backgroundColor style object from a theme token.
2810
- *
2811
- * @param token - Theme color token
2812
- * @returns CSSProperties with backgroundColor
2813
- */
2814
- function backgroundColorStyle(token) {
2815
- return { backgroundColor: rgbColorValue(token) };
2816
- }
2817
- /**
2818
- * Creates a text color style object from a theme token.
2819
- *
2820
- * @param token - Theme color token
2821
- * @returns CSSProperties with color
2822
- */
2823
- function textColorStyle(token) {
2824
- const parsed = parseToken(token);
2825
- const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
2826
- if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
2827
- return { color: rgbColorValue(token) };
2828
- }
2829
- /**
2830
- * Creates a border color style object from a theme token.
2831
- *
2832
- * @param token - Theme color token
2833
- * @returns CSSProperties with borderColor
2834
- */
2835
- function borderColorStyle(token) {
2836
- return { borderColor: rgbColorValue(token) };
2837
- }
2838
- /**
2839
- * Merges multiple CSSProperties objects into a single object.
2840
- * Later styles override earlier ones. Undefined values are filtered out.
2841
- * Returns undefined if the merged result is empty.
2842
- *
2843
- * @param styles - Variable number of CSSProperties objects (can be undefined)
2844
- * @returns Merged CSSProperties object, or undefined if empty
2845
- *
2846
- * @example
2847
- * mergeStyles(
2848
- * backgroundColorStyle('surface'),
2849
- * textColorStyle('text'),
2850
- * borderColorStyle('border')
2851
- * )
2852
- * // => { backgroundColor: '...', color: '...', borderColor: '...' }
2853
- */
2854
- function mergeStyles(...styles) {
2855
- const merged = {};
2856
- for (const style of styles) {
2857
- if (!style) continue;
2858
- Object.assign(merged, style);
2859
- }
2860
- return Object.keys(merged).length ? merged : void 0;
2861
- }
2862
- //#endregion
2863
2718
  //#region ../theme-core/src/lib/media.ts
2864
2719
  const hotspotSchema$1 = z.object({
2865
2720
  x: z.number().min(0).max(1),
@@ -2917,111 +2772,6 @@ const mediaSchema$1 = z.object({
2917
2772
  transform: transformSchema$1.optional()
2918
2773
  });
2919
2774
  //#endregion
2920
- //#region ../theme-core/src/buttons/effectClassification.ts
2921
- /**
2922
- * Exhaustive live policy for every registered effect id.
2923
- *
2924
- * The resolver consumes the transition payload directly, so adding an effect
2925
- * cannot update classification without also defining its runtime behavior.
2926
- */
2927
- const BUTTON_EFFECT_POLICIES = {
2928
- "darken-background": {
2929
- classification: "colour-transition",
2930
- transition: {
2931
- kind: "adjust-shade",
2932
- direction: "darker"
2933
- }
2934
- },
2935
- "lighten-background": {
2936
- classification: "colour-transition",
2937
- transition: {
2938
- kind: "adjust-shade",
2939
- direction: "lighter"
2940
- }
2941
- },
2942
- "background-on-hover": {
2943
- classification: "colour-transition",
2944
- transition: { kind: "replace-background" }
2945
- },
2946
- "background-on-hover-alpha": {
2947
- classification: "unsupported-colour",
2948
- reason: "alpha_requires_surface"
2949
- },
2950
- "fade-background": {
2951
- classification: "unsupported-colour",
2952
- reason: "changes_element_opacity"
2953
- },
2954
- "opacity-reduce": {
2955
- classification: "unsupported-colour",
2956
- reason: "changes_element_opacity"
2957
- },
2958
- "gradient-text": {
2959
- classification: "unsupported-colour",
2960
- reason: "changes_foreground"
2961
- },
2962
- "text-color-change": {
2963
- classification: "unsupported-colour",
2964
- reason: "changes_foreground"
2965
- },
2966
- "border-color-change": {
2967
- classification: "unsupported-colour",
2968
- reason: "changes_border"
2969
- },
2970
- "aurora-glow": {
2971
- classification: "unsupported-background-overlay",
2972
- reason: "unresolved_background_overlay"
2973
- },
2974
- "cosmic-stardust": {
2975
- classification: "unsupported-background-overlay",
2976
- reason: "unresolved_background_overlay"
2977
- },
2978
- "gradient-border-fill": {
2979
- classification: "unsupported-background-overlay",
2980
- reason: "unresolved_background_overlay"
2981
- },
2982
- "animated-gradient-border-fill": {
2983
- classification: "unsupported-background-overlay",
2984
- reason: "unresolved_background_overlay"
2985
- },
2986
- "nav-underline": {
2987
- classification: "not-button",
2988
- reason: "not_button_effect"
2989
- },
2990
- "nav-frosted-base": {
2991
- classification: "not-button",
2992
- reason: "not_button_effect"
2993
- },
2994
- "nav-frosted-hover": {
2995
- classification: "not-button",
2996
- reason: "not_button_effect"
2997
- },
2998
- "nav-frosted-active": {
2999
- classification: "not-button",
3000
- reason: "not_button_effect"
3001
- },
3002
- "animated-gradient": { classification: "non-colour" },
3003
- "border-glow": { classification: "non-colour" },
3004
- "drop-shadow": { classification: "non-colour" },
3005
- "inner-shadow": { classification: "non-colour" },
3006
- "neumorphic-shadow": { classification: "non-colour" },
3007
- "outer-glow": { classification: "non-colour" },
3008
- "retro-3d-shadow": { classification: "non-colour" },
3009
- "scale-up": { classification: "non-colour" },
3010
- "scale-down": { classification: "non-colour" },
3011
- lift: { classification: "non-colour" },
3012
- "press-down": { classification: "non-colour" },
3013
- pop: { classification: "non-colour" },
3014
- "border-bottom-grow": { classification: "non-colour" },
3015
- "sketchy-border": { classification: "non-colour" },
3016
- "pulse-animation": { classification: "non-colour" }
3017
- };
3018
- function getButtonEffectPolicy(effectId) {
3019
- return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
3020
- }
3021
- function classifyButtonEffectId(effectId) {
3022
- return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
3023
- }
3024
- //#endregion
3025
2775
  //#region ../theme-core/src/buttons/types.ts
3026
2776
  /**
3027
2777
  * Standard semantic variant roles a theme exposes.
@@ -3038,6 +2788,13 @@ const VARIANT_ROLES = [
3038
2788
  "tertiary",
3039
2789
  "accent"
3040
2790
  ];
2791
+ /**
2792
+ * Canonical persisted/CSS-safe button variant identifier.
2793
+ *
2794
+ * IDs become class selectors, so keep the contract deliberately narrower than
2795
+ * an arbitrary string and shared with every authoring/runtime boundary.
2796
+ */
2797
+ const buttonVariantIdSchema = z$1.string().min(1).max(50).regex(/^[a-z0-9-]+$/, "Button variant IDs may contain lowercase letters, numbers, and hyphens only");
3041
2798
  function parseOpaqueButtonColourToken(value) {
3042
2799
  const token = parseThemeColorToken(value);
3043
2800
  if (token === null) return null;
@@ -3052,6 +2809,109 @@ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => pa
3052
2809
  if (token === null) throw new Error("Invariant: opaque button token passed its schema");
3053
2810
  return token;
3054
2811
  });
2812
+ const buttonShadeAmountSchema = z$1.number().finite().min(0).max(900).multipleOf(100);
2813
+ const buttonShadowIntensitySchema = z$1.enum([
2814
+ "subtle",
2815
+ "medium",
2816
+ "strong"
2817
+ ]);
2818
+ /**
2819
+ * The complete persisted button-effect contract.
2820
+ *
2821
+ * Navigation has its own generic interactive-effect model. Button variants use
2822
+ * this closed union so unsupported effect IDs, unknown option keys, and unsafe
2823
+ * CSS values cannot become representable after the schema boundary.
2824
+ */
2825
+ const buttonEffectApplicationSchema = z$1.discriminatedUnion("effectId", [
2826
+ z$1.strictObject({
2827
+ effectId: z$1.literal("darken-background"),
2828
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
2829
+ }),
2830
+ z$1.strictObject({
2831
+ effectId: z$1.literal("lighten-background"),
2832
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
2833
+ }),
2834
+ z$1.strictObject({
2835
+ effectId: z$1.literal("background-on-hover"),
2836
+ options: z$1.strictObject({ colorToken: opaqueButtonColourTokenSchema.optional() }).optional()
2837
+ }),
2838
+ z$1.strictObject({
2839
+ effectId: z$1.literal("animated-gradient"),
2840
+ options: z$1.strictObject({ direction: z$1.enum(["horizontal", "vertical"]).optional() }).optional()
2841
+ }),
2842
+ z$1.strictObject({
2843
+ effectId: z$1.literal("drop-shadow"),
2844
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
2845
+ }),
2846
+ z$1.strictObject({
2847
+ effectId: z$1.literal("inner-shadow"),
2848
+ options: z$1.strictObject({}).optional()
2849
+ }),
2850
+ z$1.strictObject({
2851
+ effectId: z$1.literal("neumorphic-shadow"),
2852
+ options: z$1.strictObject({
2853
+ mode: z$1.enum(["raised", "pressed"]).optional(),
2854
+ intensity: buttonShadowIntensitySchema.optional()
2855
+ }).optional()
2856
+ }),
2857
+ z$1.strictObject({
2858
+ effectId: z$1.literal("retro-3d-shadow"),
2859
+ options: z$1.strictObject({}).optional()
2860
+ }),
2861
+ z$1.strictObject({
2862
+ effectId: z$1.literal("scale-up"),
2863
+ options: z$1.strictObject({ scale: z$1.enum([
2864
+ "1.02",
2865
+ "1.03",
2866
+ "1.05",
2867
+ "1.10"
2868
+ ]).optional() }).optional()
2869
+ }),
2870
+ z$1.strictObject({
2871
+ effectId: z$1.literal("scale-down"),
2872
+ options: z$1.strictObject({ scale: z$1.enum([
2873
+ "0.90",
2874
+ "0.95",
2875
+ "0.97",
2876
+ "0.98"
2877
+ ]).optional() }).optional()
2878
+ }),
2879
+ z$1.strictObject({
2880
+ effectId: z$1.literal("lift"),
2881
+ options: z$1.strictObject({ amount: z$1.enum([
2882
+ "0.25",
2883
+ "0.5",
2884
+ "1"
2885
+ ]).optional() }).optional()
2886
+ }),
2887
+ z$1.strictObject({
2888
+ effectId: z$1.literal("press-down"),
2889
+ options: z$1.strictObject({}).optional()
2890
+ }),
2891
+ z$1.strictObject({
2892
+ effectId: z$1.literal("pop"),
2893
+ options: z$1.strictObject({ intensity: z$1.enum([
2894
+ "subtle",
2895
+ "medium",
2896
+ "dramatic"
2897
+ ]).optional() }).optional()
2898
+ }),
2899
+ z$1.strictObject({
2900
+ effectId: z$1.literal("border-bottom-grow"),
2901
+ options: z$1.strictObject({
2902
+ height: z$1.union([z$1.literal(1), z$1.literal(2)]).optional(),
2903
+ origin: z$1.enum([
2904
+ "left",
2905
+ "center",
2906
+ "right"
2907
+ ]).optional()
2908
+ }).optional()
2909
+ }),
2910
+ z$1.strictObject({
2911
+ effectId: z$1.literal("sketchy-border"),
2912
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
2913
+ })
2914
+ ]);
3055
2915
  const cornerStyleSchema = z$1.enum([
3056
2916
  "square",
3057
2917
  "rounded",
@@ -3086,13 +2946,7 @@ const hoverTransformSchema = z$1.enum([
3086
2946
  "scale",
3087
2947
  "press"
3088
2948
  ]);
3089
- const hoverColorSchema = z$1.enum([
3090
- "none",
3091
- "brighten",
3092
- "darken",
3093
- "saturate",
3094
- "token"
3095
- ]);
2949
+ z$1.enum(["none", "token"]);
3096
2950
  const buttonPaddingPresetSchema = z$1.enum([
3097
2951
  "compact",
3098
2952
  "default",
@@ -3153,7 +3007,7 @@ const buttonSizesSchema = z$1.object({
3153
3007
  lg,
3154
3008
  xl: xl ?? lg
3155
3009
  }));
3156
- const canonicalButtonGlobalSettingsSchema = z$1.object({
3010
+ const buttonGlobalSettingsBaseSchema = z$1.strictObject({
3157
3011
  cornerStyle: cornerStyleSchema,
3158
3012
  cornerRadius: z$1.number().min(0).max(24).optional(),
3159
3013
  /**
@@ -3166,30 +3020,23 @@ const canonicalButtonGlobalSettingsSchema = z$1.object({
3166
3020
  shadow: shadowSizeSchema.optional(),
3167
3021
  textTransform: textTransformSchema,
3168
3022
  fontWeight: fontWeightSchema,
3169
- /**
3170
- * @deprecated Set per-variant via `variant.border.widthClass` instead.
3171
- * Schema field is preserved so existing presets parse; the value is unread
3172
- * by the current generator and will be removed once the legacy
3173
- * `primitives/content/components/Button.tsx` slot exports are deleted.
3174
- */
3175
- borderWidth: z$1.union([
3176
- z$1.literal(1),
3177
- z$1.literal(2),
3178
- z$1.literal(3)
3179
- ]),
3180
3023
  typography: buttonTypographySchema.optional(),
3181
3024
  fontSize: z$1.string().optional(),
3182
3025
  letterSpacing: letterSpacingSchema.optional(),
3183
3026
  italic: z$1.boolean().optional(),
3184
3027
  paddingPreset: buttonPaddingPresetSchema.optional(),
3185
3028
  hoverTransform: hoverTransformSchema,
3186
- hoverColor: hoverColorSchema,
3187
- hoverColorToken: opaqueButtonColourTokenSchema.optional(),
3188
3029
  gradientStyle: gradientStyleSchema.optional(),
3189
3030
  gradientAngle: z$1.number().min(0).max(360).optional(),
3190
3031
  gradientSharpness: gradientSharpnessSchema.optional()
3191
3032
  });
3192
- const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
3033
+ const buttonGlobalSettingsSchema = z$1.discriminatedUnion("hoverColor", [buttonGlobalSettingsBaseSchema.extend({
3034
+ hoverColor: z$1.literal("none"),
3035
+ hoverColorToken: z$1.never().optional()
3036
+ }), buttonGlobalSettingsBaseSchema.extend({
3037
+ hoverColor: z$1.literal("token"),
3038
+ hoverColorToken: opaqueButtonColourTokenSchema
3039
+ })]);
3193
3040
  const gradientDirectionSchema$1 = z$1.enum([
3194
3041
  "to-r",
3195
3042
  "to-l",
@@ -3200,12 +3047,12 @@ const gradientDirectionSchema$1 = z$1.enum([
3200
3047
  "to-br",
3201
3048
  "to-bl"
3202
3049
  ]);
3203
- const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
3204
- z$1.object({
3050
+ const buttonColourTreatmentSchema = z$1.discriminatedUnion("type", [
3051
+ z$1.strictObject({
3205
3052
  type: z$1.literal("solid"),
3206
3053
  colorToken: opaqueButtonColourTokenSchema
3207
3054
  }),
3208
- z$1.object({
3055
+ z$1.strictObject({
3209
3056
  type: z$1.literal("gradient"),
3210
3057
  stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
3211
3058
  const [first, second, ...rest] = stops;
@@ -3219,18 +3066,15 @@ const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
3219
3066
  direction: gradientDirectionSchema$1,
3220
3067
  foregroundToken: opaqueButtonColourTokenSchema
3221
3068
  }),
3222
- z$1.object({
3069
+ z$1.strictObject({
3223
3070
  type: z$1.literal("surface"),
3224
3071
  role: z$1.literal("link")
3225
3072
  })
3226
3073
  ]);
3227
- const effectApplicationSchema$1 = z$1.object({
3228
- effectId: z$1.string(),
3229
- options: z$1.record(z$1.string(), z$1.json()).optional()
3230
- });
3231
- const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
3232
- const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
3233
- const variantShadowSchema = z$1.object({
3074
+ const buttonBackgroundSchema = buttonColourTreatmentSchema;
3075
+ const buttonBorderSchema = z$1.strictObject({ widthClass: z$1.string() });
3076
+ const buttonVariantAffordanceSchema = z$1.strictObject({ iconId: z$1.string().min(1) });
3077
+ const variantShadowSchema = z$1.strictObject({
3234
3078
  elevation: z$1.enum([
3235
3079
  "none",
3236
3080
  "low",
@@ -3243,13 +3087,13 @@ const variantShadowSchema = z$1.object({
3243
3087
  "hard"
3244
3088
  ]).nullable(),
3245
3089
  position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
3246
- colorToken: themeColorTokenSchema.optional()
3090
+ colorToken: opaqueButtonColourTokenSchema.optional()
3247
3091
  });
3248
- const variantEffectsSchema = z$1.object({
3249
- base: z$1.array(effectApplicationSchema$1).optional(),
3250
- hover: z$1.array(effectApplicationSchema$1).optional(),
3251
- active: z$1.array(effectApplicationSchema$1).optional(),
3252
- focus: z$1.array(effectApplicationSchema$1).optional()
3092
+ const variantEffectsSchema = z$1.strictObject({
3093
+ base: z$1.array(buttonEffectApplicationSchema).optional(),
3094
+ hover: z$1.array(buttonEffectApplicationSchema).optional(),
3095
+ active: z$1.array(buttonEffectApplicationSchema).optional(),
3096
+ focus: z$1.array(buttonEffectApplicationSchema).optional()
3253
3097
  });
3254
3098
  /**
3255
3099
  * Per-variant size overrides schema — every entry is optional.
@@ -3270,8 +3114,8 @@ const variantSizeOverridesSchema = z$1.object({
3270
3114
  * default (e.g. link-style variants declaring zero horizontal padding at
3271
3115
  * every size).
3272
3116
  */
3273
- const canonicalButtonVariantSchema = z$1.object({
3274
- id: z$1.string(),
3117
+ const buttonVariantSchema = z$1.strictObject({
3118
+ id: buttonVariantIdSchema,
3275
3119
  name: z$1.string().min(1).max(20),
3276
3120
  enabled: z$1.boolean(),
3277
3121
  priority: prioritySchema,
@@ -3285,422 +3129,499 @@ const canonicalButtonVariantSchema = z$1.object({
3285
3129
  sizes: variantSizeOverridesSchema.optional(),
3286
3130
  affordance: buttonVariantAffordanceSchema.optional()
3287
3131
  });
3288
- /**
3289
- * Temporary persisted-theme admission boundary for issue #1206.
3290
- *
3291
- * Production inventory on 2026-07-22 found 22 rows using the old independent
3292
- * `textColorToken`/border colour shape. New source and all downstream code use
3293
- * only the canonical relationship. Delete this preprocessor after the reviewed
3294
- * production rewrite reports zero legacy rows.
3295
- */
3296
- const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
3297
- const buttonSystemSchema = z$1.object({
3132
+ const buttonSystemSchema = z$1.strictObject({
3298
3133
  global: buttonGlobalSettingsSchema,
3299
3134
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
3300
3135
  sizes: buttonSizesSchema.optional()
3136
+ }).superRefine((system, ctx) => {
3137
+ const seen = /* @__PURE__ */ new Set();
3138
+ system.variants.forEach((variant, index) => {
3139
+ if (seen.has(variant.id)) ctx.addIssue({
3140
+ code: "custom",
3141
+ path: [
3142
+ "variants",
3143
+ index,
3144
+ "id"
3145
+ ],
3146
+ message: `Duplicate button variant ID: ${variant.id}`
3147
+ });
3148
+ seen.add(variant.id);
3149
+ });
3301
3150
  });
3302
- function normalizeLegacyButtonVariantInput(input) {
3303
- if (!isUnknownRecord(input)) return input;
3304
- const background = input.background;
3305
- if (!isUnknownRecord(background)) return input;
3306
- 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;
3307
- const { textColorToken, ...withoutLegacyText } = input;
3308
- const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
3309
- const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
3310
- const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
3311
- const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
3312
- if (background.type === "transparent") return {
3313
- ...withoutLegacyText,
3314
- background: {
3315
- type: "surface",
3316
- role: "link"
3317
- },
3318
- border: normalizedBorder,
3319
- affordance: normalizedAffordance,
3320
- effects: normalizedEffects,
3321
- hoverBackgroundToken
3322
- };
3323
- if (background.type === "gradient") {
3324
- const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
3325
- return {
3326
- ...withoutLegacyText,
3327
- background: {
3328
- ...background,
3329
- stops,
3330
- foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
3331
- },
3332
- border: normalizedBorder,
3333
- affordance: normalizedAffordance,
3334
- effects: normalizedEffects,
3335
- hoverBackgroundToken
3336
- };
3337
- }
3151
+ //#endregion
3152
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
3153
+ const SURFACE_FOREGROUND_CSS_VARS = {
3154
+ text: "--rb-surface-text-color",
3155
+ heading: "--rb-surface-heading-color",
3156
+ link: "--rb-surface-link-color",
3157
+ supporting: "--rb-surface-supporting-color"
3158
+ };
3159
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
3160
+ const DEFAULT_ROLE_FALLBACKS = {
3161
+ text: DEFAULT_SURFACE_TEXT_COLOR,
3162
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
3163
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
3164
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
3165
+ };
3166
+ function requireSurfaceColorValue(role, value) {
3167
+ const normalized = value.trim();
3168
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
3169
+ return normalized;
3170
+ }
3171
+ function completeSurfaceForegrounds(input) {
3172
+ const text = requireSurfaceColorValue("text", input.text);
3338
3173
  return {
3339
- ...withoutLegacyText,
3340
- background: background.type === "solid" ? {
3341
- ...background,
3342
- colorToken: stripLegacyTokenAlpha(background.colorToken)
3343
- } : background,
3344
- border: normalizedBorder,
3345
- affordance: normalizedAffordance,
3346
- effects: normalizedEffects,
3347
- hoverBackgroundToken
3174
+ text,
3175
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
3176
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
3177
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
3348
3178
  };
3349
3179
  }
3350
- function normalizeLegacyButtonGlobalInput(input) {
3351
- if (!isUnknownRecord(input)) return input;
3352
- const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
3353
- return hoverColorToken === input.hoverColorToken ? input : {
3354
- ...input,
3355
- hoverColorToken
3356
- };
3180
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
3181
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
3182
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
3183
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
3184
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
3357
3185
  }
3358
- function hasLegacyAlphaToken(input, background) {
3359
- const stops = isUnknownArray(background.stops) ? background.stops : [];
3360
- return [
3361
- input.hoverBackgroundToken,
3362
- background.colorToken,
3363
- background.foregroundToken,
3364
- ...stops
3365
- ].some((token) => stripLegacyTokenAlpha(token) !== token);
3366
- }
3367
- function stripLegacyTokenAlpha(value) {
3368
- if (typeof value !== "string") return value;
3369
- const slashIndex = value.indexOf("/");
3370
- return slashIndex < 0 ? value : value.slice(0, slashIndex);
3371
- }
3372
- function hasLegacyButtonEffects(input) {
3373
- if (!isUnknownRecord(input)) return false;
3374
- return [
3375
- "base",
3376
- "hover",
3377
- "active",
3378
- "focus"
3379
- ].some((state) => {
3380
- const effects = input[state];
3381
- return Array.isArray(effects) && effects.some((effect) => {
3382
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
3383
- const classification = classifyButtonEffectId(effect.effectId);
3384
- if (classification !== "colour-transition" && classification !== "non-colour") return true;
3385
- return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
3386
- });
3387
- });
3186
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
3187
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
3188
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
3388
3189
  }
3389
- function normalizeLegacyButtonEffects(input, backgroundType) {
3390
- if (!isUnknownRecord(input)) return input;
3391
- const normalized = {};
3392
- for (const state of [
3393
- "base",
3394
- "hover",
3395
- "active",
3396
- "focus"
3397
- ]) {
3398
- const effects = input[state];
3399
- if (!Array.isArray(effects)) continue;
3400
- const safeEffects = effects.flatMap((effect) => {
3401
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
3402
- const classification = classifyButtonEffectId(effect.effectId);
3403
- if (classification === "non-colour") return [effect];
3404
- if (classification !== "colour-transition") return [];
3405
- if (effect.effectId === "background-on-hover") {
3406
- const options = isUnknownRecord(effect.options) ? effect.options : null;
3407
- const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
3408
- return colorToken === null ? [] : [{
3409
- ...effect,
3410
- options: {
3411
- ...options,
3412
- colorToken
3413
- }
3414
- }];
3415
- }
3416
- if (backgroundType !== "solid") return [];
3417
- const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
3418
- const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
3419
- return [{
3420
- ...effect,
3421
- options: {
3422
- ...isUnknownRecord(effect.options) ? effect.options : {},
3423
- amount
3424
- }
3425
- }];
3426
- });
3427
- if (safeEffects.length > 0) normalized[state] = safeEffects;
3428
- }
3429
- return Object.keys(normalized).length === 0 ? void 0 : normalized;
3430
- }
3431
- function isUnknownRecord(value) {
3432
- return typeof value === "object" && value !== null && !Array.isArray(value);
3433
- }
3434
- function isUnknownArray(value) {
3435
- return Array.isArray(value);
3190
+ function surfaceForegroundStyle(input) {
3191
+ const foregrounds = completeSurfaceForegrounds(input);
3192
+ return {
3193
+ color: foregrounds.text,
3194
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
3195
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
3196
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
3197
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
3198
+ };
3436
3199
  }
3437
3200
  buttonSizeNameSchema.options;
3438
3201
  //#endregion
3439
- //#region ../theme-core/src/buttons/personalities/types.ts
3440
- function asButtonPersonalityId(value) {
3441
- if (value.length === 0) throw new Error("ButtonPersonalityId must be a non-empty string");
3442
- return value;
3202
+ //#region ../theme-core/src/buttons/styles/domain.ts
3203
+ const PUBLIC_BUTTON_ROLES = [
3204
+ "primary",
3205
+ "secondary",
3206
+ "outline",
3207
+ "ghost",
3208
+ "accent"
3209
+ ];
3210
+ function defineButtonStyleRecipe(recipe) {
3211
+ return recipe;
3212
+ }
3213
+ function materializeButtonRoles(definitions) {
3214
+ const primary = materializeCompleteRole("primary", definitions.primary.appearance);
3215
+ const secondary = materializeCompleteRole("secondary", definitions.secondary.appearance);
3216
+ const completeRoles = {
3217
+ primary,
3218
+ secondary
3219
+ };
3220
+ return {
3221
+ primary,
3222
+ secondary,
3223
+ outline: materializeSupportingRole("outline", definitions.outline, completeRoles),
3224
+ ghost: materializeSupportingRole("ghost", definitions.ghost, completeRoles),
3225
+ accent: materializeSupportingRole("accent", definitions.accent, completeRoles)
3226
+ };
3443
3227
  }
3444
- const buttonPersonalityIdSchema = z$1.string().min(1).transform((value) => asButtonPersonalityId(value));
3445
- z$1.object({
3446
- id: buttonPersonalityIdSchema,
3447
- name: z$1.string().min(1),
3448
- description: z$1.string().min(1),
3449
- tags: styleTagsSchema,
3450
- buttonSystem: buttonSystemSchema
3451
- });
3452
- /**
3453
- * Helper for personality file modules to keep the call site short and
3454
- * type-safe. `as const satisfies ...` would also work but produces wider
3455
- * types at use sites.
3456
- */
3457
- function defineButtonPersonality(input) {
3228
+ function resolveButtonStyleRecipe(recipe, parameters) {
3229
+ const parsedParameters = recipe.parameterSchema.safeParse(parameters);
3230
+ if (!parsedParameters.success) return {
3231
+ kind: "error",
3232
+ error: {
3233
+ kind: "invalid-parameters",
3234
+ styleId: recipe.id,
3235
+ issues: parsedParameters.error.issues.map((issue) => issue.message)
3236
+ }
3237
+ };
3238
+ const definition = recipe.defineSystem(parsedParameters.data);
3239
+ const roles = materializeButtonRoles(definition.roles);
3240
+ const buttonSystem = {
3241
+ global: definition.global,
3242
+ variants: PUBLIC_BUTTON_ROLES.map((role) => roles[role]),
3243
+ ...definition.sizes ? { sizes: definition.sizes } : {}
3244
+ };
3245
+ const parsedButtonSystem = buttonSystemSchema.safeParse(buttonSystem);
3246
+ if (!parsedButtonSystem.success) return {
3247
+ kind: "error",
3248
+ error: {
3249
+ kind: "invalid-button-system",
3250
+ styleId: recipe.id,
3251
+ issues: parsedButtonSystem.error.issues.map((issue) => issue.message)
3252
+ }
3253
+ };
3458
3254
  return {
3459
- id: asButtonPersonalityId(input.id),
3460
- name: input.name,
3461
- description: input.description,
3462
- tags: input.tags,
3463
- buttonSystem: buttonSystemSchema.parse(input.buttonSystem)
3255
+ kind: "resolved",
3256
+ buttonSystem: parsedButtonSystem.data
3257
+ };
3258
+ }
3259
+ function materializeSupportingRole(role, definition, completeRoles) {
3260
+ if (definition.kind === "complete") return materializeCompleteRole(role, definition.appearance);
3261
+ return {
3262
+ ...completeRoles[definition.from],
3263
+ ...definition.overrides,
3264
+ id: role,
3265
+ name: roleName(role),
3266
+ enabled: true,
3267
+ priority: 3
3268
+ };
3269
+ }
3270
+ function materializeCompleteRole(role, appearance) {
3271
+ return {
3272
+ ...appearance,
3273
+ id: role,
3274
+ name: roleName(role),
3275
+ enabled: true,
3276
+ priority: rolePriority(role)
3464
3277
  };
3465
3278
  }
3279
+ function roleName(role) {
3280
+ return role.charAt(0).toUpperCase() + role.slice(1);
3281
+ }
3282
+ function rolePriority(role) {
3283
+ if (role === "primary") return 1;
3284
+ if (role === "secondary") return 2;
3285
+ return 3;
3286
+ }
3466
3287
  //#endregion
3467
- //#region ../theme-core/src/buttons/personalities/brushed-wash.ts
3468
- /**
3469
- * Brushed Wash — gradient backgrounds that read as watercolour washes.
3470
- * Generous padding, soft shadows; pairs with vibrant organic brands.
3471
- */
3472
- const brushedWash = defineButtonPersonality({
3473
- id: "brushed-wash",
3474
- name: "Brushed Wash",
3475
- description: "Watercolour-gradient buttons with soft shadows and generous padding.",
3288
+ //#region ../theme-core/src/buttons/styles/selection.ts
3289
+ const BUTTON_STYLE_IDS = [
3290
+ "soft-pill",
3291
+ "earth-pill",
3292
+ "confident-chip",
3293
+ "editorial-link",
3294
+ "pebble",
3295
+ "ink-stamp",
3296
+ "flat-ink",
3297
+ "brushed-wash",
3298
+ "showtime-pill",
3299
+ "joy-stamp",
3300
+ "ornate-ritual",
3301
+ "quiet-arrow"
3302
+ ];
3303
+ const quietArrowParametersSchema = z$1.object({ secondaryTreatment: z$1.enum(["outlined", "solid-accent"]) }).strict();
3304
+ const emptyParametersSchema$1 = z$1.object({}).strict();
3305
+ function selectionBranch(styleId, parameters) {
3306
+ return z$1.object({
3307
+ styleId: z$1.literal(styleId),
3308
+ parameters
3309
+ }).strict();
3310
+ }
3311
+ const buttonStyleSelectionSchema = z$1.discriminatedUnion("styleId", [
3312
+ selectionBranch("soft-pill", emptyParametersSchema$1),
3313
+ selectionBranch("earth-pill", emptyParametersSchema$1),
3314
+ selectionBranch("confident-chip", emptyParametersSchema$1),
3315
+ selectionBranch("editorial-link", emptyParametersSchema$1),
3316
+ selectionBranch("pebble", emptyParametersSchema$1),
3317
+ selectionBranch("ink-stamp", emptyParametersSchema$1),
3318
+ selectionBranch("flat-ink", emptyParametersSchema$1),
3319
+ selectionBranch("brushed-wash", emptyParametersSchema$1),
3320
+ selectionBranch("showtime-pill", emptyParametersSchema$1),
3321
+ selectionBranch("joy-stamp", emptyParametersSchema$1),
3322
+ selectionBranch("ornate-ritual", emptyParametersSchema$1),
3323
+ selectionBranch("quiet-arrow", quietArrowParametersSchema)
3324
+ ]);
3325
+ //#endregion
3326
+ //#region ../theme-core/src/buttons/styles/recipes/quietArrow.ts
3327
+ const quietArrowRecipe = defineButtonStyleRecipe({
3328
+ id: "quiet-arrow",
3329
+ accessibilityLabel: "Wide uppercase buttons with trailing arrows",
3476
3330
  tags: {
3477
3331
  tone: "classic",
3478
- energy: "vibrant",
3479
- motion: "standard",
3480
- era: "organic"
3332
+ energy: "calm",
3333
+ motion: "subtle",
3334
+ era: "editorial"
3481
3335
  },
3482
- buttonSystem: {
3336
+ parameterSchema: quietArrowParametersSchema,
3337
+ defaultParameters: { secondaryTreatment: "outlined" },
3338
+ controls: [{
3339
+ kind: "choice",
3340
+ parameter: "secondaryTreatment",
3341
+ label: "Secondary button treatment",
3342
+ options: [{
3343
+ value: "outlined",
3344
+ label: "Outlined"
3345
+ }, {
3346
+ value: "solid-accent",
3347
+ label: "Solid accent"
3348
+ }]
3349
+ }],
3350
+ defineSystem: (parameters) => ({
3483
3351
  global: {
3484
3352
  cornerStyle: "rounded",
3485
- shadow: "low",
3486
- textTransform: "none",
3353
+ textTransform: "uppercase",
3487
3354
  fontWeight: 500,
3488
- borderWidth: 1,
3355
+ typography: "body",
3356
+ fontSize: "text-sm",
3357
+ letterSpacing: "loose",
3489
3358
  hoverTransform: "none",
3490
3359
  hoverColor: "none"
3491
3360
  },
3492
3361
  sizes: {
3493
3362
  sm: {
3494
- padding: "0.75rem 2rem",
3363
+ padding: "0.625rem 1.25rem",
3364
+ fontSize: "text-xs"
3365
+ },
3366
+ md: {
3367
+ padding: "0.8125rem 1.75rem",
3495
3368
  fontSize: "text-sm"
3496
3369
  },
3497
- md: { padding: "1rem 2.5rem" },
3498
3370
  lg: {
3499
- padding: "1.25rem 3rem",
3500
- fontSize: "text-lg"
3371
+ padding: "0.9375rem 2.125rem",
3372
+ fontSize: "text-sm"
3501
3373
  },
3502
3374
  xl: {
3503
- padding: "1.625rem 3.75rem",
3504
- fontSize: "text-xl"
3375
+ padding: "1.0625rem 2.5rem",
3376
+ fontSize: "text-base"
3505
3377
  }
3506
3378
  },
3507
- variants: [
3508
- {
3509
- id: "primary",
3510
- name: "Primary",
3511
- enabled: true,
3512
- priority: 1,
3513
- background: {
3514
- type: "gradient",
3515
- stops: ["primary", "accent"],
3516
- direction: "to-r",
3517
- foregroundToken: "primaryForeground"
3518
- },
3519
- borderRadius: "rounded-lg",
3520
- shadow: {
3521
- elevation: "low",
3522
- softness: "soft",
3523
- position: "bottom"
3379
+ roles: {
3380
+ primary: {
3381
+ kind: "complete",
3382
+ appearance: {
3383
+ background: {
3384
+ type: "solid",
3385
+ colorToken: unsafeAsOpaqueButtonColourToken("primary")
3386
+ },
3387
+ borderRadius: "rounded-xl",
3388
+ affordance: { iconId: "ArrowRight" },
3389
+ effects: { hover: [{
3390
+ effectId: "darken-background",
3391
+ options: { amount: 100 }
3392
+ }] }
3524
3393
  }
3525
3394
  },
3526
- {
3527
- id: "secondary",
3528
- name: "Secondary",
3529
- enabled: true,
3530
- priority: 2,
3531
- background: {
3532
- type: "gradient",
3533
- stops: ["secondary", "accent"],
3534
- direction: "to-br",
3535
- foregroundToken: "primaryForeground"
3536
- },
3537
- borderRadius: "rounded-lg",
3538
- shadow: {
3539
- elevation: "low",
3540
- softness: "soft",
3541
- position: "bottom"
3395
+ secondary: {
3396
+ kind: "complete",
3397
+ appearance: parameters.secondaryTreatment === "solid-accent" ? {
3398
+ background: {
3399
+ type: "solid",
3400
+ colorToken: unsafeAsOpaqueButtonColourToken("accent")
3401
+ },
3402
+ borderRadius: "rounded-xl",
3403
+ affordance: { iconId: "ArrowRight" },
3404
+ effects: { hover: [{
3405
+ effectId: "darken-background",
3406
+ options: { amount: 100 }
3407
+ }] }
3408
+ } : {
3409
+ background: {
3410
+ type: "surface",
3411
+ role: "link"
3412
+ },
3413
+ borderRadius: "rounded-xl",
3414
+ border: { widthClass: "border" },
3415
+ affordance: { iconId: "ArrowRight" },
3416
+ effects: { hover: [{
3417
+ effectId: "background-on-hover",
3418
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
3419
+ }] }
3542
3420
  }
3543
3421
  },
3544
- {
3545
- id: "outline",
3546
- name: "Outline",
3547
- enabled: true,
3548
- priority: 3,
3549
- background: {
3550
- type: "surface",
3551
- role: "link"
3552
- },
3553
- borderRadius: "rounded-lg",
3554
- border: { widthClass: "border" },
3555
- effects: { hover: [{
3556
- effectId: "background-on-hover",
3557
- options: { colorToken: "surface" }
3558
- }] }
3422
+ outline: {
3423
+ kind: "complete",
3424
+ appearance: {
3425
+ background: {
3426
+ type: "surface",
3427
+ role: "link"
3428
+ },
3429
+ borderRadius: "rounded-xl",
3430
+ border: { widthClass: "border" }
3431
+ }
3559
3432
  },
3560
- {
3561
- id: "ghost",
3562
- name: "Ghost",
3563
- enabled: true,
3564
- priority: 3,
3565
- background: {
3566
- type: "surface",
3567
- role: "link"
3568
- },
3569
- borderRadius: "rounded-lg",
3570
- effects: { hover: [{
3571
- effectId: "background-on-hover",
3572
- options: { colorToken: "surface" }
3573
- }] }
3433
+ ghost: {
3434
+ kind: "complete",
3435
+ appearance: {
3436
+ background: {
3437
+ type: "surface",
3438
+ role: "link"
3439
+ },
3440
+ borderRadius: "rounded-xl"
3441
+ }
3442
+ },
3443
+ accent: {
3444
+ kind: "derive",
3445
+ from: "primary",
3446
+ overrides: { background: {
3447
+ type: "solid",
3448
+ colorToken: unsafeAsOpaqueButtonColourToken("accent")
3449
+ } }
3574
3450
  }
3575
- ]
3576
- }
3451
+ }
3452
+ })
3577
3453
  });
3578
3454
  //#endregion
3579
- //#region ../theme-core/src/buttons/personalities/confident-chip.ts
3580
- /**
3581
- * Confident Chip — moderate-radius solid buttons that read as competent and
3582
- * direct. Default for Clear Professional; works for any modern brand voice.
3583
- */
3584
- const confidentChip = defineButtonPersonality({
3585
- id: "confident-chip",
3586
- name: "Confident Chip",
3587
- description: "Mid-radius solid buttons with a crisp hover.",
3455
+ //#region ../theme-core/src/buttons/styles/recipes/defineStaticButtonStyleRecipe.ts
3456
+ const emptyParametersSchema = z$1.object({}).strict();
3457
+ function defineStaticButtonStyleRecipe(input) {
3458
+ const system = normalizeSystem(input.system);
3459
+ return defineButtonStyleRecipe({
3460
+ id: input.id,
3461
+ accessibilityLabel: input.accessibilityLabel,
3462
+ tags: input.tags,
3463
+ parameterSchema: emptyParametersSchema,
3464
+ defaultParameters: {},
3465
+ controls: [],
3466
+ defineSystem: () => system
3467
+ });
3468
+ }
3469
+ function normalizeSystem(input) {
3470
+ return {
3471
+ global: buttonGlobalSettingsSchema.parse(input.global),
3472
+ ...input.sizes ? { sizes: input.sizes } : {},
3473
+ roles: {
3474
+ primary: normalizeCompleteRole(input.roles.primary),
3475
+ secondary: normalizeCompleteRole(input.roles.secondary),
3476
+ outline: normalizeSupportingRole(input.roles.outline),
3477
+ ghost: normalizeSupportingRole(input.roles.ghost),
3478
+ accent: normalizeSupportingRole(input.roles.accent)
3479
+ }
3480
+ };
3481
+ }
3482
+ function normalizeCompleteRole(role) {
3483
+ return {
3484
+ kind: "complete",
3485
+ appearance: normalizeAppearance(role.appearance)
3486
+ };
3487
+ }
3488
+ function normalizeSupportingRole(role) {
3489
+ if (role.kind === "complete") return normalizeCompleteRole(role);
3490
+ return {
3491
+ kind: "derive",
3492
+ from: role.from,
3493
+ ...role.overrides ? { overrides: normalizeAppearanceOverrides(role.overrides) } : {}
3494
+ };
3495
+ }
3496
+ function normalizeAppearance(appearance) {
3497
+ const { background, hoverBackgroundToken, ...rest } = appearance;
3498
+ return {
3499
+ ...rest,
3500
+ background: buttonColourTreatmentSchema.parse(background),
3501
+ ...hoverBackgroundToken ? { hoverBackgroundToken: opaqueButtonColourTokenSchema.parse(hoverBackgroundToken) } : {}
3502
+ };
3503
+ }
3504
+ function normalizeAppearanceOverrides(overrides) {
3505
+ const { background, hoverBackgroundToken, ...rest } = overrides;
3506
+ return {
3507
+ ...rest,
3508
+ ...background ? { background: buttonColourTreatmentSchema.parse(background) } : {},
3509
+ ...hoverBackgroundToken ? { hoverBackgroundToken: opaqueButtonColourTokenSchema.parse(hoverBackgroundToken) } : {}
3510
+ };
3511
+ }
3512
+ //#endregion
3513
+ //#region ../theme-core/src/buttons/styles/recipes/staticRecipes.ts
3514
+ const softPillRecipe = defineStaticButtonStyleRecipe({
3515
+ id: "soft-pill",
3516
+ accessibilityLabel: "Rounded pill buttons with a gentle hover lift",
3588
3517
  tags: {
3589
3518
  tone: "classic",
3590
3519
  energy: "calm",
3591
- motion: "standard",
3592
- era: "modern"
3520
+ motion: "subtle",
3521
+ era: "organic"
3593
3522
  },
3594
- buttonSystem: {
3523
+ system: {
3595
3524
  global: {
3596
- cornerStyle: "rounded",
3597
- shadow: "medium",
3525
+ cornerStyle: "pill",
3526
+ shadow: "low",
3598
3527
  textTransform: "none",
3599
- fontWeight: 600,
3600
- borderWidth: 1,
3601
- hoverTransform: "none",
3528
+ fontWeight: 500,
3529
+ hoverTransform: "lift",
3602
3530
  hoverColor: "none"
3603
3531
  },
3604
- variants: [
3605
- {
3606
- id: "primary",
3607
- name: "Primary",
3608
- enabled: true,
3609
- priority: 1,
3610
- background: {
3611
- type: "solid",
3612
- colorToken: "primary"
3613
- },
3614
- borderRadius: "rounded-md",
3615
- shadow: {
3616
- elevation: "medium",
3617
- softness: "soft",
3618
- position: "bottom"
3619
- },
3620
- effects: {
3621
- hover: [{
3622
- effectId: "darken-background",
3623
- options: { amount: 100 }
3624
- }],
3625
- active: [{
3626
- effectId: "scale-down",
3627
- options: { scale: "0.97" }
3628
- }]
3532
+ roles: {
3533
+ primary: {
3534
+ kind: "complete",
3535
+ appearance: {
3536
+ background: {
3537
+ type: "solid",
3538
+ colorToken: "primary"
3539
+ },
3540
+ borderRadius: "rounded-full",
3541
+ shadow: {
3542
+ elevation: "low",
3543
+ softness: "soft",
3544
+ position: "bottom"
3545
+ },
3546
+ effects: {
3547
+ hover: [{ effectId: "lift" }],
3548
+ active: [{
3549
+ effectId: "scale-down",
3550
+ options: { scale: "0.98" }
3551
+ }]
3552
+ }
3629
3553
  }
3630
3554
  },
3631
- {
3632
- id: "secondary",
3633
- name: "Secondary",
3634
- enabled: true,
3635
- priority: 2,
3636
- background: {
3637
- type: "solid",
3638
- colorToken: "surface"
3639
- },
3640
- borderRadius: "rounded-md",
3641
- border: { widthClass: "border" },
3642
- effects: { hover: [{
3643
- effectId: "background-on-hover",
3644
- options: { colorToken: "background" }
3645
- }] }
3555
+ secondary: {
3556
+ kind: "complete",
3557
+ appearance: {
3558
+ background: {
3559
+ type: "surface",
3560
+ role: "link"
3561
+ },
3562
+ borderRadius: "rounded-full",
3563
+ border: { widthClass: "border-2" },
3564
+ effects: { hover: [{
3565
+ effectId: "background-on-hover",
3566
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
3567
+ }] }
3568
+ }
3646
3569
  },
3647
- {
3648
- id: "outline",
3649
- name: "Outline",
3650
- enabled: true,
3651
- priority: 3,
3652
- background: {
3653
- type: "surface",
3654
- role: "link"
3655
- },
3656
- borderRadius: "rounded-md",
3657
- border: { widthClass: "border" },
3658
- effects: { hover: [{
3659
- effectId: "background-on-hover",
3660
- options: { colorToken: "surface" }
3661
- }] }
3570
+ outline: {
3571
+ kind: "complete",
3572
+ appearance: {
3573
+ background: {
3574
+ type: "surface",
3575
+ role: "link"
3576
+ },
3577
+ borderRadius: "rounded-full",
3578
+ border: { widthClass: "border" },
3579
+ effects: { hover: [{
3580
+ effectId: "background-on-hover",
3581
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
3582
+ }] }
3583
+ }
3662
3584
  },
3663
- {
3664
- id: "ghost",
3665
- name: "Ghost",
3666
- enabled: true,
3667
- priority: 3,
3668
- background: {
3669
- type: "surface",
3670
- role: "link"
3671
- },
3672
- borderRadius: "rounded-md",
3673
- effects: { hover: [{
3674
- effectId: "background-on-hover",
3675
- options: { colorToken: "surface" }
3676
- }] }
3585
+ ghost: {
3586
+ kind: "complete",
3587
+ appearance: {
3588
+ background: {
3589
+ type: "surface",
3590
+ role: "link"
3591
+ },
3592
+ borderRadius: "rounded-full",
3593
+ effects: { hover: [{
3594
+ effectId: "background-on-hover",
3595
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
3596
+ }] }
3597
+ }
3598
+ },
3599
+ accent: {
3600
+ kind: "derive",
3601
+ from: "primary",
3602
+ overrides: { background: {
3603
+ type: "solid",
3604
+ colorToken: "accent"
3605
+ } }
3677
3606
  }
3678
- ]
3607
+ }
3679
3608
  }
3680
3609
  });
3681
- //#endregion
3682
- //#region ../theme-core/src/buttons/personalities/earth-pill.ts
3683
- /**
3684
- * Earth Pill - warm studio pills with compact, primary-coloured ghost CTAs.
3685
- * Built for grounded editorial wellness styles such as Soft Earth.
3686
- */
3687
- const earthPill = defineButtonPersonality({
3610
+ const earthPillRecipe = defineStaticButtonStyleRecipe({
3688
3611
  id: "earth-pill",
3689
- name: "Earth Pill",
3690
- description: "Compact terracotta pills with quiet primary-coloured ghost CTAs.",
3612
+ accessibilityLabel: "Compact rounded buttons with a quiet hover lift",
3691
3613
  tags: {
3692
3614
  tone: "classic",
3693
3615
  energy: "calm",
3694
3616
  motion: "subtle",
3695
3617
  era: "organic"
3696
3618
  },
3697
- buttonSystem: {
3619
+ system: {
3698
3620
  global: {
3699
3621
  cornerStyle: "pill",
3700
3622
  shadow: "low",
3701
3623
  textTransform: "none",
3702
3624
  fontWeight: 500,
3703
- borderWidth: 1,
3704
3625
  hoverTransform: "lift",
3705
3626
  hoverColor: "none",
3706
3627
  typography: "body",
@@ -3721,948 +3642,786 @@ const earthPill = defineButtonPersonality({
3721
3642
  fontSize: "text-xl"
3722
3643
  }
3723
3644
  },
3724
- variants: [
3725
- {
3726
- id: "primary",
3727
- name: "Primary",
3728
- enabled: true,
3729
- priority: 1,
3730
- background: {
3731
- type: "solid",
3732
- colorToken: "primary"
3733
- },
3734
- borderRadius: "rounded-full",
3735
- shadow: {
3736
- elevation: "low",
3737
- softness: "soft",
3738
- position: "bottom"
3739
- },
3740
- effects: {
3741
- hover: [{
3742
- effectId: "lift",
3743
- options: { translateY: "-1px" }
3744
- }, {
3745
- effectId: "background-on-hover",
3746
- options: { colorToken: "accent" }
3747
- }],
3748
- active: [{
3749
- effectId: "scale-down",
3750
- options: { scale: "0.98" }
3751
- }]
3645
+ roles: {
3646
+ primary: {
3647
+ kind: "complete",
3648
+ appearance: {
3649
+ background: {
3650
+ type: "solid",
3651
+ colorToken: "primary"
3652
+ },
3653
+ borderRadius: "rounded-full",
3654
+ shadow: {
3655
+ elevation: "low",
3656
+ softness: "soft",
3657
+ position: "bottom"
3658
+ },
3659
+ effects: {
3660
+ hover: [{ effectId: "lift" }, {
3661
+ effectId: "background-on-hover",
3662
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
3663
+ }],
3664
+ active: [{
3665
+ effectId: "scale-down",
3666
+ options: { scale: "0.98" }
3667
+ }]
3668
+ }
3752
3669
  }
3753
3670
  },
3754
- {
3755
- id: "secondary",
3756
- name: "Secondary",
3757
- enabled: true,
3758
- priority: 2,
3759
- background: {
3760
- type: "surface",
3761
- role: "link"
3762
- },
3763
- borderRadius: "rounded-full",
3764
- border: { widthClass: "border" },
3765
- effects: { hover: [{
3766
- effectId: "background-on-hover",
3767
- options: { colorToken: "muted" }
3768
- }] }
3671
+ secondary: {
3672
+ kind: "complete",
3673
+ appearance: {
3674
+ background: {
3675
+ type: "surface",
3676
+ role: "link"
3677
+ },
3678
+ borderRadius: "rounded-full",
3679
+ border: { widthClass: "border" },
3680
+ effects: { hover: [{
3681
+ effectId: "background-on-hover",
3682
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
3683
+ }] }
3684
+ }
3769
3685
  },
3770
- {
3771
- id: "outline",
3772
- name: "Outline",
3773
- enabled: true,
3774
- priority: 3,
3775
- background: {
3776
- type: "surface",
3777
- role: "link"
3778
- },
3779
- borderRadius: "rounded-full",
3780
- border: { widthClass: "border" },
3781
- effects: { hover: [{
3782
- effectId: "background-on-hover",
3783
- options: { colorToken: "surfaceAlt" }
3784
- }] }
3686
+ outline: {
3687
+ kind: "complete",
3688
+ appearance: {
3689
+ background: {
3690
+ type: "surface",
3691
+ role: "link"
3692
+ },
3693
+ borderRadius: "rounded-full",
3694
+ border: { widthClass: "border" },
3695
+ effects: { hover: [{
3696
+ effectId: "background-on-hover",
3697
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
3698
+ }] }
3699
+ }
3785
3700
  },
3786
- {
3787
- id: "ghost",
3788
- name: "Ghost",
3789
- enabled: true,
3790
- priority: 3,
3791
- background: {
3792
- type: "surface",
3793
- role: "link"
3794
- },
3795
- borderRadius: "rounded-full",
3796
- shadow: {
3797
- elevation: "none",
3798
- softness: null,
3799
- position: "bottom"
3800
- },
3801
- sizes: {
3802
- sm: {
3803
- padding: "0.25rem 0",
3804
- fontSize: "text-sm"
3701
+ ghost: {
3702
+ kind: "complete",
3703
+ appearance: {
3704
+ background: {
3705
+ type: "surface",
3706
+ role: "link"
3805
3707
  },
3806
- md: { padding: "0.375rem 0" },
3807
- lg: {
3808
- padding: "0.5rem 0",
3809
- fontSize: "text-lg"
3708
+ borderRadius: "rounded-full",
3709
+ shadow: {
3710
+ elevation: "none",
3711
+ softness: null,
3712
+ position: "bottom"
3810
3713
  },
3811
- xl: {
3812
- padding: "0.625rem 0",
3813
- fontSize: "text-xl"
3714
+ sizes: {
3715
+ sm: {
3716
+ padding: "0.25rem 0",
3717
+ fontSize: "text-sm"
3718
+ },
3719
+ md: { padding: "0.375rem 0" },
3720
+ lg: {
3721
+ padding: "0.5rem 0",
3722
+ fontSize: "text-lg"
3723
+ },
3724
+ xl: {
3725
+ padding: "0.625rem 0",
3726
+ fontSize: "text-xl"
3727
+ }
3814
3728
  }
3815
3729
  }
3730
+ },
3731
+ accent: {
3732
+ kind: "derive",
3733
+ from: "primary",
3734
+ overrides: { background: {
3735
+ type: "solid",
3736
+ colorToken: "accent"
3737
+ } }
3816
3738
  }
3817
- ]
3739
+ }
3818
3740
  }
3819
3741
  });
3820
- //#endregion
3821
- //#region ../theme-core/src/buttons/personalities/editorial-link.ts
3822
- /**
3823
- * Editorial Link — all variants are link-style with a growing underline.
3824
- * Default for Warm Editorial and Quiet Luxury; reads as refined and quiet.
3825
- */
3826
- const editorialLink = defineButtonPersonality({
3827
- id: "editorial-link",
3828
- name: "Editorial Link",
3829
- description: "Link-style with a growing underline. Refined, typographic.",
3742
+ const confidentChipRecipe = defineStaticButtonStyleRecipe({
3743
+ id: "confident-chip",
3744
+ accessibilityLabel: "Moderately rounded solid buttons with a crisp hover",
3830
3745
  tags: {
3831
- tone: "minimal",
3746
+ tone: "classic",
3832
3747
  energy: "calm",
3833
- motion: "subtle",
3834
- era: "editorial"
3748
+ motion: "standard",
3749
+ era: "modern"
3835
3750
  },
3836
- buttonSystem: {
3751
+ system: {
3837
3752
  global: {
3838
- cornerStyle: "square",
3753
+ cornerStyle: "rounded",
3754
+ shadow: "medium",
3839
3755
  textTransform: "none",
3840
- fontWeight: 500,
3841
- borderWidth: 1,
3756
+ fontWeight: 600,
3842
3757
  hoverTransform: "none",
3843
3758
  hoverColor: "none"
3844
3759
  },
3845
- variants: [
3846
- {
3847
- id: "primary",
3848
- name: "Primary",
3849
- enabled: true,
3850
- priority: 1,
3851
- background: {
3852
- type: "surface",
3853
- role: "link"
3854
- },
3855
- borderRadius: "rounded-none",
3856
- sizes: {
3857
- sm: { padding: "0.25rem 0" },
3858
- md: { padding: "0.5rem 0" },
3859
- lg: { padding: "0.625rem 0" },
3860
- xl: { padding: "0.75rem 0" }
3861
- },
3862
- effects: { base: [{
3863
- effectId: "border-bottom-grow",
3864
- options: {
3865
- height: 2,
3866
- colorToken: "text",
3867
- origin: "left"
3760
+ roles: {
3761
+ primary: {
3762
+ kind: "complete",
3763
+ appearance: {
3764
+ background: {
3765
+ type: "solid",
3766
+ colorToken: "primary"
3767
+ },
3768
+ borderRadius: "rounded-md",
3769
+ shadow: {
3770
+ elevation: "medium",
3771
+ softness: "soft",
3772
+ position: "bottom"
3773
+ },
3774
+ effects: {
3775
+ hover: [{
3776
+ effectId: "darken-background",
3777
+ options: { amount: 100 }
3778
+ }],
3779
+ active: [{
3780
+ effectId: "scale-down",
3781
+ options: { scale: "0.97" }
3782
+ }]
3868
3783
  }
3869
- }] }
3784
+ }
3870
3785
  },
3871
- {
3872
- id: "secondary",
3873
- name: "Secondary",
3874
- enabled: true,
3875
- priority: 2,
3876
- background: {
3877
- type: "surface",
3878
- role: "link"
3879
- },
3880
- borderRadius: "rounded-none",
3881
- sizes: {
3882
- sm: { padding: "0.25rem 0" },
3883
- md: { padding: "0.5rem 0" },
3884
- lg: { padding: "0.625rem 0" },
3885
- xl: { padding: "0.75rem 0" }
3886
- },
3887
- effects: { base: [{
3888
- effectId: "border-bottom-grow",
3889
- options: {
3890
- height: 1,
3891
- colorToken: "mutedText",
3892
- origin: "left"
3893
- }
3894
- }] }
3786
+ secondary: {
3787
+ kind: "complete",
3788
+ appearance: {
3789
+ background: {
3790
+ type: "solid",
3791
+ colorToken: "surface"
3792
+ },
3793
+ borderRadius: "rounded-md",
3794
+ border: { widthClass: "border" },
3795
+ effects: { hover: [{
3796
+ effectId: "background-on-hover",
3797
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
3798
+ }] }
3799
+ }
3895
3800
  },
3896
- {
3897
- id: "outline",
3898
- name: "Outline",
3899
- enabled: true,
3900
- priority: 3,
3901
- background: {
3902
- type: "surface",
3903
- role: "link"
3904
- },
3905
- borderRadius: "rounded-none",
3906
- sizes: {
3907
- sm: { padding: "0.25rem 0" },
3908
- md: { padding: "0.5rem 0" },
3909
- lg: { padding: "0.625rem 0" },
3910
- xl: { padding: "0.75rem 0" }
3911
- },
3912
- effects: { base: [{
3913
- effectId: "border-bottom-grow",
3914
- options: {
3915
- height: 1,
3916
- colorToken: "accent",
3917
- origin: "left"
3918
- }
3919
- }] }
3801
+ outline: {
3802
+ kind: "complete",
3803
+ appearance: {
3804
+ background: {
3805
+ type: "surface",
3806
+ role: "link"
3807
+ },
3808
+ borderRadius: "rounded-md",
3809
+ border: { widthClass: "border" },
3810
+ effects: { hover: [{
3811
+ effectId: "background-on-hover",
3812
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
3813
+ }] }
3814
+ }
3920
3815
  },
3921
- {
3922
- id: "ghost",
3923
- name: "Ghost",
3924
- enabled: true,
3925
- priority: 3,
3926
- background: {
3927
- type: "surface",
3928
- role: "link"
3929
- },
3930
- borderRadius: "rounded-none",
3931
- sizes: {
3932
- sm: { padding: "0.25rem 0" },
3933
- md: { padding: "0.5rem 0" },
3934
- lg: { padding: "0.625rem 0" },
3935
- xl: { padding: "0.75rem 0" }
3816
+ ghost: {
3817
+ kind: "complete",
3818
+ appearance: {
3819
+ background: {
3820
+ type: "surface",
3821
+ role: "link"
3822
+ },
3823
+ borderRadius: "rounded-md",
3824
+ effects: { hover: [{
3825
+ effectId: "background-on-hover",
3826
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
3827
+ }] }
3936
3828
  }
3829
+ },
3830
+ accent: {
3831
+ kind: "derive",
3832
+ from: "primary",
3833
+ overrides: { background: {
3834
+ type: "solid",
3835
+ colorToken: "accent"
3836
+ } }
3937
3837
  }
3938
- ]
3838
+ }
3939
3839
  }
3940
3840
  });
3941
- //#endregion
3942
- //#region ../theme-core/src/buttons/personalities/flat-ink.ts
3943
- /**
3944
- * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
3945
- * amber accent climax, all completely flat (no drop shadow) with a modest
3946
- * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
3947
- * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
3948
- * every variant lifts 2px; the amber accent is meant to be used sparingly as
3949
- * the page's one conversion climax.
3950
- */
3951
- const flatInk = defineButtonPersonality({
3952
- id: "flat-ink",
3953
- name: "Flat Ink",
3954
- description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
3841
+ const editorialSizes = {
3842
+ sm: { padding: "0.25rem 0" },
3843
+ md: { padding: "0.5rem 0" },
3844
+ lg: { padding: "0.625rem 0" },
3845
+ xl: { padding: "0.75rem 0" }
3846
+ };
3847
+ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
3848
+ id: "editorial-link",
3849
+ accessibilityLabel: "Flat typographic buttons with an underline treatment",
3955
3850
  tags: {
3956
- tone: "classic",
3851
+ tone: "minimal",
3957
3852
  energy: "calm",
3958
3853
  motion: "subtle",
3959
3854
  era: "editorial"
3960
3855
  },
3961
- buttonSystem: {
3856
+ system: {
3962
3857
  global: {
3963
- cornerStyle: "rounded",
3858
+ cornerStyle: "square",
3964
3859
  textTransform: "none",
3965
- fontWeight: 600,
3966
- borderWidth: 1,
3967
- typography: "body",
3968
- letterSpacing: "tight",
3969
- paddingPreset: "default",
3970
- hoverTransform: "lift",
3860
+ fontWeight: 500,
3861
+ hoverTransform: "none",
3971
3862
  hoverColor: "none"
3972
3863
  },
3973
- sizes: {
3974
- sm: {
3975
- padding: "0.625rem 1.125rem",
3976
- fontSize: "text-sm"
3864
+ roles: {
3865
+ primary: {
3866
+ kind: "complete",
3867
+ appearance: {
3868
+ background: {
3869
+ type: "surface",
3870
+ role: "link"
3871
+ },
3872
+ borderRadius: "rounded-none",
3873
+ sizes: editorialSizes,
3874
+ effects: { base: [{
3875
+ effectId: "border-bottom-grow",
3876
+ options: {
3877
+ height: 2,
3878
+ origin: "left"
3879
+ }
3880
+ }] }
3881
+ }
3977
3882
  },
3978
- md: { padding: "0.8125rem 1.375rem" },
3979
- lg: {
3980
- padding: "0.9375rem 1.625rem",
3981
- fontSize: "text-lg"
3883
+ secondary: {
3884
+ kind: "complete",
3885
+ appearance: {
3886
+ background: {
3887
+ type: "surface",
3888
+ role: "link"
3889
+ },
3890
+ borderRadius: "rounded-none",
3891
+ sizes: editorialSizes,
3892
+ effects: { base: [{
3893
+ effectId: "border-bottom-grow",
3894
+ options: {
3895
+ height: 1,
3896
+ origin: "left"
3897
+ }
3898
+ }] }
3899
+ }
3982
3900
  },
3983
- xl: {
3984
- padding: "1.0625rem 2rem",
3985
- fontSize: "text-xl"
3986
- }
3987
- },
3988
- variants: [
3989
- {
3990
- id: "primary",
3991
- name: "Primary",
3992
- enabled: true,
3993
- priority: 1,
3994
- background: {
3995
- type: "solid",
3996
- colorToken: "primary"
3997
- },
3998
- borderRadius: "rounded-lg",
3999
- affordance: { iconId: "ArrowRight" },
4000
- effects: { hover: [{
4001
- effectId: "lift",
4002
- options: { translateY: "-2px" }
4003
- }, {
4004
- effectId: "background-on-hover",
4005
- options: { colorToken: "secondary" }
4006
- }] }
3901
+ outline: {
3902
+ kind: "complete",
3903
+ appearance: {
3904
+ background: {
3905
+ type: "surface",
3906
+ role: "link"
3907
+ },
3908
+ borderRadius: "rounded-none",
3909
+ sizes: editorialSizes,
3910
+ effects: { base: [{
3911
+ effectId: "border-bottom-grow",
3912
+ options: {
3913
+ height: 1,
3914
+ origin: "left"
3915
+ }
3916
+ }] }
3917
+ }
4007
3918
  },
4008
- {
4009
- id: "secondary",
4010
- name: "Secondary",
4011
- enabled: true,
4012
- priority: 2,
4013
- background: {
4014
- type: "surface",
4015
- role: "link"
4016
- },
4017
- borderRadius: "rounded-lg",
4018
- border: { widthClass: "border" },
4019
- effects: { hover: [{
4020
- effectId: "lift",
4021
- options: { translateY: "-2px" }
4022
- }, {
4023
- effectId: "background-on-hover",
4024
- options: { colorToken: "surfaceAlt" }
4025
- }] }
3919
+ ghost: {
3920
+ kind: "complete",
3921
+ appearance: {
3922
+ background: {
3923
+ type: "surface",
3924
+ role: "link"
3925
+ },
3926
+ borderRadius: "rounded-none",
3927
+ sizes: editorialSizes
3928
+ }
4026
3929
  },
4027
- {
4028
- id: "accent",
4029
- name: "Accent",
4030
- enabled: true,
4031
- priority: 3,
4032
- background: {
4033
- type: "solid",
4034
- colorToken: "accent"
4035
- },
4036
- borderRadius: "rounded-lg",
4037
- affordance: { iconId: "ArrowRight" },
4038
- effects: { hover: [{
4039
- effectId: "lift",
4040
- options: { translateY: "-2px" }
4041
- }] }
3930
+ accent: {
3931
+ kind: "derive",
3932
+ from: "primary",
3933
+ overrides: { effects: { base: [{
3934
+ effectId: "border-bottom-grow",
3935
+ options: {
3936
+ height: 2,
3937
+ origin: "left"
3938
+ }
3939
+ }] } }
4042
3940
  }
4043
- ]
3941
+ }
4044
3942
  }
4045
3943
  });
4046
- //#endregion
4047
- //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
4048
- /**
4049
- * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
4050
- * feel. Reads as bold and handmade; suits expressive craft brands.
4051
- */
4052
- const inkStamp = defineButtonPersonality({
4053
- id: "ink-stamp",
4054
- name: "Ink Stamp",
4055
- description: "Hand-stamped block-print buttons with a slight lift on hover.",
3944
+ const pebbleRecipe = defineStaticButtonStyleRecipe({
3945
+ id: "pebble",
3946
+ accessibilityLabel: "Generously padded pill buttons",
4056
3947
  tags: {
4057
- tone: "bold",
4058
- energy: "vibrant",
3948
+ tone: "classic",
3949
+ energy: "calm",
4059
3950
  motion: "standard",
4060
3951
  era: "organic"
4061
3952
  },
4062
- buttonSystem: {
3953
+ system: {
4063
3954
  global: {
4064
- cornerStyle: "rounded",
4065
- textTransform: "uppercase",
4066
- fontWeight: 600,
4067
- borderWidth: 1,
3955
+ cornerStyle: "pill",
3956
+ textTransform: "none",
3957
+ fontWeight: 500,
4068
3958
  hoverTransform: "none",
4069
- hoverColor: "none",
4070
- letterSpacing: "loose"
3959
+ hoverColor: "none"
4071
3960
  },
4072
3961
  sizes: {
4073
3962
  sm: {
4074
- padding: "0.5rem 1.25rem",
4075
- fontSize: "text-xs"
4076
- },
4077
- md: {
4078
- padding: "0.75rem 1.75rem",
3963
+ padding: "0.625rem 1.75rem",
4079
3964
  fontSize: "text-sm"
4080
3965
  },
3966
+ md: { padding: "0.875rem 2.25rem" },
4081
3967
  lg: {
4082
- padding: "0.875rem 2.25rem",
4083
- fontSize: "text-base"
4084
- },
4085
- xl: {
4086
3968
  padding: "1.125rem 2.75rem",
4087
3969
  fontSize: "text-lg"
3970
+ },
3971
+ xl: {
3972
+ padding: "1.5rem 3.5rem",
3973
+ fontSize: "text-xl"
4088
3974
  }
4089
3975
  },
4090
- variants: [
4091
- {
4092
- id: "primary",
4093
- name: "Primary",
4094
- enabled: true,
4095
- priority: 1,
4096
- background: {
4097
- type: "solid",
4098
- colorToken: "primary"
4099
- },
4100
- borderRadius: "rounded-md",
4101
- effects: {
4102
- hover: [{
4103
- effectId: "drop-shadow",
4104
- options: { intensity: "medium" }
4105
- }, {
4106
- effectId: "scale-up",
4107
- options: { scale: "1.02" }
4108
- }],
4109
- active: [{
4110
- effectId: "scale-down",
4111
- options: { scale: "0.98" }
4112
- }]
3976
+ roles: {
3977
+ primary: {
3978
+ kind: "complete",
3979
+ appearance: {
3980
+ background: {
3981
+ type: "solid",
3982
+ colorToken: "primary"
3983
+ },
3984
+ borderRadius: "rounded-full",
3985
+ effects: { hover: [{
3986
+ effectId: "darken-background",
3987
+ options: { amount: 100 }
3988
+ }] }
4113
3989
  }
4114
3990
  },
4115
- {
4116
- id: "secondary",
4117
- name: "Secondary",
4118
- enabled: true,
4119
- priority: 2,
4120
- background: {
4121
- type: "surface",
4122
- role: "link"
4123
- },
4124
- borderRadius: "rounded-md",
4125
- border: { widthClass: "border-2" },
4126
- effects: { hover: [{
4127
- effectId: "background-on-hover",
4128
- options: { colorToken: "surface" }
4129
- }] }
4130
- },
4131
- {
4132
- id: "outline",
4133
- name: "Outline",
4134
- enabled: true,
4135
- priority: 3,
4136
- background: {
4137
- type: "surface",
4138
- role: "link"
4139
- },
4140
- borderRadius: "rounded-md",
4141
- border: { widthClass: "border-2" },
4142
- effects: { hover: [{
4143
- effectId: "background-on-hover",
4144
- options: { colorToken: "surface" }
4145
- }] }
4146
- },
4147
- {
4148
- id: "ghost",
4149
- name: "Ghost",
4150
- enabled: true,
4151
- priority: 3,
4152
- background: {
4153
- type: "surface",
4154
- role: "link"
4155
- },
4156
- borderRadius: "rounded-none",
4157
- sizes: {
4158
- sm: {
4159
- padding: "0.375rem 0",
4160
- fontSize: "text-xs"
3991
+ secondary: {
3992
+ kind: "complete",
3993
+ appearance: {
3994
+ background: {
3995
+ type: "surface",
3996
+ role: "link"
4161
3997
  },
4162
- md: {
4163
- padding: "0.5rem 0",
4164
- fontSize: "text-sm"
3998
+ borderRadius: "rounded-full",
3999
+ border: { widthClass: "border-2" },
4000
+ effects: { hover: [{
4001
+ effectId: "background-on-hover",
4002
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
4003
+ }] }
4004
+ }
4005
+ },
4006
+ outline: {
4007
+ kind: "complete",
4008
+ appearance: {
4009
+ background: {
4010
+ type: "surface",
4011
+ role: "link"
4165
4012
  },
4166
- lg: {
4167
- padding: "0.625rem 0",
4168
- fontSize: "text-base"
4013
+ borderRadius: "rounded-full",
4014
+ border: { widthClass: "border" },
4015
+ effects: { hover: [{
4016
+ effectId: "background-on-hover",
4017
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4018
+ }] }
4019
+ }
4020
+ },
4021
+ ghost: {
4022
+ kind: "complete",
4023
+ appearance: {
4024
+ background: {
4025
+ type: "surface",
4026
+ role: "link"
4169
4027
  },
4170
- xl: {
4171
- padding: "0.75rem 0",
4172
- fontSize: "text-lg"
4173
- }
4174
- },
4175
- effects: { base: [{
4176
- effectId: "border-bottom-grow",
4177
- options: {
4178
- height: 1,
4179
- colorToken: "accent",
4180
- origin: "left"
4028
+ borderRadius: "rounded-full",
4029
+ sizes: {
4030
+ sm: {
4031
+ padding: "0.375rem 0.875rem",
4032
+ fontSize: "text-sm"
4033
+ },
4034
+ md: { padding: "0.5rem 1rem" },
4035
+ lg: {
4036
+ padding: "0.625rem 1.25rem",
4037
+ fontSize: "text-lg"
4038
+ },
4039
+ xl: {
4040
+ padding: "0.75rem 1.5rem",
4041
+ fontSize: "text-xl"
4042
+ }
4181
4043
  }
4182
- }] }
4044
+ }
4045
+ },
4046
+ accent: {
4047
+ kind: "derive",
4048
+ from: "primary",
4049
+ overrides: { background: {
4050
+ type: "solid",
4051
+ colorToken: "accent"
4052
+ } }
4183
4053
  }
4184
- ]
4054
+ }
4185
4055
  }
4186
4056
  });
4187
- //#endregion
4188
- //#region ../theme-core/src/buttons/personalities/joy-stamp.ts
4189
- /**
4190
- * Joy Stamp — neo-brutalist pills with a hard, brand-coloured offset shadow
4191
- * that collapses as the button presses into it on hover. Authored for the
4192
- * Original Joy site style: a cream "stamp" shadow under the solid primary, and
4193
- * a brown shadow under the cream outline. Reads as bold, warm, and handmade.
4194
- *
4195
- * The coloured offset shadow relies on the per-variant `shadow.colorToken`
4196
- * extension; the press is the shared `press-down` effect, which matches its
4197
- * translate to each variant's own shadow offset.
4198
- */
4199
- const joyStamp = defineButtonPersonality({
4200
- id: "joy-stamp",
4201
- name: "Joy Stamp",
4202
- description: "Pill buttons with a hard, brand-coloured offset shadow that presses in on hover.",
4057
+ const brushedWashRecipe = defineStaticButtonStyleRecipe({
4058
+ id: "brushed-wash",
4059
+ accessibilityLabel: "Generously padded gradient buttons with soft shadows",
4203
4060
  tags: {
4204
- tone: "bold",
4061
+ tone: "classic",
4205
4062
  energy: "vibrant",
4206
- motion: "expressive",
4063
+ motion: "standard",
4207
4064
  era: "organic"
4208
4065
  },
4209
- buttonSystem: {
4066
+ system: {
4210
4067
  global: {
4211
- cornerStyle: "pill",
4212
- shadow: "high",
4068
+ cornerStyle: "rounded",
4069
+ shadow: "low",
4213
4070
  textTransform: "none",
4214
- typography: "heading",
4215
- fontWeight: 700,
4216
- fontSize: "text-lg",
4217
- borderWidth: 2,
4071
+ fontWeight: 500,
4218
4072
  hoverTransform: "none",
4219
4073
  hoverColor: "none"
4220
4074
  },
4221
4075
  sizes: {
4222
4076
  sm: {
4223
- padding: "0.625rem 1.25rem",
4224
- fontSize: "text-base"
4225
- },
4226
- md: {
4227
- padding: "0.875rem 1.75rem",
4228
- fontSize: "text-lg"
4077
+ padding: "0.75rem 2rem",
4078
+ fontSize: "text-sm"
4229
4079
  },
4080
+ md: { padding: "1rem 2.5rem" },
4230
4081
  lg: {
4231
- padding: "1.125rem 2.25rem",
4232
- fontSize: "text-xl"
4082
+ padding: "1.25rem 3rem",
4083
+ fontSize: "text-lg"
4233
4084
  },
4234
4085
  xl: {
4235
- padding: "1.25rem 2.75rem",
4236
- fontSize: "text-2xl"
4086
+ padding: "1.625rem 3.75rem",
4087
+ fontSize: "text-xl"
4237
4088
  }
4238
4089
  },
4239
- variants: [{
4240
- id: "primary",
4241
- name: "Primary",
4242
- enabled: true,
4243
- priority: 1,
4244
- background: {
4245
- type: "solid",
4246
- colorToken: "primary"
4090
+ roles: {
4091
+ primary: {
4092
+ kind: "complete",
4093
+ appearance: {
4094
+ background: {
4095
+ type: "gradient",
4096
+ stops: ["primary", "accent"],
4097
+ direction: "to-r",
4098
+ foregroundToken: "primaryForeground"
4099
+ },
4100
+ borderRadius: "rounded-lg",
4101
+ shadow: {
4102
+ elevation: "low",
4103
+ softness: "soft",
4104
+ position: "bottom"
4105
+ }
4106
+ }
4247
4107
  },
4248
- borderRadius: "rounded-full",
4249
- shadow: {
4250
- elevation: "high",
4251
- softness: "hard",
4252
- position: "bottom-right",
4253
- colorToken: "text"
4108
+ secondary: {
4109
+ kind: "complete",
4110
+ appearance: {
4111
+ background: {
4112
+ type: "gradient",
4113
+ stops: ["secondary", "accent"],
4114
+ direction: "to-br",
4115
+ foregroundToken: "primaryForeground"
4116
+ },
4117
+ borderRadius: "rounded-lg",
4118
+ shadow: {
4119
+ elevation: "low",
4120
+ softness: "soft",
4121
+ position: "bottom"
4122
+ }
4123
+ }
4254
4124
  },
4255
- effects: { hover: [{ effectId: "press-down" }] }
4256
- }, {
4257
- id: "secondary",
4258
- name: "Secondary",
4259
- enabled: true,
4260
- priority: 2,
4261
- background: {
4262
- type: "surface",
4263
- role: "link"
4264
- },
4265
- borderRadius: "rounded-full",
4266
- border: { widthClass: "border-2" },
4267
- shadow: {
4268
- elevation: "high",
4269
- softness: "hard",
4270
- position: "bottom-right",
4271
- colorToken: "secondary"
4272
- },
4273
- effects: { hover: [{ effectId: "press-down" }] }
4274
- }]
4125
+ outline: {
4126
+ kind: "complete",
4127
+ appearance: {
4128
+ background: {
4129
+ type: "surface",
4130
+ role: "link"
4131
+ },
4132
+ borderRadius: "rounded-lg",
4133
+ border: { widthClass: "border" },
4134
+ effects: { hover: [{
4135
+ effectId: "background-on-hover",
4136
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4137
+ }] }
4138
+ }
4139
+ },
4140
+ ghost: {
4141
+ kind: "complete",
4142
+ appearance: {
4143
+ background: {
4144
+ type: "surface",
4145
+ role: "link"
4146
+ },
4147
+ borderRadius: "rounded-lg",
4148
+ effects: { hover: [{
4149
+ effectId: "background-on-hover",
4150
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4151
+ }] }
4152
+ }
4153
+ },
4154
+ accent: {
4155
+ kind: "derive",
4156
+ from: "primary",
4157
+ overrides: { background: {
4158
+ type: "solid",
4159
+ colorToken: "accent"
4160
+ } }
4161
+ }
4162
+ }
4275
4163
  }
4276
4164
  });
4277
- //#endregion
4278
- //#region ../theme-core/src/buttons/personalities/ornate-ritual.ts
4279
- /**
4280
- * Ornate Ritual — framed, ceremonial buttons with jewel-toned fills and
4281
- * deliberate spacing. Designed for music, ceremony, and soulful event sites.
4282
- */
4283
- const ornateRitual = defineButtonPersonality({
4284
- id: "ornate-ritual",
4285
- name: "Ornate Ritual",
4286
- description: "Ceremonial framed buttons with jewel-toned fills and quiet color-change hover states.",
4165
+ const inkStampRecipe = defineStaticButtonStyleRecipe({
4166
+ id: "ink-stamp",
4167
+ accessibilityLabel: "Uppercase block-print buttons with a playful hover",
4287
4168
  tags: {
4288
- tone: "classic",
4169
+ tone: "bold",
4289
4170
  energy: "vibrant",
4290
- motion: "subtle",
4171
+ motion: "standard",
4291
4172
  era: "organic"
4292
4173
  },
4293
- buttonSystem: {
4174
+ system: {
4294
4175
  global: {
4295
4176
  cornerStyle: "rounded",
4296
4177
  textTransform: "uppercase",
4297
4178
  fontWeight: 600,
4298
- borderWidth: 1,
4299
4179
  hoverTransform: "none",
4300
4180
  hoverColor: "none",
4301
4181
  letterSpacing: "loose"
4302
4182
  },
4303
4183
  sizes: {
4304
4184
  sm: {
4305
- padding: "0.5625rem 1.25rem",
4185
+ padding: "0.5rem 1.25rem",
4306
4186
  fontSize: "text-xs"
4307
4187
  },
4308
4188
  md: {
4309
4189
  padding: "0.75rem 1.75rem",
4310
- fontSize: "text-xs"
4190
+ fontSize: "text-sm"
4311
4191
  },
4312
4192
  lg: {
4313
4193
  padding: "0.875rem 2.25rem",
4314
- fontSize: "text-sm"
4315
- },
4316
- xl: {
4317
- padding: "1rem 2.75rem",
4318
4194
  fontSize: "text-base"
4319
- }
4320
- },
4321
- variants: [
4322
- {
4323
- id: "primary",
4324
- name: "Primary",
4325
- enabled: true,
4326
- priority: 1,
4327
- background: {
4328
- type: "solid",
4329
- colorToken: "primary"
4330
- },
4331
- borderRadius: "rounded-md",
4332
- border: { widthClass: "border" },
4333
- effects: {
4334
- hover: [{
4335
- effectId: "background-on-hover",
4336
- options: { colorToken: "primary-100" }
4337
- }],
4338
- active: [{
4339
- effectId: "scale-down",
4340
- options: { scale: "0.98" }
4341
- }]
4342
- }
4343
- },
4344
- {
4345
- id: "secondary",
4346
- name: "Secondary",
4347
- enabled: true,
4348
- priority: 2,
4349
- background: {
4350
- type: "solid",
4351
- colorToken: "secondary"
4352
- },
4353
- borderRadius: "rounded-md",
4354
- border: { widthClass: "border" },
4355
- effects: { hover: [{
4356
- effectId: "background-on-hover",
4357
- options: { colorToken: "surface" }
4358
- }] }
4359
- },
4360
- {
4361
- id: "outline",
4362
- name: "Outline",
4363
- enabled: true,
4364
- priority: 3,
4365
- background: {
4366
- type: "surface",
4367
- role: "link"
4368
- },
4369
- borderRadius: "rounded-md",
4370
- border: { widthClass: "border" },
4371
- effects: { hover: [{
4372
- effectId: "background-on-hover",
4373
- options: { colorToken: "muted" }
4374
- }] }
4375
- },
4376
- {
4377
- id: "ghost",
4378
- name: "Ghost",
4379
- enabled: true,
4380
- priority: 3,
4381
- background: {
4382
- type: "surface",
4383
- role: "link"
4384
- },
4385
- borderRadius: "rounded-none",
4386
- sizes: {
4387
- sm: {
4388
- padding: "0.375rem 0",
4389
- fontSize: "text-xs"
4390
- },
4391
- md: {
4392
- padding: "0.5rem 0",
4393
- fontSize: "text-sm"
4394
- },
4395
- lg: {
4396
- padding: "0.625rem 0",
4397
- fontSize: "text-base"
4398
- },
4399
- xl: {
4400
- padding: "0.75rem 0",
4401
- fontSize: "text-lg"
4402
- }
4403
- },
4404
- effects: { base: [{
4405
- effectId: "border-bottom-grow",
4406
- options: {
4407
- height: 1,
4408
- colorToken: "accent",
4409
- origin: "left"
4410
- }
4411
- }] }
4412
- }
4413
- ]
4414
- }
4415
- });
4416
- //#endregion
4417
- //#region ../theme-core/src/buttons/personalities/pebble.ts
4418
- /**
4419
- * Pebble — large pill primaries with generous padding. Reads as spa-luxurious
4420
- * and natural; pairs well with wellness/yoga/dance brands.
4421
- */
4422
- const pebble = defineButtonPersonality({
4423
- id: "pebble",
4424
- name: "Pebble",
4425
- description: "Generous pill buttons with a quiet colour-softening hover.",
4426
- tags: {
4427
- tone: "classic",
4428
- energy: "calm",
4429
- motion: "standard",
4430
- era: "organic"
4431
- },
4432
- buttonSystem: {
4433
- global: {
4434
- cornerStyle: "pill",
4435
- textTransform: "none",
4436
- fontWeight: 500,
4437
- borderWidth: 2,
4438
- hoverTransform: "none",
4439
- hoverColor: "none"
4440
- },
4441
- sizes: {
4442
- sm: {
4443
- padding: "0.625rem 1.75rem",
4444
- fontSize: "text-sm"
4445
4195
  },
4446
- md: { padding: "0.875rem 2.25rem" },
4447
- lg: {
4196
+ xl: {
4448
4197
  padding: "1.125rem 2.75rem",
4449
4198
  fontSize: "text-lg"
4450
- },
4451
- xl: {
4452
- padding: "1.5rem 3.5rem",
4453
- fontSize: "text-xl"
4454
4199
  }
4455
4200
  },
4456
- variants: [
4457
- {
4458
- id: "primary",
4459
- name: "Primary",
4460
- enabled: true,
4461
- priority: 1,
4462
- background: {
4463
- type: "solid",
4464
- colorToken: "primary"
4465
- },
4466
- borderRadius: "rounded-full",
4467
- effects: { hover: [{
4468
- effectId: "darken-background",
4469
- options: { amount: 100 }
4470
- }] }
4201
+ roles: {
4202
+ primary: {
4203
+ kind: "complete",
4204
+ appearance: {
4205
+ background: {
4206
+ type: "solid",
4207
+ colorToken: "primary"
4208
+ },
4209
+ borderRadius: "rounded-md",
4210
+ effects: {
4211
+ hover: [{
4212
+ effectId: "drop-shadow",
4213
+ options: { intensity: "medium" }
4214
+ }, {
4215
+ effectId: "scale-up",
4216
+ options: { scale: "1.02" }
4217
+ }],
4218
+ active: [{
4219
+ effectId: "scale-down",
4220
+ options: { scale: "0.98" }
4221
+ }]
4222
+ }
4223
+ }
4471
4224
  },
4472
- {
4473
- id: "secondary",
4474
- name: "Secondary",
4475
- enabled: true,
4476
- priority: 2,
4477
- background: {
4478
- type: "surface",
4479
- role: "link"
4480
- },
4481
- borderRadius: "rounded-full",
4482
- border: { widthClass: "border-2" },
4483
- effects: { hover: [{
4484
- effectId: "background-on-hover",
4485
- options: { colorToken: "accent" }
4486
- }] }
4225
+ secondary: {
4226
+ kind: "complete",
4227
+ appearance: {
4228
+ background: {
4229
+ type: "surface",
4230
+ role: "link"
4231
+ },
4232
+ borderRadius: "rounded-md",
4233
+ border: { widthClass: "border-2" },
4234
+ effects: { hover: [{
4235
+ effectId: "background-on-hover",
4236
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4237
+ }] }
4238
+ }
4487
4239
  },
4488
- {
4489
- id: "outline",
4490
- name: "Outline",
4491
- enabled: true,
4492
- priority: 3,
4493
- background: {
4494
- type: "surface",
4495
- role: "link"
4496
- },
4497
- borderRadius: "rounded-full",
4498
- border: { widthClass: "border" },
4499
- effects: { hover: [{
4500
- effectId: "background-on-hover",
4501
- options: { colorToken: "surface" }
4502
- }] }
4240
+ outline: {
4241
+ kind: "complete",
4242
+ appearance: {
4243
+ background: {
4244
+ type: "surface",
4245
+ role: "link"
4246
+ },
4247
+ borderRadius: "rounded-md",
4248
+ border: { widthClass: "border-2" },
4249
+ effects: { hover: [{
4250
+ effectId: "background-on-hover",
4251
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4252
+ }] }
4253
+ }
4503
4254
  },
4504
- {
4505
- id: "ghost",
4506
- name: "Ghost",
4507
- enabled: true,
4508
- priority: 3,
4509
- background: {
4510
- type: "surface",
4511
- role: "link"
4512
- },
4513
- borderRadius: "rounded-full",
4514
- sizes: {
4515
- sm: {
4516
- padding: "0.375rem 0.875rem",
4517
- fontSize: "text-sm"
4255
+ ghost: {
4256
+ kind: "complete",
4257
+ appearance: {
4258
+ background: {
4259
+ type: "surface",
4260
+ role: "link"
4518
4261
  },
4519
- md: { padding: "0.5rem 1rem" },
4520
- lg: {
4521
- padding: "0.625rem 1.25rem",
4522
- fontSize: "text-lg"
4262
+ borderRadius: "rounded-none",
4263
+ sizes: {
4264
+ sm: {
4265
+ padding: "0.375rem 0",
4266
+ fontSize: "text-xs"
4267
+ },
4268
+ md: {
4269
+ padding: "0.5rem 0",
4270
+ fontSize: "text-sm"
4271
+ },
4272
+ lg: {
4273
+ padding: "0.625rem 0",
4274
+ fontSize: "text-base"
4275
+ },
4276
+ xl: {
4277
+ padding: "0.75rem 0",
4278
+ fontSize: "text-lg"
4279
+ }
4523
4280
  },
4524
- xl: {
4525
- padding: "0.75rem 1.5rem",
4526
- fontSize: "text-xl"
4527
- }
4281
+ effects: { base: [{
4282
+ effectId: "border-bottom-grow",
4283
+ options: {
4284
+ height: 1,
4285
+ origin: "left"
4286
+ }
4287
+ }] }
4528
4288
  }
4289
+ },
4290
+ accent: {
4291
+ kind: "derive",
4292
+ from: "primary",
4293
+ overrides: { background: {
4294
+ type: "solid",
4295
+ colorToken: "accent"
4296
+ } }
4529
4297
  }
4530
- ]
4298
+ }
4531
4299
  }
4532
4300
  });
4533
- //#endregion
4534
- //#region ../theme-core/src/buttons/personalities/quiet-arrow.ts
4535
- /**
4536
- * Quiet Arrow — wide, flat editorial buttons for grounded service businesses.
4537
- * Primary and secondary keep stable semantic colours; surrounding surfaces are
4538
- * responsible for contrast rather than reversing the button roles.
4539
- */
4540
- const quietArrow = defineButtonPersonality({
4541
- id: "quiet-arrow",
4542
- name: "Quiet Arrow",
4543
- description: "Wide, flat arrow buttons with a solid primary and a primary-colour outlined secondary.",
4301
+ const flatInkRecipe = defineStaticButtonStyleRecipe({
4302
+ id: "flat-ink",
4303
+ accessibilityLabel: "Flat rounded buttons with arrow affordances",
4544
4304
  tags: {
4545
4305
  tone: "classic",
4546
4306
  energy: "calm",
4547
4307
  motion: "subtle",
4548
4308
  era: "editorial"
4549
4309
  },
4550
- buttonSystem: {
4310
+ system: {
4551
4311
  global: {
4552
4312
  cornerStyle: "rounded",
4553
- textTransform: "uppercase",
4554
- fontWeight: 500,
4555
- borderWidth: 1,
4313
+ textTransform: "none",
4314
+ fontWeight: 600,
4556
4315
  typography: "body",
4557
- fontSize: "text-sm",
4558
- letterSpacing: "loose",
4559
- hoverTransform: "none",
4316
+ letterSpacing: "tight",
4317
+ paddingPreset: "default",
4318
+ hoverTransform: "lift",
4560
4319
  hoverColor: "none"
4561
4320
  },
4562
4321
  sizes: {
4563
4322
  sm: {
4564
- padding: "0.625rem 1.25rem",
4565
- fontSize: "text-xs"
4566
- },
4567
- md: {
4568
- padding: "0.8125rem 1.75rem",
4323
+ padding: "0.625rem 1.125rem",
4569
4324
  fontSize: "text-sm"
4570
4325
  },
4326
+ md: { padding: "0.8125rem 1.375rem" },
4571
4327
  lg: {
4572
- padding: "0.9375rem 2.125rem",
4573
- fontSize: "text-sm"
4328
+ padding: "0.9375rem 1.625rem",
4329
+ fontSize: "text-lg"
4574
4330
  },
4575
4331
  xl: {
4576
- padding: "1.0625rem 2.5rem",
4577
- fontSize: "text-base"
4332
+ padding: "1.0625rem 2rem",
4333
+ fontSize: "text-xl"
4578
4334
  }
4579
4335
  },
4580
- variants: [
4581
- {
4582
- id: "primary",
4583
- name: "Primary",
4584
- enabled: true,
4585
- priority: 1,
4586
- background: {
4587
- type: "solid",
4588
- colorToken: "primary"
4589
- },
4590
- borderRadius: "rounded-xl",
4591
- affordance: { iconId: "ArrowRight" },
4592
- effects: { hover: [{
4593
- effectId: "darken-background",
4594
- options: { amount: 100 }
4595
- }] }
4336
+ roles: {
4337
+ primary: {
4338
+ kind: "complete",
4339
+ appearance: {
4340
+ background: {
4341
+ type: "solid",
4342
+ colorToken: "primary"
4343
+ },
4344
+ borderRadius: "rounded-lg",
4345
+ affordance: { iconId: "ArrowRight" },
4346
+ effects: { hover: [{ effectId: "lift" }, {
4347
+ effectId: "background-on-hover",
4348
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("secondary") }
4349
+ }] }
4350
+ }
4596
4351
  },
4597
- {
4598
- id: "secondary",
4599
- name: "Secondary",
4600
- enabled: true,
4601
- priority: 2,
4602
- background: {
4603
- type: "surface",
4604
- role: "link"
4605
- },
4606
- borderRadius: "rounded-xl",
4607
- border: { widthClass: "border" },
4608
- affordance: { iconId: "ArrowRight" },
4609
- effects: { hover: [{
4610
- effectId: "background-on-hover",
4611
- options: { colorToken: "surfaceAlt" }
4612
- }] }
4352
+ secondary: {
4353
+ kind: "complete",
4354
+ appearance: {
4355
+ background: {
4356
+ type: "surface",
4357
+ role: "link"
4358
+ },
4359
+ borderRadius: "rounded-lg",
4360
+ border: { widthClass: "border" },
4361
+ effects: { hover: [{ effectId: "lift" }, {
4362
+ effectId: "background-on-hover",
4363
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
4364
+ }] }
4365
+ }
4613
4366
  },
4614
- {
4615
- id: "outline",
4616
- name: "Outline",
4617
- enabled: true,
4618
- priority: 3,
4619
- background: {
4620
- type: "surface",
4621
- role: "link"
4622
- },
4623
- borderRadius: "rounded-xl",
4624
- border: { widthClass: "border" }
4367
+ outline: {
4368
+ kind: "complete",
4369
+ appearance: {
4370
+ background: {
4371
+ type: "surface",
4372
+ role: "link"
4373
+ },
4374
+ borderRadius: "rounded-lg",
4375
+ border: { widthClass: "border" },
4376
+ effects: { hover: [{ effectId: "lift" }, {
4377
+ effectId: "background-on-hover",
4378
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
4379
+ }] }
4380
+ }
4625
4381
  },
4626
- {
4627
- id: "ghost",
4628
- name: "Ghost",
4629
- enabled: true,
4630
- priority: 3,
4631
- background: {
4632
- type: "surface",
4633
- role: "link"
4634
- },
4635
- borderRadius: "rounded-xl"
4382
+ ghost: {
4383
+ kind: "complete",
4384
+ appearance: {
4385
+ background: {
4386
+ type: "surface",
4387
+ role: "link"
4388
+ },
4389
+ borderRadius: "rounded-lg",
4390
+ effects: { hover: [{ effectId: "lift" }] }
4391
+ }
4392
+ },
4393
+ accent: {
4394
+ kind: "complete",
4395
+ appearance: {
4396
+ background: {
4397
+ type: "solid",
4398
+ colorToken: "accent"
4399
+ },
4400
+ borderRadius: "rounded-lg",
4401
+ affordance: { iconId: "ArrowRight" },
4402
+ effects: { hover: [{ effectId: "lift" }] }
4403
+ }
4636
4404
  }
4637
- ]
4405
+ }
4638
4406
  }
4639
4407
  });
4640
- //#endregion
4641
- //#region ../theme-core/src/buttons/personalities/showtime-pill.ts
4642
- /**
4643
- * Showtime Pill — bold, spacious, fully rounded buttons with playful pop
4644
- * motion. Suits energetic schools, performers, festivals, and community brands
4645
- * that need calls to action to feel loud without becoming visually chaotic.
4646
- */
4647
- const showtimePill = defineButtonPersonality({
4408
+ const showtimePillRecipe = defineStaticButtonStyleRecipe({
4648
4409
  id: "showtime-pill",
4649
- name: "Showtime Pill",
4650
- description: "Bold pill buttons with uppercase labels, generous padding, and a playful pop.",
4410
+ accessibilityLabel: "Bold uppercase pill buttons with playful movement",
4651
4411
  tags: {
4652
4412
  tone: "bold",
4653
4413
  energy: "vibrant",
4654
4414
  motion: "expressive",
4655
4415
  era: "modern"
4656
4416
  },
4657
- buttonSystem: {
4417
+ system: {
4658
4418
  global: {
4659
4419
  cornerStyle: "pill",
4660
4420
  shadow: "low",
4661
4421
  textTransform: "uppercase",
4662
4422
  fontWeight: 700,
4663
- borderWidth: 2,
4664
4423
  hoverTransform: "lift",
4665
- hoverColor: "darken",
4424
+ hoverColor: "none",
4666
4425
  typography: "body",
4667
4426
  paddingPreset: "spacious",
4668
4427
  italic: false
@@ -4685,272 +4444,373 @@ const showtimePill = defineButtonPersonality({
4685
4444
  fontSize: "1.125rem"
4686
4445
  }
4687
4446
  },
4688
- variants: [
4689
- {
4690
- id: "primary",
4691
- name: "Primary",
4692
- enabled: true,
4693
- priority: 1,
4694
- background: {
4695
- type: "solid",
4696
- colorToken: "primary"
4697
- },
4698
- borderRadius: "rounded-full",
4699
- effects: { hover: [{ effectId: "pop" }] }
4700
- },
4701
- {
4702
- id: "secondary",
4703
- name: "Secondary",
4704
- enabled: true,
4705
- priority: 2,
4706
- background: {
4707
- type: "surface",
4708
- role: "link"
4709
- },
4710
- borderRadius: "rounded-full",
4711
- border: { widthClass: "border-2" },
4712
- effects: { hover: [{ effectId: "pop" }] }
4447
+ roles: {
4448
+ primary: {
4449
+ kind: "complete",
4450
+ appearance: {
4451
+ background: {
4452
+ type: "solid",
4453
+ colorToken: "primary"
4454
+ },
4455
+ borderRadius: "rounded-full",
4456
+ effects: { hover: [{ effectId: "pop" }] }
4457
+ }
4713
4458
  },
4714
- {
4715
- id: "hero",
4716
- name: "Hero",
4717
- enabled: true,
4718
- priority: 1,
4719
- background: {
4720
- type: "solid",
4721
- colorToken: "secondary"
4722
- },
4723
- borderRadius: "rounded-full",
4724
- effects: { hover: [{ effectId: "pop" }] }
4459
+ secondary: {
4460
+ kind: "complete",
4461
+ appearance: {
4462
+ background: {
4463
+ type: "surface",
4464
+ role: "link"
4465
+ },
4466
+ borderRadius: "rounded-full",
4467
+ border: { widthClass: "border-2" },
4468
+ effects: { hover: [{ effectId: "pop" }] }
4469
+ }
4725
4470
  },
4726
- {
4727
- id: "header",
4728
- name: "Header",
4729
- enabled: true,
4730
- priority: 2,
4731
- background: {
4732
- type: "solid",
4733
- colorToken: "background"
4734
- },
4735
- borderRadius: "rounded-full",
4736
- border: { widthClass: "border-2" },
4737
- effects: { hover: [{
4738
- effectId: "background-on-hover",
4739
- options: { colorToken: "surface" }
4740
- }] }
4471
+ outline: {
4472
+ kind: "complete",
4473
+ appearance: {
4474
+ background: {
4475
+ type: "surface",
4476
+ role: "link"
4477
+ },
4478
+ borderRadius: "rounded-full",
4479
+ border: { widthClass: "border-2" },
4480
+ effects: { hover: [{
4481
+ effectId: "background-on-hover",
4482
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
4483
+ }] }
4484
+ }
4741
4485
  },
4742
- {
4743
- id: "outline",
4744
- name: "Outline",
4745
- enabled: true,
4746
- priority: 3,
4747
- background: {
4748
- type: "surface",
4749
- role: "link"
4750
- },
4751
- borderRadius: "rounded-full",
4752
- border: { widthClass: "border-2" },
4753
- effects: { hover: [{
4754
- effectId: "background-on-hover",
4755
- options: { colorToken: "background" }
4756
- }] }
4486
+ ghost: {
4487
+ kind: "complete",
4488
+ appearance: {
4489
+ background: {
4490
+ type: "surface",
4491
+ role: "link"
4492
+ },
4493
+ borderRadius: "rounded-full",
4494
+ effects: { hover: [{
4495
+ effectId: "background-on-hover",
4496
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4497
+ }] }
4498
+ }
4757
4499
  },
4758
- {
4759
- id: "ghost",
4760
- name: "Ghost",
4761
- enabled: true,
4762
- priority: 3,
4763
- background: {
4764
- type: "surface",
4765
- role: "link"
4766
- },
4767
- borderRadius: "rounded-full",
4768
- effects: { hover: [{
4769
- effectId: "background-on-hover",
4770
- options: { colorToken: "surface" }
4771
- }] }
4500
+ accent: {
4501
+ kind: "complete",
4502
+ appearance: {
4503
+ background: {
4504
+ type: "solid",
4505
+ colorToken: "secondary"
4506
+ },
4507
+ borderRadius: "rounded-full",
4508
+ effects: { hover: [{ effectId: "pop" }] }
4509
+ }
4772
4510
  }
4773
- ]
4511
+ }
4774
4512
  }
4775
4513
  });
4776
- //#endregion
4777
- //#region ../theme-core/src/buttons/personalities/soft-pill.ts
4778
- /**
4779
- * Soft Pill — rounded-full pill buttons with a hover lift. The default for
4780
- * Calm Studio and Modern Wellness; reads as quietly modern and welcoming.
4781
- */
4782
- const softPill = defineButtonPersonality({
4783
- id: "soft-pill",
4784
- name: "Soft Pill",
4785
- description: "Rounded pills with a gentle lift on hover. Quietly modern.",
4514
+ const joyStampRecipe = defineStaticButtonStyleRecipe({
4515
+ id: "joy-stamp",
4516
+ accessibilityLabel: "Bold pill buttons with a hard offset shadow",
4786
4517
  tags: {
4787
- tone: "classic",
4788
- energy: "calm",
4789
- motion: "subtle",
4518
+ tone: "bold",
4519
+ energy: "vibrant",
4520
+ motion: "expressive",
4790
4521
  era: "organic"
4791
4522
  },
4792
- buttonSystem: {
4523
+ system: {
4793
4524
  global: {
4794
4525
  cornerStyle: "pill",
4795
- shadow: "low",
4526
+ shadow: "high",
4796
4527
  textTransform: "none",
4797
- fontWeight: 500,
4798
- borderWidth: 2,
4799
- hoverTransform: "lift",
4528
+ typography: "heading",
4529
+ fontWeight: 700,
4530
+ fontSize: "text-lg",
4531
+ hoverTransform: "none",
4800
4532
  hoverColor: "none"
4801
4533
  },
4802
- variants: [
4803
- {
4804
- id: "primary",
4805
- name: "Primary",
4806
- enabled: true,
4807
- priority: 1,
4808
- background: {
4809
- type: "solid",
4810
- colorToken: "primary"
4811
- },
4812
- borderRadius: "rounded-full",
4813
- shadow: {
4814
- elevation: "low",
4815
- softness: "soft",
4816
- position: "bottom"
4817
- },
4818
- effects: {
4819
- hover: [{
4820
- effectId: "lift",
4821
- options: { translateY: "-1px" }
4822
- }],
4823
- active: [{
4824
- effectId: "scale-down",
4825
- options: { scale: "0.98" }
4826
- }]
4534
+ sizes: {
4535
+ sm: {
4536
+ padding: "0.625rem 1.25rem",
4537
+ fontSize: "text-base"
4538
+ },
4539
+ md: {
4540
+ padding: "0.875rem 1.75rem",
4541
+ fontSize: "text-lg"
4542
+ },
4543
+ lg: {
4544
+ padding: "1.125rem 2.25rem",
4545
+ fontSize: "text-xl"
4546
+ },
4547
+ xl: {
4548
+ padding: "1.25rem 2.75rem",
4549
+ fontSize: "text-2xl"
4550
+ }
4551
+ },
4552
+ roles: {
4553
+ primary: {
4554
+ kind: "complete",
4555
+ appearance: {
4556
+ background: {
4557
+ type: "solid",
4558
+ colorToken: "primary"
4559
+ },
4560
+ borderRadius: "rounded-full",
4561
+ shadow: {
4562
+ elevation: "high",
4563
+ softness: "hard",
4564
+ position: "bottom-right",
4565
+ colorToken: unsafeAsOpaqueButtonColourToken("text")
4566
+ },
4567
+ effects: { hover: [{ effectId: "press-down" }] }
4827
4568
  }
4828
4569
  },
4829
- {
4830
- id: "secondary",
4831
- name: "Secondary",
4832
- enabled: true,
4833
- priority: 2,
4834
- background: {
4835
- type: "surface",
4836
- role: "link"
4837
- },
4838
- borderRadius: "rounded-full",
4839
- border: { widthClass: "border-2" },
4840
- effects: { hover: [{
4841
- effectId: "background-on-hover",
4842
- options: { colorToken: "surface" }
4843
- }] }
4570
+ secondary: {
4571
+ kind: "complete",
4572
+ appearance: {
4573
+ background: {
4574
+ type: "surface",
4575
+ role: "link"
4576
+ },
4577
+ borderRadius: "rounded-full",
4578
+ border: { widthClass: "border-2" },
4579
+ shadow: {
4580
+ elevation: "high",
4581
+ softness: "hard",
4582
+ position: "bottom-right",
4583
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
4584
+ },
4585
+ effects: { hover: [{ effectId: "press-down" }] }
4586
+ }
4844
4587
  },
4845
- {
4846
- id: "outline",
4847
- name: "Outline",
4848
- enabled: true,
4849
- priority: 3,
4850
- background: {
4851
- type: "surface",
4852
- role: "link"
4853
- },
4854
- borderRadius: "rounded-full",
4855
- border: { widthClass: "border" },
4856
- effects: { hover: [{
4857
- effectId: "background-on-hover",
4858
- options: { colorToken: "surface" }
4859
- }] }
4588
+ outline: {
4589
+ kind: "derive",
4590
+ from: "secondary"
4860
4591
  },
4861
- {
4862
- id: "ghost",
4863
- name: "Ghost",
4864
- enabled: true,
4865
- priority: 3,
4866
- background: {
4867
- type: "surface",
4868
- role: "link"
4869
- },
4870
- borderRadius: "rounded-full",
4871
- effects: { hover: [{
4872
- effectId: "background-on-hover",
4873
- options: { colorToken: "surface" }
4874
- }] }
4592
+ ghost: {
4593
+ kind: "complete",
4594
+ appearance: {
4595
+ background: {
4596
+ type: "surface",
4597
+ role: "link"
4598
+ },
4599
+ borderRadius: "rounded-full",
4600
+ effects: { hover: [{ effectId: "press-down" }] }
4601
+ }
4602
+ },
4603
+ accent: {
4604
+ kind: "derive",
4605
+ from: "primary",
4606
+ overrides: { background: {
4607
+ type: "solid",
4608
+ colorToken: "accent"
4609
+ } }
4875
4610
  }
4876
- ]
4611
+ }
4877
4612
  }
4878
4613
  });
4879
- //#endregion
4880
- //#region ../theme-core/src/buttons/personalities/two-tone-pill.ts
4881
- const solidSecondary = {
4882
- id: "secondary",
4883
- name: "Secondary",
4884
- enabled: true,
4885
- priority: 2,
4886
- background: {
4887
- type: "solid",
4888
- colorToken: unsafeAsOpaqueButtonColourToken("secondary")
4889
- },
4890
- borderRadius: "rounded-full",
4891
- shadow: {
4892
- elevation: "none",
4893
- softness: null,
4894
- position: "bottom"
4614
+ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
4615
+ id: "ornate-ritual",
4616
+ accessibilityLabel: "Uppercase framed buttons with restrained movement",
4617
+ tags: {
4618
+ tone: "classic",
4619
+ energy: "vibrant",
4620
+ motion: "subtle",
4621
+ era: "organic"
4895
4622
  },
4896
- effects: {
4897
- hover: [{
4898
- effectId: "background-on-hover-alpha",
4899
- options: {
4900
- colorToken: "secondary",
4901
- opacity: 88
4623
+ system: {
4624
+ global: {
4625
+ cornerStyle: "rounded",
4626
+ textTransform: "uppercase",
4627
+ fontWeight: 600,
4628
+ hoverTransform: "none",
4629
+ hoverColor: "none",
4630
+ letterSpacing: "loose"
4631
+ },
4632
+ sizes: {
4633
+ sm: {
4634
+ padding: "0.5625rem 1.25rem",
4635
+ fontSize: "text-xs"
4636
+ },
4637
+ md: {
4638
+ padding: "0.75rem 1.75rem",
4639
+ fontSize: "text-xs"
4640
+ },
4641
+ lg: {
4642
+ padding: "0.875rem 2.25rem",
4643
+ fontSize: "text-sm"
4644
+ },
4645
+ xl: {
4646
+ padding: "1rem 2.75rem",
4647
+ fontSize: "text-base"
4902
4648
  }
4903
- }],
4904
- active: [{
4905
- effectId: "scale-down",
4906
- options: { scale: "0.98" }
4907
- }]
4649
+ },
4650
+ roles: {
4651
+ primary: {
4652
+ kind: "complete",
4653
+ appearance: {
4654
+ background: {
4655
+ type: "solid",
4656
+ colorToken: "primary"
4657
+ },
4658
+ borderRadius: "rounded-md",
4659
+ border: { widthClass: "border" },
4660
+ effects: {
4661
+ hover: [{
4662
+ effectId: "background-on-hover",
4663
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("primary-100") }
4664
+ }],
4665
+ active: [{
4666
+ effectId: "scale-down",
4667
+ options: { scale: "0.98" }
4668
+ }]
4669
+ }
4670
+ }
4671
+ },
4672
+ secondary: {
4673
+ kind: "complete",
4674
+ appearance: {
4675
+ background: {
4676
+ type: "solid",
4677
+ colorToken: "secondary"
4678
+ },
4679
+ borderRadius: "rounded-md",
4680
+ border: { widthClass: "border" },
4681
+ effects: { hover: [{
4682
+ effectId: "background-on-hover",
4683
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
4684
+ }] }
4685
+ }
4686
+ },
4687
+ outline: {
4688
+ kind: "complete",
4689
+ appearance: {
4690
+ background: {
4691
+ type: "surface",
4692
+ role: "link"
4693
+ },
4694
+ borderRadius: "rounded-md",
4695
+ border: { widthClass: "border" },
4696
+ effects: { hover: [{
4697
+ effectId: "background-on-hover",
4698
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
4699
+ }] }
4700
+ }
4701
+ },
4702
+ ghost: {
4703
+ kind: "complete",
4704
+ appearance: {
4705
+ background: {
4706
+ type: "surface",
4707
+ role: "link"
4708
+ },
4709
+ borderRadius: "rounded-none",
4710
+ sizes: {
4711
+ sm: {
4712
+ padding: "0.375rem 0",
4713
+ fontSize: "text-xs"
4714
+ },
4715
+ md: {
4716
+ padding: "0.5rem 0",
4717
+ fontSize: "text-sm"
4718
+ },
4719
+ lg: {
4720
+ padding: "0.625rem 0",
4721
+ fontSize: "text-base"
4722
+ },
4723
+ xl: {
4724
+ padding: "0.75rem 0",
4725
+ fontSize: "text-lg"
4726
+ }
4727
+ },
4728
+ effects: { base: [{
4729
+ effectId: "border-bottom-grow",
4730
+ options: {
4731
+ height: 1,
4732
+ origin: "left"
4733
+ }
4734
+ }] }
4735
+ }
4736
+ },
4737
+ accent: {
4738
+ kind: "derive",
4739
+ from: "primary",
4740
+ overrides: { background: {
4741
+ type: "solid",
4742
+ colorToken: "accent"
4743
+ } }
4744
+ }
4745
+ }
4908
4746
  }
4909
- };
4747
+ });
4910
4748
  //#endregion
4911
- //#region ../theme-core/src/buttons/personalities/index.ts
4912
- const buttonPersonalities = [
4913
- softPill,
4914
- defineButtonPersonality({
4915
- id: "two-tone-pill",
4916
- name: "Two-tone Pill",
4917
- description: "Rounded pills with distinct solid primary and secondary brand colours.",
4918
- tags: softPill.tags,
4919
- buttonSystem: {
4920
- ...softPill.buttonSystem,
4921
- variants: softPill.buttonSystem.variants.map((variant) => variant.id === "secondary" ? solidSecondary : variant)
4922
- }
4923
- }),
4924
- earthPill,
4925
- confidentChip,
4926
- editorialLink,
4927
- pebble,
4928
- inkStamp,
4929
- flatInk,
4930
- brushedWash,
4931
- showtimePill,
4932
- joyStamp,
4933
- ornateRitual,
4934
- quietArrow
4749
+ //#region ../theme-core/src/buttons/styles/registry.ts
4750
+ const buttonStyleRecipes = [
4751
+ softPillRecipe,
4752
+ earthPillRecipe,
4753
+ confidentChipRecipe,
4754
+ editorialLinkRecipe,
4755
+ pebbleRecipe,
4756
+ inkStampRecipe,
4757
+ flatInkRecipe,
4758
+ brushedWashRecipe,
4759
+ showtimePillRecipe,
4760
+ joyStampRecipe,
4761
+ ornateRitualRecipe,
4762
+ quietArrowRecipe
4935
4763
  ];
4936
- new Map(buttonPersonalities.map((p) => [p.id, p]));
4937
- buttonPersonalities.map((personality) => ({
4938
- id: personality.id,
4939
- name: personality.name,
4940
- description: personality.description,
4941
- category: presetCategory(personality),
4942
- buttonSystem: personality.buttonSystem,
4943
- compatibility: {
4944
- ...personality.tags.tone ? { tones: [personality.tags.tone] } : {},
4945
- ...personality.tags.energy ? { energies: [personality.tags.energy] } : {}
4946
- }
4947
- }));
4948
- function presetCategory(personality) {
4949
- switch (personality.tags.era) {
4764
+ const recipesById = new Map(buttonStyleRecipes.map((recipe) => [recipe.id, recipe]));
4765
+ function findButtonStyleRecipe(id) {
4766
+ const recipe = recipesById.get(id);
4767
+ if (recipe === void 0) throw new Error(`Button style registry is missing known style ${id}`);
4768
+ return recipe;
4769
+ }
4770
+ function resolveButtonStyleSelection(selection) {
4771
+ switch (selection.styleId) {
4772
+ case "soft-pill": return resolveButtonStyleRecipe(softPillRecipe, selection.parameters);
4773
+ case "earth-pill": return resolveButtonStyleRecipe(earthPillRecipe, selection.parameters);
4774
+ case "confident-chip": return resolveButtonStyleRecipe(confidentChipRecipe, selection.parameters);
4775
+ case "editorial-link": return resolveButtonStyleRecipe(editorialLinkRecipe, selection.parameters);
4776
+ case "pebble": return resolveButtonStyleRecipe(pebbleRecipe, selection.parameters);
4777
+ case "ink-stamp": return resolveButtonStyleRecipe(inkStampRecipe, selection.parameters);
4778
+ case "flat-ink": return resolveButtonStyleRecipe(flatInkRecipe, selection.parameters);
4779
+ case "brushed-wash": return resolveButtonStyleRecipe(brushedWashRecipe, selection.parameters);
4780
+ case "showtime-pill": return resolveButtonStyleRecipe(showtimePillRecipe, selection.parameters);
4781
+ case "joy-stamp": return resolveButtonStyleRecipe(joyStampRecipe, selection.parameters);
4782
+ case "ornate-ritual": return resolveButtonStyleRecipe(ornateRitualRecipe, selection.parameters);
4783
+ case "quiet-arrow": return resolveButtonStyleRecipe(quietArrowRecipe, selection.parameters);
4784
+ }
4785
+ }
4786
+ function defaultButtonStyleSelectionFor(styleId) {
4787
+ return {
4788
+ styleId,
4789
+ parameters: findButtonStyleRecipe(styleId).defaultParameters
4790
+ };
4791
+ }
4792
+ if (buttonStyleRecipes.map((recipe) => recipe.id).length !== BUTTON_STYLE_IDS.length || BUTTON_STYLE_IDS.some((id) => !recipesById.has(id))) throw new Error("Button style registry and ButtonStyleId catalog are out of sync");
4793
+ buttonStyleRecipes.map((recipe) => {
4794
+ const resolved = resolveButtonStyleSelection(defaultButtonStyleSelectionFor(recipe.id));
4795
+ if (resolved.kind === "error") throw new Error(`Invalid registered button style ${recipe.id}: ${resolved.error.issues.join(", ")}`);
4796
+ return {
4797
+ id: recipe.id,
4798
+ name: recipe.accessibilityLabel,
4799
+ description: recipe.accessibilityLabel,
4800
+ category: presetCategory(recipe),
4801
+ buttonSystem: resolved.buttonSystem,
4802
+ compatibility: {
4803
+ ...recipe.tags.tone ? { tones: [recipe.tags.tone] } : {},
4804
+ ...recipe.tags.energy ? { energies: [recipe.tags.energy] } : {}
4805
+ }
4806
+ };
4807
+ });
4808
+ function presetCategory(recipe) {
4809
+ switch (recipe.tags.era) {
4950
4810
  case "retro": return "retro";
4951
4811
  case "editorial": return "minimal";
4952
4812
  case "organic": return "artistic";
4953
- case "modern": return personality.tags.tone === "bold" ? "modern" : "technical";
4813
+ case "modern": return recipe.tags.tone === "bold" ? "modern" : "technical";
4954
4814
  case "technical": return "technical";
4955
4815
  case void 0: return "modern";
4956
4816
  }
@@ -5973,6 +5833,102 @@ new Set(themeV2NormalEditorRawStyleFieldNames);
5973
5833
  const pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
5974
5834
  const siteChromeBackgroundOverrideOwner = "site-chrome-editor";
5975
5835
  //#endregion
5836
+ //#region ../theme-core/src/utils/colorStyles.ts
5837
+ const COLOR_VAR_PREFIX = "--tb-";
5838
+ function parseToken(source) {
5839
+ const parts = parseColorTokenParts(source);
5840
+ if (parts.kind === "raw") return {
5841
+ token: source,
5842
+ raw: parts.value
5843
+ };
5844
+ if (parts.kind === "invalid") return { token: source };
5845
+ return {
5846
+ token: parts.token,
5847
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
5848
+ };
5849
+ }
5850
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
5851
+ text: "text",
5852
+ heading: "heading",
5853
+ mutedText: "supporting"
5854
+ };
5855
+ function surfaceRoleForTextToken(token) {
5856
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
5857
+ }
5858
+ /**
5859
+ * Converts a theme color token to a CSS rgb value.
5860
+ * This is the primary function for applying theme colors to CSS properties.
5861
+ *
5862
+ * @param token - Theme color token (semantic, with opacity, or raw)
5863
+ * @returns CSS color value (rgb with optional alpha, or raw CSS value)
5864
+ *
5865
+ * @example
5866
+ * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
5867
+ * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
5868
+ * rgbColorValue('raw:transparent') // => 'transparent'
5869
+ */
5870
+ function rgbColorValue(token) {
5871
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
5872
+ if (raw) return raw;
5873
+ const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
5874
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
5875
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
5876
+ }
5877
+ /**
5878
+ * Creates a backgroundColor style object from a theme token.
5879
+ *
5880
+ * @param token - Theme color token
5881
+ * @returns CSSProperties with backgroundColor
5882
+ */
5883
+ function backgroundColorStyle(token) {
5884
+ return { backgroundColor: rgbColorValue(token) };
5885
+ }
5886
+ /**
5887
+ * Creates a text color style object from a theme token.
5888
+ *
5889
+ * @param token - Theme color token
5890
+ * @returns CSSProperties with color
5891
+ */
5892
+ function textColorStyle(token) {
5893
+ const parsed = parseToken(token);
5894
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
5895
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
5896
+ return { color: rgbColorValue(token) };
5897
+ }
5898
+ /**
5899
+ * Creates a border color style object from a theme token.
5900
+ *
5901
+ * @param token - Theme color token
5902
+ * @returns CSSProperties with borderColor
5903
+ */
5904
+ function borderColorStyle(token) {
5905
+ return { borderColor: rgbColorValue(token) };
5906
+ }
5907
+ /**
5908
+ * Merges multiple CSSProperties objects into a single object.
5909
+ * Later styles override earlier ones. Undefined values are filtered out.
5910
+ * Returns undefined if the merged result is empty.
5911
+ *
5912
+ * @param styles - Variable number of CSSProperties objects (can be undefined)
5913
+ * @returns Merged CSSProperties object, or undefined if empty
5914
+ *
5915
+ * @example
5916
+ * mergeStyles(
5917
+ * backgroundColorStyle('surface'),
5918
+ * textColorStyle('text'),
5919
+ * borderColorStyle('border')
5920
+ * )
5921
+ * // => { backgroundColor: '...', color: '...', borderColor: '...' }
5922
+ */
5923
+ function mergeStyles(...styles) {
5924
+ const merged = {};
5925
+ for (const style of styles) {
5926
+ if (!style) continue;
5927
+ Object.assign(merged, style);
5928
+ }
5929
+ return Object.keys(merged).length ? merged : void 0;
5930
+ }
5931
+ //#endregion
5976
5932
  //#region ../theme-core/src/utils/tokenOrColor.ts
5977
5933
  /**
5978
5934
  * Runtime color-reference fields intentionally accept any string, including
@@ -9409,309 +9365,77 @@ const headerLookCatalog = defineExactCatalog()([
9409
9365
  },
9410
9366
  {
9411
9367
  id: asHeaderLookId("practical-utility"),
9412
- label: "Practical utility",
9413
- visualTreatment: "solid-neutral",
9414
- layoutBehavior: "sticky-shrink",
9415
- configuration: {
9416
- structure: "inline-classic",
9417
- treatment: "solid-neutral",
9418
- behavior: "sticky-shrink"
9419
- },
9420
- openingRequirement: "none",
9421
- requiresProminentCta: true,
9422
- logoAccommodations: inlineClassicLogoAccommodations,
9423
- compile: practicalUtilityHeader
9424
- },
9425
- {
9426
- id: asHeaderLookId("minimal-transparent"),
9427
- label: "Minimal transparent",
9428
- visualTreatment: "minimal-transparent",
9429
- layoutBehavior: "normal-flow",
9430
- configuration: {
9431
- structure: "inline-classic",
9432
- treatment: "minimal-transparent",
9433
- behavior: "normal-flow"
9434
- },
9435
- openingRequirement: "reserved-space-ok",
9436
- logoAccommodations: inlineClassicLogoAccommodations,
9437
- compile: minimalTransparentHeader
9438
- },
9439
- {
9440
- id: asHeaderLookId("transparent-overlay"),
9441
- label: "Transparent overlay",
9442
- visualTreatment: "transparent-overlay",
9443
- layoutBehavior: "fixed-overlay",
9444
- configuration: {
9445
- structure: "inline-classic",
9446
- treatment: "transparent-overlay",
9447
- behavior: "fixed-overlay"
9448
- },
9449
- openingRequirement: "requires-image-hero-with-contrast-overlay",
9450
- maxRecommendedNavItems: 5,
9451
- logoAccommodations: overlayLogoAccommodations,
9452
- compile: transparentOverlayHeader
9453
- },
9454
- {
9455
- id: asHeaderLookId("floating-glass"),
9456
- label: "Floating glass",
9457
- visualTreatment: "floating-glass",
9458
- layoutBehavior: "fixed-floating",
9459
- configuration: {
9460
- structure: "floating-shell",
9461
- treatment: "floating-glass",
9462
- behavior: "fixed-floating"
9463
- },
9464
- openingRequirement: "requires-image-hero-with-contrast-overlay",
9465
- maxRecommendedNavItems: 5,
9466
- logoAccommodations: overlayLogoAccommodations,
9467
- compile: floatingGlassHeader
9468
- }
9469
- ]);
9470
- new Set(headerLookCatalog.map((look) => headerConfigurationKey(look.configuration)));
9471
- new Map(headerLookCatalog.map((look) => [look.id, look]));
9472
- function headerConfigurationKey(configuration) {
9473
- return `${configuration.structure}:${configuration.treatment}:${configuration.behavior}`;
9474
- }
9475
- //#endregion
9476
- //#region ../theme-core/src/site-styles/footerLooks.ts
9477
- function asFooterLookId(value) {
9478
- return value;
9479
- }
9480
- function asFooterLayoutId(value) {
9481
- return value;
9482
- }
9483
- function asFooterStyleId(value) {
9484
- return value;
9485
- }
9486
- const footerLayoutCatalog = defineExactCatalog()([
9487
- {
9488
- id: asFooterLayoutId("identity"),
9489
- label: "Identity",
9490
- description: "Logo, site text, address, and legal copy without relying on footer navigation."
9491
- },
9492
- {
9493
- id: asFooterLayoutId("centered"),
9494
- label: "Centered",
9495
- description: "A calm centered stack for identity, short copy, and optional compact navigation.",
9496
- maxRecommendedNavDensity: "sparse"
9497
- },
9498
- {
9499
- id: asFooterLayoutId("split"),
9500
- label: "Split",
9501
- description: "Navigation and brand signoff separated into a wider split footer.",
9502
- requiresFooterNav: true,
9503
- maxRecommendedNavDensity: "balanced"
9504
- },
9505
- {
9506
- id: asFooterLayoutId("columns"),
9507
- label: "Columns",
9508
- description: "Grouped footer navigation columns with a supporting bottom line.",
9509
- requiresFooterNav: true,
9510
- requiresFooterNavGroups: true
9511
- }
9512
- ]);
9513
- const footerStyleCatalog = defineExactCatalog()([
9514
- {
9515
- id: asFooterStyleId("quiet"),
9516
- label: "Quiet",
9517
- description: "Lightweight, practical footer treatment."
9518
- },
9519
- {
9520
- id: asFooterStyleId("calm"),
9521
- label: "Calm",
9522
- description: "Soft centered treatment with restrained dividers."
9523
- },
9524
- {
9525
- id: asFooterStyleId("editorial"),
9526
- label: "Editorial",
9527
- description: "Text-forward treatment with fine-line structure."
9528
- },
9529
- {
9530
- id: asFooterStyleId("brand-band"),
9531
- label: "Brand band",
9532
- description: "High-contrast brand treatment with a stronger bottom bar."
9533
- }
9534
- ]);
9535
- new Map(footerLayoutCatalog.map((layout) => [layout.id, layout]));
9536
- new Map(footerStyleCatalog.map((style) => [style.id, style]));
9537
- /**
9538
- * No-nav-safe identity footer: brand and legal/supporting copy carry the
9539
- * footer without implying missing navigation.
9540
- */
9541
- function identityQuietFooter(theme) {
9542
- return {
9543
- ...theme.footer,
9544
- variant: "simple",
9545
- maxWidth: "container",
9546
- background: {
9547
- type: "color",
9548
- color: "surface"
9549
- },
9550
- navStyle: "minimal",
9551
- navWeight: "regular",
9552
- navColor: "text",
9553
- navLinkStyle: {
9554
- typography: "body",
9555
- fontWeight: "regular",
9556
- textTransform: "none",
9557
- italic: false,
9558
- colorToken: "text",
9559
- hoverColorToken: "primary",
9560
- padding: "none",
9561
- borderRadius: "none"
9562
- },
9563
- navLayout: {
9564
- layout: "inline-wrap",
9565
- align: "start",
9566
- gapX: "default",
9567
- gapY: "tight"
9568
- },
9569
- logo: {
9570
- showLogo: true,
9571
- showLogoText: true,
9572
- placement: "left",
9573
- size: "sm",
9574
- align: "start"
9575
- },
9576
- bottomBar: {
9577
- enabled: false,
9578
- fullBleed: false,
9579
- textAlign: "left",
9580
- paddingX: "default",
9581
- paddingY: "compact",
9582
- borderTop: { width: "none" }
9583
- }
9584
- };
9585
- }
9586
- /**
9587
- * Back-compat alias for the previous default footer Look. It keeps existing
9588
- * persisted selections valid while staying hidden from the new layout/style UI.
9589
- */
9590
- function mutedGroundingFooter(theme) {
9591
- return {
9592
- ...identityQuietFooter(theme),
9593
- background: theme.footer.background,
9594
- navStyle: theme.footer.navStyle,
9595
- navColor: theme.footer.navColor,
9596
- navWeight: theme.footer.navWeight,
9597
- navLinkStyle: theme.footer.navLinkStyle,
9598
- bottomTextLinkStyle: theme.footer.bottomTextLinkStyle
9599
- };
9600
- }
9601
- /** Applies a high-contrast brand band with a darker full-bleed bottom bar. */
9602
- function brandTwoBandFooter(theme) {
9603
- return {
9604
- ...theme.footer,
9605
- variant: "split",
9606
- maxWidth: "container",
9607
- background: {
9608
- type: "color",
9609
- color: "primary-800"
9610
- },
9611
- navStyle: "minimal",
9612
- navWeight: "medium",
9613
- navColor: "primaryForeground",
9614
- navLinkStyle: {
9615
- typography: "body",
9616
- fontWeight: "medium",
9617
- textTransform: "none",
9618
- italic: false,
9619
- colorToken: "primaryForeground",
9620
- hoverColorToken: "secondary",
9621
- padding: "none",
9622
- borderRadius: "none"
9623
- },
9624
- bottomTextLinkStyle: {
9625
- colorToken: "secondary",
9626
- decorationColorToken: "secondary"
9627
- },
9628
- navLayout: {
9629
- layout: "inline-wrap",
9630
- align: "start",
9631
- gapX: "default",
9632
- gapY: "tight"
9633
- },
9634
- logo: {
9635
- showLogo: true,
9636
- showLogoText: false,
9637
- placement: "left",
9638
- size: "sm",
9639
- align: "start"
9640
- },
9641
- bottomBar: {
9642
- enabled: true,
9643
- fullBleed: true,
9644
- background: {
9645
- type: "color",
9646
- color: "primary-900"
9647
- },
9648
- textColor: "primaryForeground",
9649
- textAlign: "center",
9650
- paddingX: "default",
9651
- paddingY: "compact",
9652
- borderTop: { width: "none" }
9653
- }
9654
- };
9655
- }
9656
- /**
9657
- * Centered stack on a softly tinted band: logo, inline nav, address, and a
9658
- * hairline-topped copyright line, all centered.
9659
- */
9660
- function centeredCalmFooter(theme) {
9661
- return {
9662
- ...theme.footer,
9663
- variant: "simple",
9664
- maxWidth: "container",
9665
- background: {
9666
- type: "color",
9667
- color: "surfaceAlt"
9368
+ label: "Practical utility",
9369
+ visualTreatment: "solid-neutral",
9370
+ layoutBehavior: "sticky-shrink",
9371
+ configuration: {
9372
+ structure: "inline-classic",
9373
+ treatment: "solid-neutral",
9374
+ behavior: "sticky-shrink"
9668
9375
  },
9669
- navStyle: "minimal",
9670
- navWeight: "regular",
9671
- navColor: "text",
9672
- navLinkStyle: {
9673
- typography: "body",
9674
- fontWeight: "regular",
9675
- textTransform: "none",
9676
- italic: false,
9677
- colorToken: "text",
9678
- hoverColorToken: "primary",
9679
- padding: "none",
9680
- borderRadius: "none"
9376
+ openingRequirement: "none",
9377
+ requiresProminentCta: true,
9378
+ logoAccommodations: inlineClassicLogoAccommodations,
9379
+ compile: practicalUtilityHeader
9380
+ },
9381
+ {
9382
+ id: asHeaderLookId("minimal-transparent"),
9383
+ label: "Minimal transparent",
9384
+ visualTreatment: "minimal-transparent",
9385
+ layoutBehavior: "normal-flow",
9386
+ configuration: {
9387
+ structure: "inline-classic",
9388
+ treatment: "minimal-transparent",
9389
+ behavior: "normal-flow"
9681
9390
  },
9682
- navLayout: {
9683
- layout: "inline-wrap",
9684
- align: "center",
9685
- gapX: "relaxed",
9686
- gapY: "tight"
9391
+ openingRequirement: "reserved-space-ok",
9392
+ logoAccommodations: inlineClassicLogoAccommodations,
9393
+ compile: minimalTransparentHeader
9394
+ },
9395
+ {
9396
+ id: asHeaderLookId("transparent-overlay"),
9397
+ label: "Transparent overlay",
9398
+ visualTreatment: "transparent-overlay",
9399
+ layoutBehavior: "fixed-overlay",
9400
+ configuration: {
9401
+ structure: "inline-classic",
9402
+ treatment: "transparent-overlay",
9403
+ behavior: "fixed-overlay"
9687
9404
  },
9688
- logo: {
9689
- showLogo: true,
9690
- showLogoText: false,
9691
- placement: "above",
9692
- size: "sm",
9693
- align: "center"
9405
+ openingRequirement: "requires-image-hero-with-contrast-overlay",
9406
+ maxRecommendedNavItems: 5,
9407
+ logoAccommodations: overlayLogoAccommodations,
9408
+ compile: transparentOverlayHeader
9409
+ },
9410
+ {
9411
+ id: asHeaderLookId("floating-glass"),
9412
+ label: "Floating glass",
9413
+ visualTreatment: "floating-glass",
9414
+ layoutBehavior: "fixed-floating",
9415
+ configuration: {
9416
+ structure: "floating-shell",
9417
+ treatment: "floating-glass",
9418
+ behavior: "fixed-floating"
9694
9419
  },
9695
- bottomBar: {
9696
- enabled: true,
9697
- fullBleed: false,
9698
- textAlign: "center",
9699
- paddingX: "default",
9700
- paddingY: "compact",
9701
- borderTop: { width: "thin" }
9702
- }
9703
- };
9420
+ openingRequirement: "requires-image-hero-with-contrast-overlay",
9421
+ maxRecommendedNavItems: 5,
9422
+ logoAccommodations: overlayLogoAccommodations,
9423
+ compile: floatingGlassHeader
9424
+ }
9425
+ ]);
9426
+ new Set(headerLookCatalog.map((look) => headerConfigurationKey(look.configuration)));
9427
+ new Map(headerLookCatalog.map((look) => [look.id, look]));
9428
+ function headerConfigurationKey(configuration) {
9429
+ return `${configuration.structure}:${configuration.treatment}:${configuration.behavior}`;
9430
+ }
9431
+ //#endregion
9432
+ //#region ../theme-core/src/site-styles/footerLooks.ts
9433
+ function asFooterLookId(value) {
9434
+ return value;
9704
9435
  }
9705
- /** Editorial split footer: compact nav on one side, brand signoff on the other. */
9706
- function splitEditorialFooter(theme) {
9436
+ function quietFooterLinks(theme) {
9707
9437
  return {
9708
9438
  ...theme.footer,
9709
- variant: "split",
9710
- maxWidth: "container",
9711
- background: {
9712
- type: "color",
9713
- color: "surface"
9714
- },
9715
9439
  navStyle: "minimal",
9716
9440
  navWeight: "regular",
9717
9441
  navColor: "text",
@@ -9724,120 +9448,38 @@ function splitEditorialFooter(theme) {
9724
9448
  hoverColorToken: "primary",
9725
9449
  padding: "none",
9726
9450
  borderRadius: "none"
9727
- },
9728
- navLayout: {
9729
- layout: "inline-wrap",
9730
- align: "start",
9731
- gapX: "relaxed",
9732
- gapY: "tight"
9733
- },
9734
- logo: {
9735
- showLogo: true,
9736
- showLogoText: true,
9737
- placement: "right",
9738
- size: "sm",
9739
- align: "end"
9740
- },
9741
- bottomBar: {
9742
- enabled: true,
9743
- fullBleed: false,
9744
- textAlign: "left",
9745
- paddingX: "default",
9746
- paddingY: "compact",
9747
- borderTop: { width: "thin" }
9748
9451
  }
9749
9452
  };
9750
9453
  }
9751
- /**
9752
- * Editorial columns: site name and supporting copy on the left, inline nav on
9753
- * the right, with a hairline-topped bottom line. Uses the footer block's
9754
- * columns layout.
9755
- */
9756
- function editorialColumnsFooter(theme) {
9454
+ function emphasizedFooterLinks(theme) {
9757
9455
  return {
9758
9456
  ...theme.footer,
9759
- variant: "columns",
9760
- maxWidth: "container",
9761
- background: {
9762
- type: "color",
9763
- color: "surface"
9764
- },
9765
9457
  navStyle: "minimal",
9766
- navWeight: "regular",
9458
+ navWeight: "medium",
9767
9459
  navColor: "text",
9768
9460
  navLinkStyle: {
9769
9461
  typography: "body",
9770
- fontWeight: "regular",
9462
+ fontWeight: "medium",
9771
9463
  textTransform: "none",
9772
9464
  italic: false,
9773
9465
  colorToken: "text",
9774
9466
  hoverColorToken: "primary",
9775
9467
  padding: "none",
9776
9468
  borderRadius: "none"
9777
- },
9778
- logo: {
9779
- showLogo: true,
9780
- showLogoText: true,
9781
- placement: "left",
9782
- size: "sm",
9783
- align: "start"
9784
- },
9785
- bottomBar: {
9786
- enabled: true,
9787
- fullBleed: false,
9788
- textAlign: "center",
9789
- paddingX: "default",
9790
- paddingY: "compact",
9791
- borderTop: { width: "thin" }
9792
9469
  }
9793
9470
  };
9794
9471
  }
9795
- const footerLookCatalog = defineExactCatalog()([
9796
- {
9797
- id: asFooterLookId("footer-identity-quiet"),
9798
- label: "Identity footer",
9799
- layoutId: asFooterLayoutId("identity"),
9800
- styleId: asFooterStyleId("quiet"),
9801
- compile: identityQuietFooter
9802
- },
9803
- {
9804
- id: asFooterLookId("footer-muted-grounding"),
9805
- label: "Muted grounding footer",
9806
- layoutId: asFooterLayoutId("identity"),
9807
- styleId: asFooterStyleId("quiet"),
9808
- hiddenFromLayoutStyleControls: true,
9809
- compile: mutedGroundingFooter
9810
- },
9811
- {
9812
- id: asFooterLookId("footer-centered-calm"),
9813
- label: "Centered calm footer",
9814
- layoutId: asFooterLayoutId("centered"),
9815
- styleId: asFooterStyleId("calm"),
9816
- compile: centeredCalmFooter
9817
- },
9818
- {
9819
- id: asFooterLookId("footer-split-editorial"),
9820
- label: "Split editorial footer",
9821
- layoutId: asFooterLayoutId("split"),
9822
- styleId: asFooterStyleId("editorial"),
9823
- compile: splitEditorialFooter
9824
- },
9825
- {
9826
- id: asFooterLookId("footer-editorial-columns"),
9827
- label: "Editorial columns footer",
9828
- layoutId: asFooterLayoutId("columns"),
9829
- styleId: asFooterStyleId("editorial"),
9830
- compile: editorialColumnsFooter
9831
- },
9832
- {
9833
- id: asFooterLookId("site-footer-brand-two-band"),
9834
- label: "Two-band brand footer",
9835
- layoutId: asFooterLayoutId("split"),
9836
- styleId: asFooterStyleId("brand-band"),
9837
- allowedContentPresence: ["default-footer", "default-and-blocks"],
9838
- compile: brandTwoBandFooter
9839
- }
9840
- ]);
9472
+ const footerLookCatalog = defineExactCatalog()([{
9473
+ id: asFooterLookId("footer-links-quiet"),
9474
+ label: "Quiet links",
9475
+ description: "Regular-weight footer navigation with a restrained hover.",
9476
+ compile: quietFooterLinks
9477
+ }, {
9478
+ id: asFooterLookId("footer-links-emphasized"),
9479
+ label: "Emphasized links",
9480
+ description: "Medium-weight footer navigation with a clearer emphasis.",
9481
+ compile: emphasizedFooterLinks
9482
+ }]);
9841
9483
  new Map(footerLookCatalog.map((look) => [look.id, look]));
9842
9484
  //#endregion
9843
9485
  //#region ../theme-core/src/site-styles/sourceCatalogs.ts
@@ -10966,17 +10608,21 @@ function defineCuratedSiteStyle(input) {
10966
10608
  })),
10967
10609
  recommendedHeaderLookIds: legacyHeaderLookIds.map(asHeaderLookId),
10968
10610
  recommendedFooterLookIds: (input.recommendedFooterLookIds ?? []).map(asFooterLookId),
10969
- buttonPersonalityChoices: input.buttonPersonalityChoices,
10611
+ buttonStyleChoices: input.buttonStyleChoices,
10970
10612
  paletteRecommendations: input.paletteRecommendations,
10971
10613
  typography: input.typography ?? defaultTypographyForRecipe(input.tokenRecipes.typography),
10972
10614
  ...input.heroTypography ? { heroTypography: input.heroTypography } : {}
10973
10615
  };
10974
10616
  }
10975
- function buttonChoice(id, note) {
10617
+ function buttonChoice(styleId, note, parameters) {
10618
+ const selection = parameters === void 0 ? defaultButtonStyleSelectionFor(styleId) : buttonStyleSelectionSchema.parse({
10619
+ styleId,
10620
+ parameters
10621
+ });
10976
10622
  return note ? {
10977
- id: asButtonPersonalityId(id),
10623
+ ...selection,
10978
10624
  note
10979
- } : { id: asButtonPersonalityId(id) };
10625
+ } : selection;
10980
10626
  }
10981
10627
  function paletteRecommendation(authoringKey, note) {
10982
10628
  const valueKey = requireCuratedPaletteValueKey(asCuratedPaletteAuthoringKey(authoringKey));
@@ -11161,9 +10807,8 @@ const curatedSiteStyles = [
11161
10807
  "minimal-transparent",
11162
10808
  "clean-base"
11163
10809
  ],
11164
- buttonPersonalityChoices: [
10810
+ buttonStyleChoices: [
11165
10811
  buttonChoice("soft-pill"),
11166
- buttonChoice("two-tone-pill"),
11167
10812
  buttonChoice("pebble"),
11168
10813
  buttonChoice("editorial-link"),
11169
10814
  buttonChoice("brushed-wash")
@@ -11199,7 +10844,7 @@ const curatedSiteStyles = [
11199
10844
  "brand-solid",
11200
10845
  "clean-base"
11201
10846
  ],
11202
- buttonPersonalityChoices: [
10847
+ buttonStyleChoices: [
11203
10848
  buttonChoice("confident-chip"),
11204
10849
  buttonChoice("soft-pill"),
11205
10850
  buttonChoice("editorial-link")
@@ -11238,7 +10883,7 @@ const curatedSiteStyles = [
11238
10883
  "minimal-transparent",
11239
10884
  "centered-calm"
11240
10885
  ],
11241
- buttonPersonalityChoices: [
10886
+ buttonStyleChoices: [
11242
10887
  buttonChoice("editorial-link"),
11243
10888
  buttonChoice("soft-pill"),
11244
10889
  buttonChoice("brushed-wash")
@@ -11278,7 +10923,7 @@ const curatedSiteStyles = [
11278
10923
  "practical-utility",
11279
10924
  "clean-base"
11280
10925
  ],
11281
- buttonPersonalityChoices: [
10926
+ buttonStyleChoices: [
11282
10927
  buttonChoice("pebble"),
11283
10928
  buttonChoice("brushed-wash"),
11284
10929
  buttonChoice("confident-chip")
@@ -11339,7 +10984,7 @@ const curatedSiteStyles = [
11339
10984
  "serif-line",
11340
10985
  "clean-base"
11341
10986
  ],
11342
- buttonPersonalityChoices: [
10987
+ buttonStyleChoices: [
11343
10988
  buttonChoice("editorial-link"),
11344
10989
  buttonChoice("confident-chip"),
11345
10990
  buttonChoice("soft-pill")
@@ -11401,7 +11046,7 @@ const curatedSiteStyles = [
11401
11046
  "brand-solid",
11402
11047
  "clean-base"
11403
11048
  ],
11404
- buttonPersonalityChoices: [
11049
+ buttonStyleChoices: [
11405
11050
  buttonChoice("confident-chip"),
11406
11051
  buttonChoice("pebble"),
11407
11052
  buttonChoice("ink-stamp")
@@ -11440,7 +11085,7 @@ const curatedSiteStyles = [
11440
11085
  "minimal-transparent",
11441
11086
  "clean-base"
11442
11087
  ],
11443
- buttonPersonalityChoices: [
11088
+ buttonStyleChoices: [
11444
11089
  buttonChoice("soft-pill"),
11445
11090
  buttonChoice("pebble"),
11446
11091
  buttonChoice("confident-chip")
@@ -11513,7 +11158,7 @@ const curatedSiteStyles = [
11513
11158
  "clean-base",
11514
11159
  "centered-calm"
11515
11160
  ],
11516
- buttonPersonalityChoices: [
11161
+ buttonStyleChoices: [
11517
11162
  buttonChoice("pebble"),
11518
11163
  buttonChoice("soft-pill"),
11519
11164
  buttonChoice("editorial-link")
@@ -11632,12 +11277,8 @@ const curatedSiteStyles = [
11632
11277
  "serif-line",
11633
11278
  "minimal-transparent"
11634
11279
  ],
11635
- recommendedFooterLookIds: [
11636
- "footer-centered-calm",
11637
- "footer-editorial-columns",
11638
- "footer-muted-grounding"
11639
- ],
11640
- buttonPersonalityChoices: [
11280
+ recommendedFooterLookIds: ["footer-links-quiet"],
11281
+ buttonStyleChoices: [
11641
11282
  buttonChoice("soft-pill"),
11642
11283
  buttonChoice("pebble"),
11643
11284
  buttonChoice("editorial-link")
@@ -11711,7 +11352,7 @@ const curatedSiteStyles = [
11711
11352
  "centered-calm",
11712
11353
  "minimal-transparent"
11713
11354
  ],
11714
- buttonPersonalityChoices: [
11355
+ buttonStyleChoices: [
11715
11356
  buttonChoice("editorial-link"),
11716
11357
  buttonChoice("soft-pill"),
11717
11358
  buttonChoice("brushed-wash")
@@ -11784,8 +11425,8 @@ const curatedSiteStyles = [
11784
11425
  "practical-utility",
11785
11426
  "clean-base"
11786
11427
  ],
11787
- recommendedFooterLookIds: ["site-footer-brand-two-band", "footer-muted-grounding"],
11788
- buttonPersonalityChoices: [
11428
+ recommendedFooterLookIds: ["footer-links-emphasized", "footer-links-quiet"],
11429
+ buttonStyleChoices: [
11789
11430
  buttonChoice("pebble"),
11790
11431
  buttonChoice("confident-chip"),
11791
11432
  buttonChoice("ink-stamp"),
@@ -11906,8 +11547,8 @@ const curatedSiteStyles = [
11906
11547
  "minimal-transparent",
11907
11548
  "serif-line"
11908
11549
  ],
11909
- recommendedFooterLookIds: ["footer-muted-grounding", "site-footer-brand-two-band"],
11910
- buttonPersonalityChoices: [
11550
+ recommendedFooterLookIds: ["footer-links-quiet", "footer-links-emphasized"],
11551
+ buttonStyleChoices: [
11911
11552
  buttonChoice("earth-pill"),
11912
11553
  buttonChoice("soft-pill"),
11913
11554
  buttonChoice("pebble"),
@@ -11978,7 +11619,7 @@ const curatedSiteStyles = [
11978
11619
  "floating-glass",
11979
11620
  "brand-solid"
11980
11621
  ],
11981
- buttonPersonalityChoices: [
11622
+ buttonStyleChoices: [
11982
11623
  buttonChoice("ink-stamp"),
11983
11624
  buttonChoice("confident-chip"),
11984
11625
  buttonChoice("brushed-wash"),
@@ -12033,8 +11674,8 @@ const curatedSiteStyles = [
12033
11674
  "transparent-overlay",
12034
11675
  "practical-utility"
12035
11676
  ],
12036
- recommendedFooterLookIds: ["site-footer-brand-two-band", "footer-muted-grounding"],
12037
- buttonPersonalityChoices: [
11677
+ recommendedFooterLookIds: ["footer-links-emphasized", "footer-links-quiet"],
11678
+ buttonStyleChoices: [
12038
11679
  buttonChoice("showtime-pill"),
12039
11680
  buttonChoice("confident-chip"),
12040
11681
  buttonChoice("ink-stamp"),
@@ -12115,8 +11756,8 @@ const curatedSiteStyles = [
12115
11756
  headerStyleChoice("glass")
12116
11757
  ] },
12117
11758
  recommendedHeaderLookIds: ["minimal-transparent"],
12118
- recommendedFooterLookIds: ["site-footer-brand-two-band", "footer-muted-grounding"],
12119
- buttonPersonalityChoices: [
11759
+ recommendedFooterLookIds: ["footer-links-emphasized", "footer-links-quiet"],
11760
+ buttonStyleChoices: [
12120
11761
  buttonChoice("ornate-ritual"),
12121
11762
  buttonChoice("joy-stamp"),
12122
11763
  buttonChoice("ink-stamp")
@@ -12169,8 +11810,8 @@ const curatedSiteStyles = [
12169
11810
  headerStyleChoice("glass")
12170
11811
  ] },
12171
11812
  recommendedHeaderLookIds: ["minimal-transparent"],
12172
- recommendedFooterLookIds: ["site-footer-brand-two-band", "footer-muted-grounding"],
12173
- buttonPersonalityChoices: [
11813
+ recommendedFooterLookIds: ["footer-links-emphasized", "footer-links-quiet"],
11814
+ buttonStyleChoices: [
12174
11815
  buttonChoice("joy-stamp"),
12175
11816
  buttonChoice("ink-stamp"),
12176
11817
  buttonChoice("showtime-pill")
@@ -12260,8 +11901,8 @@ const curatedSiteStyles = [
12260
11901
  "centered-calm",
12261
11902
  "clean-base"
12262
11903
  ],
12263
- recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
12264
- buttonPersonalityChoices: [
11904
+ recommendedFooterLookIds: ["footer-links-quiet"],
11905
+ buttonStyleChoices: [
12265
11906
  buttonChoice("flat-ink"),
12266
11907
  buttonChoice("confident-chip"),
12267
11908
  buttonChoice("editorial-link")
@@ -12338,14 +11979,9 @@ const curatedSiteStyles = [
12338
11979
  "serif-line",
12339
11980
  "clean-base"
12340
11981
  ],
12341
- recommendedFooterLookIds: [
12342
- "footer-editorial-columns",
12343
- "footer-muted-grounding",
12344
- "footer-identity-quiet"
12345
- ],
12346
- buttonPersonalityChoices: [
12347
- buttonChoice("quiet-arrow"),
12348
- buttonChoice("two-tone-pill"),
11982
+ recommendedFooterLookIds: ["footer-links-quiet"],
11983
+ buttonStyleChoices: [
11984
+ buttonChoice("quiet-arrow", void 0, { secondaryTreatment: "solid-accent" }),
12349
11985
  buttonChoice("flat-ink"),
12350
11986
  buttonChoice("editorial-link")
12351
11987
  ],
@@ -13286,6 +12922,7 @@ const DISPLAY_MEDIA_KINDS = [
13286
12922
  "video",
13287
12923
  "audio"
13288
12924
  ];
12925
+ const DEFAULT_MEDIA_FIELD_KINDS = ["image"];
13289
12926
  const PUBLIC_FILE_FIELD_KINDS = [
13290
12927
  "document",
13291
12928
  "spreadsheet",
@@ -13486,18 +13123,28 @@ const inlineTextFieldSchema = baseFieldSchema.extend({
13486
13123
  maxLength: z$1.number().int().positive().optional(),
13487
13124
  plainTextField: z$1.string().min(1).optional()
13488
13125
  });
13126
+ function hasMediaDefaultIdentifier(value) {
13127
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
13128
+ }
13129
+ function validateMediaFieldDefault(field, ctx) {
13130
+ if (field.defaultValue !== void 0 && field.defaultValue !== null && !hasMediaDefaultIdentifier(field.defaultValue)) ctx.addIssue({
13131
+ code: z$1.ZodIssueCode.custom,
13132
+ path: ["defaultValue"],
13133
+ message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
13134
+ });
13135
+ }
13489
13136
  const mediaFieldSchema = baseFieldSchema.extend({
13490
13137
  type: z$1.literal("media"),
13491
- mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default(["image"]).transform((kinds) => {
13138
+ mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default([...DEFAULT_MEDIA_FIELD_KINDS]).transform((kinds) => {
13492
13139
  const displayKinds = kinds.filter(isDisplayMediaKind);
13493
- return displayKinds.length > 0 ? displayKinds : ["image"];
13140
+ return displayKinds.length > 0 ? displayKinds : [...DEFAULT_MEDIA_FIELD_KINDS];
13494
13141
  }),
13495
13142
  aspectRatio: z$1.string().optional()
13496
- });
13143
+ }).superRefine(validateMediaFieldDefault);
13497
13144
  const fileFieldSchema = baseFieldSchema.extend({
13498
13145
  type: z$1.literal("file"),
13499
13146
  fileKinds: z$1.array(z$1.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
13500
- });
13147
+ }).superRefine(validateMediaFieldDefault);
13501
13148
  const booleanFieldSchema = baseFieldSchema.extend({ type: z$1.literal("boolean") });
13502
13149
  const dateFieldSchema = baseFieldSchema.extend({ type: z$1.literal("date") });
13503
13150
  const timeFieldSchema = baseFieldSchema.extend({ type: z$1.literal("time") });
@@ -16291,25 +15938,41 @@ const heroLayoutControlsFragment = defineFragment({
16291
15938
  layout: []
16292
15939
  });
16293
15940
  //#endregion
15941
+ //#region ../blocks/src/system/constants/buttonRoles.ts
15942
+ /**
15943
+ * Semantic button roles available to block authors.
15944
+ *
15945
+ * These roles are deliberately independent of a site's visual button style:
15946
+ * the active theme owns how each role looks.
15947
+ */
15948
+ const BLOCK_BUTTON_ROLES = PUBLIC_BUTTON_ROLES;
15949
+ const blockButtonRoleSchema = z$1.enum(BLOCK_BUTTON_ROLES);
15950
+ /**
15951
+ * Temporary rollout boundary for persisted content written before issue #1220.
15952
+ * Authoring remains strict through `blockButtonRoleSchema`; this parser exists
15953
+ * only until the post-deploy role rewrite has been audited in production.
15954
+ */
15955
+ function canonicalizePersistedBlockButtonRole(value) {
15956
+ return value === "link" || value === "tertiary" ? "ghost" : value;
15957
+ }
15958
+ const persistedBlockButtonRoleSchema = z$1.preprocess((value) => typeof value === "string" ? canonicalizePersistedBlockButtonRole(value) : value, blockButtonRoleSchema);
15959
+ const BLOCK_BUTTON_ROLE_LABELS = {
15960
+ primary: "Primary",
15961
+ secondary: "Secondary",
15962
+ outline: "Outline",
15963
+ ghost: "Ghost",
15964
+ accent: "Accent"
15965
+ };
15966
+ const BLOCK_BUTTON_ROLE_OPTIONS = BLOCK_BUTTON_ROLES.map((value) => ({
15967
+ value,
15968
+ label: BLOCK_BUTTON_ROLE_LABELS[value]
15969
+ }));
15970
+ function isBlockButtonRole(value) {
15971
+ return blockButtonRoleSchema.safeParse(value).success;
15972
+ }
15973
+ //#endregion
16294
15974
  //#region ../blocks/src/system/fields/button.ts
16295
- const DEFAULT_THEME_BUTTON_VARIANTS = [
16296
- {
16297
- value: "primary",
16298
- label: "Primary"
16299
- },
16300
- {
16301
- value: "secondary",
16302
- label: "Secondary"
16303
- },
16304
- {
16305
- value: "outline",
16306
- label: "Outline"
16307
- },
16308
- {
16309
- value: "link",
16310
- label: "Link"
16311
- }
16312
- ];
15975
+ const DEFAULT_THEME_BUTTON_VARIANTS = BLOCK_BUTTON_ROLE_OPTIONS;
16313
15976
  function createButtonVariantField(options = {}) {
16314
15977
  return {
16315
15978
  id: options.id ?? "buttonVariant",
@@ -16327,20 +15990,7 @@ function createButtonVariantField(options = {}) {
16327
15990
  };
16328
15991
  }
16329
15992
  function createButtonGroup(options = {}) {
16330
- const { variants = [
16331
- {
16332
- value: "primary",
16333
- label: "Primary"
16334
- },
16335
- {
16336
- value: "secondary",
16337
- label: "Secondary"
16338
- },
16339
- {
16340
- value: "outline",
16341
- label: "Outline"
16342
- }
16343
- ], defaultVariant, includeSize = true, defaultSize = "md", showGroupLabel = false, groupId = "button", groupLabel = "Button", flattenInRepeater = true } = options;
15993
+ const { variants = BLOCK_BUTTON_ROLE_OPTIONS, defaultVariant, includeSize = true, defaultSize = "md", showGroupLabel = false, groupId = "button", groupLabel = "Button", flattenInRepeater = true } = options;
16344
15994
  const mainFields = [
16345
15995
  {
16346
15996
  id: "label",
@@ -16476,29 +16126,7 @@ const ctaRowFragment = defineFragment({
16476
16126
  fields: [createCtasRepeater({
16477
16127
  label: "Calls to action",
16478
16128
  itemLabel: "CTA",
16479
- maxItems: 3,
16480
- button: { variants: [
16481
- {
16482
- value: "primary",
16483
- label: "Primary"
16484
- },
16485
- {
16486
- value: "secondary",
16487
- label: "Secondary"
16488
- },
16489
- {
16490
- value: "outline",
16491
- label: "Outline"
16492
- },
16493
- {
16494
- value: "link",
16495
- label: "Link"
16496
- },
16497
- {
16498
- value: "accent",
16499
- label: "Accent"
16500
- }
16501
- ] }
16129
+ maxItems: 3
16502
16130
  })],
16503
16131
  layout: [ctaRow({
16504
16132
  collectionPath: "content.ctas",
@@ -16517,10 +16145,6 @@ const heroCtaRowFragment = defineFragment({
16517
16145
  defaultValue: [],
16518
16146
  button: {
16519
16147
  variants: [
16520
- {
16521
- value: "default",
16522
- label: "Default"
16523
- },
16524
16148
  {
16525
16149
  value: "primary",
16526
16150
  label: "Primary"
@@ -16532,9 +16156,17 @@ const heroCtaRowFragment = defineFragment({
16532
16156
  {
16533
16157
  value: "outline",
16534
16158
  label: "Outline"
16159
+ },
16160
+ {
16161
+ value: "ghost",
16162
+ label: "Ghost"
16163
+ },
16164
+ {
16165
+ value: "accent",
16166
+ label: "Accent"
16535
16167
  }
16536
16168
  ],
16537
- defaultVariant: "default",
16169
+ defaultVariant: "primary",
16538
16170
  includeSize: true,
16539
16171
  defaultSize: "lg"
16540
16172
  }
@@ -17225,11 +16857,13 @@ const blogFeaturedPostFragment = defineFragment({
17225
16857
  as: "p",
17226
16858
  style: textColorStyle("text/80")
17227
16859
  }, bind("post.excerpt", { fallback: "Select a blog post to display its preview." })),
17228
- link({
17229
- className: "rb-inline-flex rb-items-center rb-caption rb-hover-opacity-80",
17230
- style: textColorStyle("primary"),
17231
- href: { $bind: { from: "post.path" } }
17232
- }, [text({ as: "strong" }, props({ value: "Read more →" }))])
16860
+ button({
16861
+ className: "rb-self-start",
16862
+ href: { $bind: { from: "post.path" } },
16863
+ variantId: "ghost",
16864
+ size: "sm",
16865
+ linkRequired: true
16866
+ }, [text({ as: "span" }, props({ value: "Read more" }))])
17233
16867
  ])], when("post")), stack({
17234
16868
  gap: "sm",
17235
16869
  className: "rb-rounded-xl rb-border rb-border-dashed rb-p-6",
@@ -17500,21 +17134,7 @@ const newsletterFormFragment = defineFragment({
17500
17134
  createButtonVariantField({
17501
17135
  id: "buttonVariant",
17502
17136
  label: "Button style",
17503
- defaultValue: "primary",
17504
- variants: [
17505
- {
17506
- value: "primary",
17507
- label: "Primary"
17508
- },
17509
- {
17510
- value: "secondary",
17511
- label: "Secondary"
17512
- },
17513
- {
17514
- value: "outline",
17515
- label: "Outline"
17516
- }
17517
- ]
17137
+ defaultValue: "primary"
17518
17138
  }),
17519
17139
  {
17520
17140
  id: "successMessage",
@@ -19063,7 +18683,8 @@ function postExcerpt(className) {
19063
18683
  function postReadMore() {
19064
18684
  return button({
19065
18685
  href: { $bind: { from: "post.path" } },
19066
- className: "rb-self-start"
18686
+ className: "rb-self-start",
18687
+ variantId: "ghost"
19067
18688
  }, [text({ as: "span" }, bind("content.readMoreText", { fallback: "Read more" }), when("content.readMoreText")), text({
19068
18689
  as: "span",
19069
18690
  value: "Read more"
@@ -21338,31 +20959,7 @@ const featureCardsBlockDefinition = { manifest: createBlockManifest({
21338
20959
  label: "Calls to action",
21339
20960
  itemLabel: "CTA",
21340
20961
  maxItems: 1,
21341
- button: {
21342
- variants: [
21343
- {
21344
- value: "primary",
21345
- label: "Primary"
21346
- },
21347
- {
21348
- value: "secondary",
21349
- label: "Secondary"
21350
- },
21351
- {
21352
- value: "outline",
21353
- label: "Outline"
21354
- },
21355
- {
21356
- value: "ghost",
21357
- label: "Ghost"
21358
- },
21359
- {
21360
- value: "link",
21361
- label: "Link"
21362
- }
21363
- ],
21364
- defaultVariant: "ghost"
21365
- }
20962
+ button: { defaultVariant: "ghost" }
21366
20963
  })
21367
20964
  ] }
21368
20965
  },
@@ -22335,11 +21932,8 @@ const mediaSchema = z.discriminatedUnion("type", [
22335
21932
  * This module provides a single source of truth for TipTap rich text validation
22336
21933
  * across both the block-form editor and the block rendering system.
22337
21934
  *
22338
- * Format compatibility:
22339
- * - Unwrapped format (new): { type: 'doc', content: [...] }
22340
- * - Wrapped format (legacy): { doc: { type: 'doc', content: [...] } }
22341
- *
22342
- * Both formats are supported for backward compatibility during migration.
21935
+ * Rich-text fields use one canonical root shape:
21936
+ * `{ type: 'doc', content: [...] }`.
22343
21937
  */
22344
21938
  /**
22345
21939
  * TipTap document node schema (recursive structure)
@@ -22352,17 +21946,40 @@ const mediaSchema = z.discriminatedUnion("type", [
22352
21946
  * - marks: Text formatting marks (e.g., bold, italic)
22353
21947
  */
22354
21948
  const TipTapMarkSchema = z$1.object({
22355
- type: z$1.string(),
21949
+ type: z$1.string().min(1),
22356
21950
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional()
22357
- });
21951
+ }).strict();
22358
21952
  const TipTapNodeSchema = z$1.lazy(() => z$1.object({
22359
- type: z$1.string(),
21953
+ type: z$1.string().min(1),
22360
21954
  content: z$1.array(TipTapNodeSchema).optional(),
22361
21955
  text: z$1.string().optional(),
22362
21956
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional(),
22363
21957
  marks: z$1.array(TipTapMarkSchema).optional()
21958
+ }).strict().superRefine((node, ctx) => {
21959
+ if (node.type === "doc") ctx.addIssue({
21960
+ code: "custom",
21961
+ path: ["type"],
21962
+ message: "Document nodes are only valid at the rich-text root"
21963
+ });
21964
+ if (node.type === "text") {
21965
+ if (node.text === void 0 || node.text.length === 0) ctx.addIssue({
21966
+ code: "custom",
21967
+ path: ["text"],
21968
+ message: "Text nodes require a non-empty text value"
21969
+ });
21970
+ if (node.content !== void 0) ctx.addIssue({
21971
+ code: "custom",
21972
+ path: ["content"],
21973
+ message: "Text nodes cannot contain child nodes"
21974
+ });
21975
+ return;
21976
+ }
21977
+ if (node.text !== void 0) ctx.addIssue({
21978
+ code: "custom",
21979
+ path: ["text"],
21980
+ message: "Only text nodes can contain a text value"
21981
+ });
22364
21982
  }));
22365
- z$1.union([TipTapNodeSchema, z$1.object({ doc: TipTapNodeSchema })]);
22366
21983
  /**
22367
21984
  * Canonical schema for a TipTap rich-text document root.
22368
21985
  *
@@ -22371,13 +21988,11 @@ z$1.union([TipTapNodeSchema, z$1.object({ doc: TipTapNodeSchema })]);
22371
21988
  * (dummy content, editor output, migrations) must emit, and the shape every
22372
21989
  * non-legacy consumer can rely on.
22373
21990
  *
22374
- * Legacy envelopes are read-tolerated by {@link RichTextValueSchema} but
22375
- * normalised before reaching anywhere typed as `RichTextDoc`.
22376
21991
  */
22377
21992
  const RichTextDocSchema = z$1.object({
22378
21993
  type: z$1.literal("doc"),
22379
21994
  content: z$1.array(TipTapNodeSchema)
22380
- });
21995
+ }).strict();
22381
21996
  function createHardBreakNode() {
22382
21997
  return { type: "hardBreak" };
22383
21998
  }
@@ -23525,17 +23140,8 @@ function readAmount(value) {
23525
23140
  //#endregion
23526
23141
  //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
23527
23142
  const previewStageSchema = z$1.enum(["preview", "published"]);
23528
- const registrationButtonVariantSchema = z$1.enum([
23529
- "primary",
23530
- "secondary",
23531
- "outline"
23532
- ]);
23533
- const eventButtonVariantSchema = z$1.enum([
23534
- "primary",
23535
- "secondary",
23536
- "outline",
23537
- "link"
23538
- ]);
23143
+ const registrationButtonVariantSchema = blockButtonRoleSchema;
23144
+ const eventButtonVariantSchema = persistedBlockButtonRoleSchema;
23539
23145
  const eventLayoutSchema = z$1.enum(eventCalendarListLayoutValues);
23540
23146
  const eventCalendarDisplayModeSchema = z$1.enum(eventCalendarDisplayModeValues);
23541
23147
  const eventCalendarDefaultViewSchema = z$1.enum(eventCalendarDefaultViewValues);
@@ -23900,23 +23506,7 @@ const eventRegistrationBlockDefinition = {
23900
23506
  defaultValue: "Complete Registration",
23901
23507
  maxLength: 40
23902
23508
  },
23903
- createButtonVariantField({
23904
- description: "Choose a button style from your theme",
23905
- variants: [
23906
- {
23907
- value: "primary",
23908
- label: "Primary"
23909
- },
23910
- {
23911
- value: "secondary",
23912
- label: "Secondary"
23913
- },
23914
- {
23915
- value: "outline",
23916
- label: "Outline"
23917
- }
23918
- ]
23919
- })
23509
+ createButtonVariantField()
23920
23510
  ],
23921
23511
  layout: [styledSection({
23922
23512
  baseClass: "",
@@ -24223,23 +23813,7 @@ const courseRegistrationBlockDefinition = {
24223
23813
  defaultValue: "Sign up",
24224
23814
  maxLength: 40
24225
23815
  },
24226
- createButtonVariantField({
24227
- description: "Choose a button style from your theme",
24228
- variants: [
24229
- {
24230
- value: "primary",
24231
- label: "Primary"
24232
- },
24233
- {
24234
- value: "secondary",
24235
- label: "Secondary"
24236
- },
24237
- {
24238
- value: "outline",
24239
- label: "Outline"
24240
- }
24241
- ]
24242
- })
23816
+ createButtonVariantField()
24243
23817
  ],
24244
23818
  layout: [styledSection({
24245
23819
  baseClass: "",
@@ -27104,36 +26678,7 @@ const offeringsPreviewFields = [
27104
26678
  ui: { intent: "content" },
27105
26679
  schema: { fields: groupOverrideFields }
27106
26680
  },
27107
- {
27108
- id: "buttonVariant",
27109
- type: "select",
27110
- label: "Button style",
27111
- required: false,
27112
- multiple: false,
27113
- defaultValue: "primary",
27114
- options: [
27115
- {
27116
- value: "primary",
27117
- label: "Primary"
27118
- },
27119
- {
27120
- value: "secondary",
27121
- label: "Secondary"
27122
- },
27123
- {
27124
- value: "outline",
27125
- label: "Outline"
27126
- },
27127
- {
27128
- value: "link",
27129
- label: "Link"
27130
- }
27131
- ],
27132
- ui: {
27133
- intent: "display",
27134
- widget: "buttonVariant"
27135
- }
27136
- },
26681
+ createButtonVariantField(),
27137
26682
  {
27138
26683
  id: "buttonText",
27139
26684
  type: "text",
@@ -27339,7 +26884,7 @@ function normalizeOfferingsPreviewGroupOverride(value) {
27339
26884
  return [override];
27340
26885
  }
27341
26886
  function isButtonVariant(value) {
27342
- return value === "primary" || value === "secondary" || value === "outline" || value === "link" || value === null;
26887
+ return value === null || isBlockButtonRole(value);
27343
26888
  }
27344
26889
  function normalizeCardStylesContent(value) {
27345
26890
  return isRecord$1(value) || value === null ? value : void 0;
@@ -29191,12 +28736,7 @@ const shopSourceSchema = z$1.object({
29191
28736
  showPrices: z$1.boolean().optional(),
29192
28737
  showValidityPeriod: z$1.boolean().optional(),
29193
28738
  showPricePerCredit: z$1.boolean().nullable().optional(),
29194
- buttonVariant: z$1.enum([
29195
- "primary",
29196
- "secondary",
29197
- "outline",
29198
- "link"
29199
- ]).optional(),
28739
+ buttonVariant: blockButtonRoleSchema.optional(),
29200
28740
  passesHeading: z$1.string().optional(),
29201
28741
  membershipsHeading: z$1.string().optional(),
29202
28742
  buyButtonText: z$1.string().optional(),
@@ -29341,12 +28881,7 @@ const passesMembershipsHydrationSchema = z$1.object({
29341
28881
  passes: z$1.array(publicPassProductSchema),
29342
28882
  memberships: z$1.array(publicMembershipProductSchema),
29343
28883
  passPurchaseMode: z$1.enum(["add_to_cart", "buy_now"]),
29344
- buttonVariant: z$1.enum([
29345
- "primary",
29346
- "secondary",
29347
- "outline",
29348
- "link"
29349
- ])
28884
+ buttonVariant: blockButtonRoleSchema
29350
28885
  });
29351
28886
  const productListHydrationSchema = z$1.object({
29352
28887
  products: z$1.array(publicProductSchema),
@@ -30670,11 +30205,7 @@ const newsletterPresetSchema = z$1.enum([
30670
30205
  "emailNameCompany"
30671
30206
  ]);
30672
30207
  const newsletterDestinationProviderSchema = z$1.enum(["riverbank", "mailchimp"]);
30673
- const newsletterButtonVariantSchema = z$1.enum([
30674
- "primary",
30675
- "secondary",
30676
- "outline"
30677
- ]);
30208
+ const newsletterButtonVariantSchema = blockButtonRoleSchema;
30678
30209
  const newsletterFieldSchema = z$1.object({
30679
30210
  id: z$1.string(),
30680
30211
  type: z$1.enum(["email", "text"]),
@@ -35209,17 +34740,12 @@ function stringToTipTapDoc(text) {
35209
34740
  * Handles:
35210
34741
  * - Plain strings → TipTap doc with paragraphs
35211
34742
  * - Already valid TipTap docs → pass through
35212
- * - Legacy { doc: {...} } wrapper → unwrap
35213
34743
  */
35214
34744
  function coerceRichTextDefaultValue(value) {
35215
34745
  if (value === null || value === void 0) return null;
35216
- if (value && typeof value === "object" && "doc" in value) {
35217
- const doc = value.doc;
35218
- if (doc && typeof doc === "object" && "type" in doc) return doc;
35219
- }
35220
34746
  if (typeof value === "string") return stringToTipTapDoc(value);
35221
- if (value && typeof value === "object" && "type" in value) return value;
35222
- return null;
34747
+ const parsed = RichTextDocSchema.safeParse(value);
34748
+ return parsed.success ? parsed.data : null;
35223
34749
  }
35224
34750
  /**
35225
34751
  * Check if a Pass 1 field is a repeater field.
@@ -36294,7 +35820,7 @@ ${fieldList}
36294
35820
 
36295
35821
  1. **Text fields**: Extract plain text content, strip HTML tags
36296
35822
  2. **RichText fields**: Preserve structure as simplified JSON (type: "doc", content: [...])
36297
- 3. **Media fields**: Extract src URL, alt text → { src: "...", alt: "..." }
35823
+ 3. **Media fields**: Use only an existing CMS media identifier → { type: "image", identifier: "...", alt: "..." }. Never emit a URL-only media object or invent an identifier; omit the field if no identifier is available.
36298
35824
  4. **Link fields**: Extract href, label → { href: "...", label: "..." }
36299
35825
  5. **Repeater fields**: Create array of objects with item field values
36300
35826
  6. **Select fields**: Match to one of the allowed options