@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
@@ -1,6 +1,346 @@
1
1
  import z, { z as z$1 } from "zod";
2
2
  import * as fs from "fs/promises";
3
3
  import * as path from "path";
4
+ //#region ../theme-core/src/palette/utils/colorConversion.ts
5
+ const clamp01 = (x) => Math.max(0, Math.min(1, x));
6
+ const mod = (a, n) => (a % n + n) % n;
7
+ const deg = (x) => mod(x, 360);
8
+ function hexToRgb(hex) {
9
+ const h = hex.replace(/^#/, "");
10
+ const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
11
+ if (m.length !== 6) throw new Error("Bad hex");
12
+ const int = parseInt(m, 16);
13
+ return {
14
+ r: int >> 16 & 255,
15
+ g: int >> 8 & 255,
16
+ b: int & 255
17
+ };
18
+ }
19
+ function rgbToHex(r, g, b) {
20
+ const to = (n) => Math.round(n).toString(16).padStart(2, "0");
21
+ return `#${to(r)}${to(g)}${to(b)}`;
22
+ }
23
+ function srgbToLinear(u) {
24
+ const x = u / 255;
25
+ return x <= .04045 ? x / 12.92 : Math.pow((x + .055) / 1.055, 2.4);
26
+ }
27
+ function linearToSrgb(x) {
28
+ return clamp01(x <= .0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - .055) * 255;
29
+ }
30
+ function rgbToHsl(r, g, b) {
31
+ const R = r / 255, G = g / 255, B = b / 255;
32
+ const max = Math.max(R, G, B), min = Math.min(R, G, B);
33
+ const d = max - min;
34
+ let h = 0;
35
+ if (d !== 0) {
36
+ switch (max) {
37
+ case R:
38
+ h = (G - B) / d + (G < B ? 6 : 0);
39
+ break;
40
+ case G:
41
+ h = (B - R) / d + 2;
42
+ break;
43
+ case B:
44
+ h = (R - G) / d + 4;
45
+ break;
46
+ }
47
+ h *= 60;
48
+ }
49
+ const l = (max + min) / 2;
50
+ const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
51
+ return {
52
+ h,
53
+ s,
54
+ l
55
+ };
56
+ }
57
+ function srgbToOklab(r, g, b) {
58
+ const R = srgbToLinear(r), G = srgbToLinear(g), B = srgbToLinear(b);
59
+ const l = .4122214708 * R + .5363325363 * G + .0514459929 * B;
60
+ const m = .2119034982 * R + .6806995451 * G + .1073969566 * B;
61
+ const s = .0883024619 * R + .2817188376 * G + .6299787005 * B;
62
+ const l_ = Math.cbrt(l), m_ = Math.cbrt(m), s_ = Math.cbrt(s);
63
+ return {
64
+ L: .2104542553 * l_ + .793617785 * m_ - .0040720468 * s_,
65
+ a: 1.9779984951 * l_ - 2.428592205 * m_ + .4505937099 * s_,
66
+ b: .0259040371 * l_ + .7827717662 * m_ - .808675766 * s_
67
+ };
68
+ }
69
+ function oklabToSrgb(L, a, b) {
70
+ const l_ = Math.pow(L + .3963377774 * a + .2158037573 * b, 3);
71
+ const m_ = Math.pow(L - .1055613458 * a - .0638541728 * b, 3);
72
+ const s_ = Math.pow(L - .0894841775 * a - 1.291485548 * b, 3);
73
+ const R = 4.0767416621 * l_ - 3.3077115913 * m_ + .2309699292 * s_;
74
+ const G = -1.2684380046 * l_ + 2.6097574011 * m_ - .3413193965 * s_;
75
+ const B = -.0041960863 * l_ - .7034186147 * m_ + 1.707614701 * s_;
76
+ return {
77
+ r: linearToSrgb(R),
78
+ g: linearToSrgb(G),
79
+ b: linearToSrgb(B)
80
+ };
81
+ }
82
+ function oklabToOklch(L, a, b) {
83
+ const C = Math.sqrt(a * a + b * b);
84
+ return {
85
+ L,
86
+ C,
87
+ h: C < 1e-7 ? 0 : deg(Math.atan2(b, a) * 180 / Math.PI)
88
+ };
89
+ }
90
+ function oklchToOklab(L, C, h) {
91
+ const hr = h * Math.PI / 180;
92
+ return {
93
+ L,
94
+ a: C * Math.cos(hr),
95
+ b: C * Math.sin(hr)
96
+ };
97
+ }
98
+ function inSRGB(r, g, b) {
99
+ return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
100
+ }
101
+ function oklchToHexGamut(L, C, h) {
102
+ let lo = 0, hi = C, best = 0;
103
+ for (let i = 0; i < 18; i++) {
104
+ const mid = (lo + hi) / 2;
105
+ const { a, b } = oklchToOklab(L, mid, h);
106
+ const { r, g, b: bb } = oklabToSrgb(L, a, b);
107
+ if (inSRGB(r, g, bb)) {
108
+ best = mid;
109
+ lo = mid;
110
+ } else hi = mid;
111
+ }
112
+ const { a, b } = oklchToOklab(L, best, h);
113
+ const { r, g, b: bb } = oklabToSrgb(L, a, b);
114
+ return rgbToHex(r, g, bb);
115
+ }
116
+ function featuresFromHex(hex) {
117
+ const { r, g, b } = hexToRgb(hex);
118
+ const { h, s, l } = rgbToHsl(r, g, b);
119
+ const { L, a, b: b2 } = srgbToOklab(r, g, b);
120
+ const { L: okL, C: okC, h: okH } = oklabToOklch(L, a, b2);
121
+ return {
122
+ r,
123
+ g,
124
+ b,
125
+ h,
126
+ s,
127
+ l,
128
+ v: Math.max(r, g, b) / 255,
129
+ Y: .2126 * srgbToLinear(r) + .7152 * srgbToLinear(g) + .0722 * srgbToLinear(b),
130
+ chroma: s,
131
+ okL,
132
+ okC,
133
+ okH,
134
+ hex
135
+ };
136
+ }
137
+ //#endregion
138
+ //#region ../theme-core/src/palette/neutralScale.ts
139
+ function generateNeutralScale({ background, steps = 11, warm = false }) {
140
+ const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
141
+ const shadeNames = [
142
+ 50,
143
+ 100,
144
+ 200,
145
+ 300,
146
+ 400,
147
+ 500,
148
+ 600,
149
+ 700,
150
+ 800,
151
+ 900,
152
+ 950
153
+ ];
154
+ const count = Math.min(steps, shadeNames.length);
155
+ const scale = [];
156
+ for (let i = 0; i < count; i++) {
157
+ const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
158
+ const t = i / (count - 1);
159
+ const hex = oklchToHexGamut(isDark ? mix(.06, .92, t) : mix(.98, .1, t), .02, warm ? 85 : 250);
160
+ const name = `neutral-${shadeName}`;
161
+ scale.push({
162
+ name,
163
+ hex
164
+ });
165
+ }
166
+ return scale;
167
+ }
168
+ function mix(a, b, t) {
169
+ return a + (b - a) * Math.max(0, Math.min(1, t));
170
+ }
171
+ //#endregion
172
+ //#region ../theme-core/src/palette/utils/contrast.ts
173
+ function relativeLuminance(r, g, b) {
174
+ const R = srgbToLinear(r), G = srgbToLinear(g), B = srgbToLinear(b);
175
+ return .2126 * R + .7152 * G + .0722 * B;
176
+ }
177
+ function contrastRatio(hex1, hex2) {
178
+ const { r: r1, g: g1, b: b1 } = hexToRgb(hex1);
179
+ const { r: r2, g: g2, b: b2 } = hexToRgb(hex2);
180
+ const L1 = relativeLuminance(r1, g1, b1);
181
+ const L2 = relativeLuminance(r2, g2, b2);
182
+ const [hi, lo] = L1 >= L2 ? [L1, L2] : [L2, L1];
183
+ return (hi + .05) / (lo + .05);
184
+ }
185
+ //#endregion
186
+ //#region ../theme-core/src/palette/utils/textColor.ts
187
+ /**
188
+ * Pick the better of black/white text for a given background using actual
189
+ * contrast ratios, not a simple luminance cutoff.
190
+ *
191
+ * @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
192
+ * @param minContrast - Preferred minimum contrast ratio for body-sized text
193
+ * @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
194
+ */
195
+ function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
196
+ const { r, g, b } = parseColorToRgb(background);
197
+ const luminance = relativeLuminance(r, g, b);
198
+ const blackContrast = (luminance + .05) / .05;
199
+ const whiteContrast = 1.05 / (luminance + .05);
200
+ if (blackContrast >= minContrast && whiteContrast < minContrast) return "#000000";
201
+ if (whiteContrast >= minContrast && blackContrast < minContrast) return "#FFFFFF";
202
+ return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
203
+ }
204
+ /**
205
+ * Backwards-compatible alias for callers that want the best black/white text
206
+ * color for a background.
207
+ */
208
+ function getTextColorForBackground(background) {
209
+ return pickBlackOrWhiteTextColor(background);
210
+ }
211
+ function parseColorToRgb(input) {
212
+ const trimmed = input.trim();
213
+ if (trimmed.startsWith("#")) return hexToRgb(trimmed);
214
+ const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
215
+ if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) return {
216
+ r: clamp255(Number(rgbMatch[1])),
217
+ g: clamp255(Number(rgbMatch[2])),
218
+ b: clamp255(Number(rgbMatch[3]))
219
+ };
220
+ throw new Error("Unsupported color format");
221
+ }
222
+ function clamp255(value) {
223
+ if (Number.isNaN(value)) return 0;
224
+ return Math.max(0, Math.min(255, value));
225
+ }
226
+ //#endregion
227
+ //#region ../theme-core/src/palette/utils/onColor.ts
228
+ const BACKGROUND_ROLE_NAMES = /* @__PURE__ */ new Set([
229
+ "primary",
230
+ "secondary",
231
+ "accent",
232
+ "success",
233
+ "warning",
234
+ "destructive",
235
+ "info",
236
+ "background",
237
+ "surface",
238
+ "surfaceAlt",
239
+ "muted",
240
+ "neutral"
241
+ ]);
242
+ function onColorTokenName(backgroundToken) {
243
+ return `on${backgroundToken.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
244
+ }
245
+ function isOnColorBackgroundCandidateToken(token) {
246
+ const role = token.match(/^(.*?)(?:-\d{2,3})?$/)?.[1] ?? token;
247
+ return BACKGROUND_ROLE_NAMES.has(role);
248
+ }
249
+ //#endregion
250
+ //#region ../theme-core/src/palette/generateShades.ts
251
+ function generateShades(hexColor, name) {
252
+ const shadeSteps = [
253
+ 50,
254
+ 100,
255
+ 200,
256
+ 300,
257
+ 400,
258
+ 500,
259
+ 600,
260
+ 700,
261
+ 800,
262
+ 900,
263
+ 950
264
+ ];
265
+ const base = featuresFromHex(hexColor);
266
+ const hue = base.okH;
267
+ const baseChroma = base.okC;
268
+ const toneMap = {
269
+ 50: .975,
270
+ 100: .945,
271
+ 200: .89,
272
+ 300: .82,
273
+ 400: .74,
274
+ 500: .66,
275
+ 600: .58,
276
+ 700: .49,
277
+ 800: .4,
278
+ 900: .28,
279
+ 950: .16
280
+ };
281
+ const shadeDefs = shadeSteps.map((step, idx) => {
282
+ const t = idx / (shadeSteps.length - 1);
283
+ const L = toneMap[step];
284
+ const C = baseChroma * (.06 + .94 * Math.sin(Math.PI * t));
285
+ return {
286
+ name: `${name}-${step}`,
287
+ hex: oklchToHexGamut(L, C, hue)
288
+ };
289
+ });
290
+ shadeDefs.push({
291
+ name,
292
+ hex: hexColor
293
+ });
294
+ return shadeDefs;
295
+ }
296
+ //#endregion
297
+ //#region ../theme-core/src/palette/expandPalette.ts
298
+ function colorsToMap(palette) {
299
+ const map = {};
300
+ for (const c of palette.colors) map[c.name] = c.hex;
301
+ return map;
302
+ }
303
+ function isBackgroundDark(hex) {
304
+ try {
305
+ return featuresFromHex(hex).Y < .5;
306
+ } catch {
307
+ return false;
308
+ }
309
+ }
310
+ const nonShadeablePaletteRoleNames = /* @__PURE__ */ new Set([
311
+ "success",
312
+ "warning",
313
+ "destructive"
314
+ ]);
315
+ function expandPalette(palette) {
316
+ const base = colorsToMap(palette);
317
+ const expanded = { ...base };
318
+ generateNeutralScale({
319
+ background: base.background || base.surface || "#ffffff",
320
+ steps: 11,
321
+ warm: false
322
+ }).forEach((n) => {
323
+ expanded[n.name] = n.hex;
324
+ });
325
+ for (const [name, hex] of Object.entries(base)) {
326
+ if (nonShadeablePaletteRoleNames.has(name)) continue;
327
+ if (hex) generateShades(hex, name).forEach((shade) => {
328
+ expanded[shade.name] = shade.hex;
329
+ });
330
+ }
331
+ if (base.text) expanded.body = base.text;
332
+ if (base.danger && !base.destructive) expanded.destructive = base.danger;
333
+ const isDark = base.background ? isBackgroundDark(base.background) : false;
334
+ expanded.mutedText = palette.meta?.mode === "dark" || isDark ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
335
+ for (const [token, hex] of Object.entries({ ...expanded })) {
336
+ if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
337
+ const onToken = onColorTokenName(token);
338
+ if (expanded[onToken]) continue;
339
+ expanded[onToken] = getTextColorForBackground(hex);
340
+ }
341
+ return expanded;
342
+ }
343
+ //#endregion
4
344
  //#region ../theme-core/src/tokens/colorTokenParts.ts
5
345
  function parseOpacityPercent(source) {
6
346
  const trimmed = source.trim();
@@ -138,39 +478,6 @@ function parseThemeColorToken(value) {
138
478
  }
139
479
  const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
140
480
  //#endregion
141
- //#region ../theme-core/src/site-styles/styleTags.ts
142
- /**
143
- * Descriptive style-axis metadata for button personalities and palette
144
- * variants. Site Style picker shape is curated explicitly; these tags remain
145
- * for catalog audit, docs, and future discovery tooling.
146
- */
147
- const STYLE_AXIS_VALUES = {
148
- tone: [
149
- "minimal",
150
- "classic",
151
- "bold"
152
- ],
153
- energy: ["calm", "vibrant"],
154
- motion: [
155
- "subtle",
156
- "standard",
157
- "expressive"
158
- ],
159
- era: [
160
- "editorial",
161
- "modern",
162
- "organic",
163
- "technical",
164
- "retro"
165
- ]
166
- };
167
- const styleTagsSchema = z$1.object({
168
- tone: z$1.enum(STYLE_AXIS_VALUES.tone).optional(),
169
- energy: z$1.enum(STYLE_AXIS_VALUES.energy).optional(),
170
- motion: z$1.enum(STYLE_AXIS_VALUES.motion).optional(),
171
- era: z$1.enum(STYLE_AXIS_VALUES.era).optional()
172
- });
173
- //#endregion
174
481
  //#region ../theme-core/src/lib/media.ts
175
482
  const hotspotSchema = z.object({
176
483
  x: z.number().min(0).max(1),
@@ -228,111 +535,6 @@ const mediaSchema = z.object({
228
535
  transform: transformSchema.optional()
229
536
  });
230
537
  //#endregion
231
- //#region ../theme-core/src/buttons/effectClassification.ts
232
- /**
233
- * Exhaustive live policy for every registered effect id.
234
- *
235
- * The resolver consumes the transition payload directly, so adding an effect
236
- * cannot update classification without also defining its runtime behavior.
237
- */
238
- const BUTTON_EFFECT_POLICIES = {
239
- "darken-background": {
240
- classification: "colour-transition",
241
- transition: {
242
- kind: "adjust-shade",
243
- direction: "darker"
244
- }
245
- },
246
- "lighten-background": {
247
- classification: "colour-transition",
248
- transition: {
249
- kind: "adjust-shade",
250
- direction: "lighter"
251
- }
252
- },
253
- "background-on-hover": {
254
- classification: "colour-transition",
255
- transition: { kind: "replace-background" }
256
- },
257
- "background-on-hover-alpha": {
258
- classification: "unsupported-colour",
259
- reason: "alpha_requires_surface"
260
- },
261
- "fade-background": {
262
- classification: "unsupported-colour",
263
- reason: "changes_element_opacity"
264
- },
265
- "opacity-reduce": {
266
- classification: "unsupported-colour",
267
- reason: "changes_element_opacity"
268
- },
269
- "gradient-text": {
270
- classification: "unsupported-colour",
271
- reason: "changes_foreground"
272
- },
273
- "text-color-change": {
274
- classification: "unsupported-colour",
275
- reason: "changes_foreground"
276
- },
277
- "border-color-change": {
278
- classification: "unsupported-colour",
279
- reason: "changes_border"
280
- },
281
- "aurora-glow": {
282
- classification: "unsupported-background-overlay",
283
- reason: "unresolved_background_overlay"
284
- },
285
- "cosmic-stardust": {
286
- classification: "unsupported-background-overlay",
287
- reason: "unresolved_background_overlay"
288
- },
289
- "gradient-border-fill": {
290
- classification: "unsupported-background-overlay",
291
- reason: "unresolved_background_overlay"
292
- },
293
- "animated-gradient-border-fill": {
294
- classification: "unsupported-background-overlay",
295
- reason: "unresolved_background_overlay"
296
- },
297
- "nav-underline": {
298
- classification: "not-button",
299
- reason: "not_button_effect"
300
- },
301
- "nav-frosted-base": {
302
- classification: "not-button",
303
- reason: "not_button_effect"
304
- },
305
- "nav-frosted-hover": {
306
- classification: "not-button",
307
- reason: "not_button_effect"
308
- },
309
- "nav-frosted-active": {
310
- classification: "not-button",
311
- reason: "not_button_effect"
312
- },
313
- "animated-gradient": { classification: "non-colour" },
314
- "border-glow": { classification: "non-colour" },
315
- "drop-shadow": { classification: "non-colour" },
316
- "inner-shadow": { classification: "non-colour" },
317
- "neumorphic-shadow": { classification: "non-colour" },
318
- "outer-glow": { classification: "non-colour" },
319
- "retro-3d-shadow": { classification: "non-colour" },
320
- "scale-up": { classification: "non-colour" },
321
- "scale-down": { classification: "non-colour" },
322
- lift: { classification: "non-colour" },
323
- "press-down": { classification: "non-colour" },
324
- pop: { classification: "non-colour" },
325
- "border-bottom-grow": { classification: "non-colour" },
326
- "sketchy-border": { classification: "non-colour" },
327
- "pulse-animation": { classification: "non-colour" }
328
- };
329
- function getButtonEffectPolicy(effectId) {
330
- return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
331
- }
332
- function classifyButtonEffectId(effectId) {
333
- return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
334
- }
335
- //#endregion
336
538
  //#region ../theme-core/src/buttons/types.ts
337
539
  /**
338
540
  * Standard semantic variant roles a theme exposes.
@@ -349,6 +551,13 @@ const VARIANT_ROLES = [
349
551
  "tertiary",
350
552
  "accent"
351
553
  ];
554
+ /**
555
+ * Canonical persisted/CSS-safe button variant identifier.
556
+ *
557
+ * IDs become class selectors, so keep the contract deliberately narrower than
558
+ * an arbitrary string and shared with every authoring/runtime boundary.
559
+ */
560
+ const buttonVariantIdSchema = z$1.string().min(1).max(50).regex(/^[a-z0-9-]+$/, "Button variant IDs may contain lowercase letters, numbers, and hyphens only");
352
561
  function parseOpaqueButtonColourToken(value) {
353
562
  const token = parseThemeColorToken(value);
354
563
  if (token === null) return null;
@@ -363,6 +572,109 @@ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => pa
363
572
  if (token === null) throw new Error("Invariant: opaque button token passed its schema");
364
573
  return token;
365
574
  });
575
+ const buttonShadeAmountSchema = z$1.number().finite().min(0).max(900).multipleOf(100);
576
+ const buttonShadowIntensitySchema = z$1.enum([
577
+ "subtle",
578
+ "medium",
579
+ "strong"
580
+ ]);
581
+ /**
582
+ * The complete persisted button-effect contract.
583
+ *
584
+ * Navigation has its own generic interactive-effect model. Button variants use
585
+ * this closed union so unsupported effect IDs, unknown option keys, and unsafe
586
+ * CSS values cannot become representable after the schema boundary.
587
+ */
588
+ const buttonEffectApplicationSchema = z$1.discriminatedUnion("effectId", [
589
+ z$1.strictObject({
590
+ effectId: z$1.literal("darken-background"),
591
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
592
+ }),
593
+ z$1.strictObject({
594
+ effectId: z$1.literal("lighten-background"),
595
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
596
+ }),
597
+ z$1.strictObject({
598
+ effectId: z$1.literal("background-on-hover"),
599
+ options: z$1.strictObject({ colorToken: opaqueButtonColourTokenSchema.optional() }).optional()
600
+ }),
601
+ z$1.strictObject({
602
+ effectId: z$1.literal("animated-gradient"),
603
+ options: z$1.strictObject({ direction: z$1.enum(["horizontal", "vertical"]).optional() }).optional()
604
+ }),
605
+ z$1.strictObject({
606
+ effectId: z$1.literal("drop-shadow"),
607
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
608
+ }),
609
+ z$1.strictObject({
610
+ effectId: z$1.literal("inner-shadow"),
611
+ options: z$1.strictObject({}).optional()
612
+ }),
613
+ z$1.strictObject({
614
+ effectId: z$1.literal("neumorphic-shadow"),
615
+ options: z$1.strictObject({
616
+ mode: z$1.enum(["raised", "pressed"]).optional(),
617
+ intensity: buttonShadowIntensitySchema.optional()
618
+ }).optional()
619
+ }),
620
+ z$1.strictObject({
621
+ effectId: z$1.literal("retro-3d-shadow"),
622
+ options: z$1.strictObject({}).optional()
623
+ }),
624
+ z$1.strictObject({
625
+ effectId: z$1.literal("scale-up"),
626
+ options: z$1.strictObject({ scale: z$1.enum([
627
+ "1.02",
628
+ "1.03",
629
+ "1.05",
630
+ "1.10"
631
+ ]).optional() }).optional()
632
+ }),
633
+ z$1.strictObject({
634
+ effectId: z$1.literal("scale-down"),
635
+ options: z$1.strictObject({ scale: z$1.enum([
636
+ "0.90",
637
+ "0.95",
638
+ "0.97",
639
+ "0.98"
640
+ ]).optional() }).optional()
641
+ }),
642
+ z$1.strictObject({
643
+ effectId: z$1.literal("lift"),
644
+ options: z$1.strictObject({ amount: z$1.enum([
645
+ "0.25",
646
+ "0.5",
647
+ "1"
648
+ ]).optional() }).optional()
649
+ }),
650
+ z$1.strictObject({
651
+ effectId: z$1.literal("press-down"),
652
+ options: z$1.strictObject({}).optional()
653
+ }),
654
+ z$1.strictObject({
655
+ effectId: z$1.literal("pop"),
656
+ options: z$1.strictObject({ intensity: z$1.enum([
657
+ "subtle",
658
+ "medium",
659
+ "dramatic"
660
+ ]).optional() }).optional()
661
+ }),
662
+ z$1.strictObject({
663
+ effectId: z$1.literal("border-bottom-grow"),
664
+ options: z$1.strictObject({
665
+ height: z$1.union([z$1.literal(1), z$1.literal(2)]).optional(),
666
+ origin: z$1.enum([
667
+ "left",
668
+ "center",
669
+ "right"
670
+ ]).optional()
671
+ }).optional()
672
+ }),
673
+ z$1.strictObject({
674
+ effectId: z$1.literal("sketchy-border"),
675
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
676
+ })
677
+ ]);
366
678
  const cornerStyleSchema = z$1.enum([
367
679
  "square",
368
680
  "rounded",
@@ -397,13 +709,7 @@ const hoverTransformSchema = z$1.enum([
397
709
  "scale",
398
710
  "press"
399
711
  ]);
400
- const hoverColorSchema = z$1.enum([
401
- "none",
402
- "brighten",
403
- "darken",
404
- "saturate",
405
- "token"
406
- ]);
712
+ z$1.enum(["none", "token"]);
407
713
  const buttonPaddingPresetSchema = z$1.enum([
408
714
  "compact",
409
715
  "default",
@@ -464,7 +770,7 @@ const buttonSizesSchema = z$1.object({
464
770
  lg,
465
771
  xl: xl ?? lg
466
772
  }));
467
- const canonicalButtonGlobalSettingsSchema = z$1.object({
773
+ const buttonGlobalSettingsBaseSchema = z$1.strictObject({
468
774
  cornerStyle: cornerStyleSchema,
469
775
  cornerRadius: z$1.number().min(0).max(24).optional(),
470
776
  /**
@@ -477,30 +783,23 @@ const canonicalButtonGlobalSettingsSchema = z$1.object({
477
783
  shadow: shadowSizeSchema.optional(),
478
784
  textTransform: textTransformSchema,
479
785
  fontWeight: fontWeightSchema,
480
- /**
481
- * @deprecated Set per-variant via `variant.border.widthClass` instead.
482
- * Schema field is preserved so existing presets parse; the value is unread
483
- * by the current generator and will be removed once the legacy
484
- * `primitives/content/components/Button.tsx` slot exports are deleted.
485
- */
486
- borderWidth: z$1.union([
487
- z$1.literal(1),
488
- z$1.literal(2),
489
- z$1.literal(3)
490
- ]),
491
786
  typography: buttonTypographySchema.optional(),
492
787
  fontSize: z$1.string().optional(),
493
788
  letterSpacing: letterSpacingSchema.optional(),
494
789
  italic: z$1.boolean().optional(),
495
790
  paddingPreset: buttonPaddingPresetSchema.optional(),
496
791
  hoverTransform: hoverTransformSchema,
497
- hoverColor: hoverColorSchema,
498
- hoverColorToken: opaqueButtonColourTokenSchema.optional(),
499
792
  gradientStyle: gradientStyleSchema.optional(),
500
793
  gradientAngle: z$1.number().min(0).max(360).optional(),
501
794
  gradientSharpness: gradientSharpnessSchema.optional()
502
795
  });
503
- const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
796
+ const buttonGlobalSettingsSchema = z$1.discriminatedUnion("hoverColor", [buttonGlobalSettingsBaseSchema.extend({
797
+ hoverColor: z$1.literal("none"),
798
+ hoverColorToken: z$1.never().optional()
799
+ }), buttonGlobalSettingsBaseSchema.extend({
800
+ hoverColor: z$1.literal("token"),
801
+ hoverColorToken: opaqueButtonColourTokenSchema
802
+ })]);
504
803
  const gradientDirectionSchema$1 = z$1.enum([
505
804
  "to-r",
506
805
  "to-l",
@@ -511,12 +810,12 @@ const gradientDirectionSchema$1 = z$1.enum([
511
810
  "to-br",
512
811
  "to-bl"
513
812
  ]);
514
- const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
515
- z$1.object({
813
+ const buttonColourTreatmentSchema = z$1.discriminatedUnion("type", [
814
+ z$1.strictObject({
516
815
  type: z$1.literal("solid"),
517
816
  colorToken: opaqueButtonColourTokenSchema
518
817
  }),
519
- z$1.object({
818
+ z$1.strictObject({
520
819
  type: z$1.literal("gradient"),
521
820
  stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
522
821
  const [first, second, ...rest] = stops;
@@ -530,18 +829,15 @@ const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
530
829
  direction: gradientDirectionSchema$1,
531
830
  foregroundToken: opaqueButtonColourTokenSchema
532
831
  }),
533
- z$1.object({
832
+ z$1.strictObject({
534
833
  type: z$1.literal("surface"),
535
834
  role: z$1.literal("link")
536
835
  })
537
836
  ]);
538
- const effectApplicationSchema$1 = z$1.object({
539
- effectId: z$1.string(),
540
- options: z$1.record(z$1.string(), z$1.json()).optional()
541
- });
542
- const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
543
- const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
544
- const variantShadowSchema = z$1.object({
837
+ const buttonBackgroundSchema = buttonColourTreatmentSchema;
838
+ const buttonBorderSchema = z$1.strictObject({ widthClass: z$1.string() });
839
+ const buttonVariantAffordanceSchema = z$1.strictObject({ iconId: z$1.string().min(1) });
840
+ const variantShadowSchema = z$1.strictObject({
545
841
  elevation: z$1.enum([
546
842
  "none",
547
843
  "low",
@@ -554,13 +850,13 @@ const variantShadowSchema = z$1.object({
554
850
  "hard"
555
851
  ]).nullable(),
556
852
  position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
557
- colorToken: themeColorTokenSchema.optional()
853
+ colorToken: opaqueButtonColourTokenSchema.optional()
558
854
  });
559
- const variantEffectsSchema = z$1.object({
560
- base: z$1.array(effectApplicationSchema$1).optional(),
561
- hover: z$1.array(effectApplicationSchema$1).optional(),
562
- active: z$1.array(effectApplicationSchema$1).optional(),
563
- focus: z$1.array(effectApplicationSchema$1).optional()
855
+ const variantEffectsSchema = z$1.strictObject({
856
+ base: z$1.array(buttonEffectApplicationSchema).optional(),
857
+ hover: z$1.array(buttonEffectApplicationSchema).optional(),
858
+ active: z$1.array(buttonEffectApplicationSchema).optional(),
859
+ focus: z$1.array(buttonEffectApplicationSchema).optional()
564
860
  });
565
861
  /**
566
862
  * Per-variant size overrides schema — every entry is optional.
@@ -581,8 +877,8 @@ const variantSizeOverridesSchema = z$1.object({
581
877
  * default (e.g. link-style variants declaring zero horizontal padding at
582
878
  * every size).
583
879
  */
584
- const canonicalButtonVariantSchema = z$1.object({
585
- id: z$1.string(),
880
+ const buttonVariantSchema = z$1.strictObject({
881
+ id: buttonVariantIdSchema,
586
882
  name: z$1.string().min(1).max(20),
587
883
  enabled: z$1.boolean(),
588
884
  priority: prioritySchema,
@@ -596,422 +892,1011 @@ const canonicalButtonVariantSchema = z$1.object({
596
892
  sizes: variantSizeOverridesSchema.optional(),
597
893
  affordance: buttonVariantAffordanceSchema.optional()
598
894
  });
599
- /**
600
- * Temporary persisted-theme admission boundary for issue #1206.
601
- *
602
- * Production inventory on 2026-07-22 found 22 rows using the old independent
603
- * `textColorToken`/border colour shape. New source and all downstream code use
604
- * only the canonical relationship. Delete this preprocessor after the reviewed
605
- * production rewrite reports zero legacy rows.
606
- */
607
- const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
608
- const buttonSystemSchema = z$1.object({
895
+ const buttonSystemSchema = z$1.strictObject({
609
896
  global: buttonGlobalSettingsSchema,
610
897
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
611
898
  sizes: buttonSizesSchema.optional()
899
+ }).superRefine((system, ctx) => {
900
+ const seen = /* @__PURE__ */ new Set();
901
+ system.variants.forEach((variant, index) => {
902
+ if (seen.has(variant.id)) ctx.addIssue({
903
+ code: "custom",
904
+ path: [
905
+ "variants",
906
+ index,
907
+ "id"
908
+ ],
909
+ message: `Duplicate button variant ID: ${variant.id}`
910
+ });
911
+ seen.add(variant.id);
912
+ });
612
913
  });
613
- function normalizeLegacyButtonVariantInput(input) {
614
- if (!isUnknownRecord(input)) return input;
615
- const background = input.background;
616
- if (!isUnknownRecord(background)) return input;
617
- 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;
618
- const { textColorToken, ...withoutLegacyText } = input;
619
- const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
620
- const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
621
- const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
622
- const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
623
- if (background.type === "transparent") return {
624
- ...withoutLegacyText,
625
- background: {
626
- type: "surface",
627
- role: "link"
628
- },
629
- border: normalizedBorder,
630
- affordance: normalizedAffordance,
631
- effects: normalizedEffects,
632
- hoverBackgroundToken
633
- };
634
- if (background.type === "gradient") {
635
- const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
636
- return {
637
- ...withoutLegacyText,
638
- background: {
639
- ...background,
640
- stops,
641
- foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
642
- },
643
- border: normalizedBorder,
644
- affordance: normalizedAffordance,
645
- effects: normalizedEffects,
646
- hoverBackgroundToken
647
- };
648
- }
649
- return {
650
- ...withoutLegacyText,
651
- background: background.type === "solid" ? {
652
- ...background,
653
- colorToken: stripLegacyTokenAlpha(background.colorToken)
654
- } : background,
655
- border: normalizedBorder,
656
- affordance: normalizedAffordance,
657
- effects: normalizedEffects,
658
- hoverBackgroundToken
659
- };
660
- }
661
- function normalizeLegacyButtonGlobalInput(input) {
662
- if (!isUnknownRecord(input)) return input;
663
- const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
664
- return hoverColorToken === input.hoverColorToken ? input : {
665
- ...input,
666
- hoverColorToken
667
- };
668
- }
669
- function hasLegacyAlphaToken(input, background) {
670
- const stops = isUnknownArray(background.stops) ? background.stops : [];
671
- return [
672
- input.hoverBackgroundToken,
673
- background.colorToken,
674
- background.foregroundToken,
675
- ...stops
676
- ].some((token) => stripLegacyTokenAlpha(token) !== token);
914
+ //#endregion
915
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
916
+ const SURFACE_FOREGROUND_CSS_VARS = {
917
+ text: "--rb-surface-text-color",
918
+ heading: "--rb-surface-heading-color",
919
+ link: "--rb-surface-link-color",
920
+ supporting: "--rb-surface-supporting-color"
921
+ };
922
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
923
+ const DEFAULT_ROLE_FALLBACKS = {
924
+ text: DEFAULT_SURFACE_TEXT_COLOR,
925
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
926
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
927
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
928
+ };
929
+ function requireSurfaceColorValue(role, value) {
930
+ const normalized = value.trim();
931
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
932
+ return normalized;
677
933
  }
678
- function stripLegacyTokenAlpha(value) {
679
- if (typeof value !== "string") return value;
680
- const slashIndex = value.indexOf("/");
681
- return slashIndex < 0 ? value : value.slice(0, slashIndex);
934
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
935
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
936
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
937
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
938
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
682
939
  }
683
- function hasLegacyButtonEffects(input) {
684
- if (!isUnknownRecord(input)) return false;
685
- return [
686
- "base",
687
- "hover",
688
- "active",
689
- "focus"
690
- ].some((state) => {
691
- const effects = input[state];
692
- return Array.isArray(effects) && effects.some((effect) => {
693
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
694
- const classification = classifyButtonEffectId(effect.effectId);
695
- if (classification !== "colour-transition" && classification !== "non-colour") return true;
696
- return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
697
- });
698
- });
940
+ //#endregion
941
+ //#region ../theme-core/src/buttons/constants.ts
942
+ /**
943
+ * Shared constants for button CSS generation
944
+ *
945
+ * These mappings convert Tailwind-style tokens to actual CSS values.
946
+ * Centralized here to eliminate duplication across multiple files.
947
+ */
948
+ /**
949
+ * Map Tailwind-style gradient direction tokens to CSS gradient directions
950
+ */
951
+ const GRADIENT_DIRECTION_MAP = {
952
+ "to-r": "to right",
953
+ "to-l": "to left",
954
+ "to-t": "to top",
955
+ "to-b": "to bottom",
956
+ "to-br": "to bottom right",
957
+ "to-bl": "to bottom left",
958
+ "to-tr": "to top right",
959
+ "to-tl": "to top left"
960
+ };
961
+ //#endregion
962
+ //#region ../theme-core/src/buttons/effectClassification.ts
963
+ /**
964
+ * Exhaustive live policy for every registered effect id.
965
+ *
966
+ * The resolver consumes the transition payload directly, so adding an effect
967
+ * cannot update classification without also defining its runtime behavior.
968
+ */
969
+ const BUTTON_EFFECT_POLICIES = {
970
+ "darken-background": {
971
+ classification: "colour-transition",
972
+ transition: {
973
+ kind: "adjust-shade",
974
+ direction: "darker"
975
+ }
976
+ },
977
+ "lighten-background": {
978
+ classification: "colour-transition",
979
+ transition: {
980
+ kind: "adjust-shade",
981
+ direction: "lighter"
982
+ }
983
+ },
984
+ "background-on-hover": {
985
+ classification: "colour-transition",
986
+ transition: { kind: "replace-background" }
987
+ },
988
+ "background-on-hover-alpha": {
989
+ classification: "unsupported-colour",
990
+ reason: "alpha_requires_surface"
991
+ },
992
+ "fade-background": {
993
+ classification: "unsupported-colour",
994
+ reason: "changes_element_opacity"
995
+ },
996
+ "opacity-reduce": {
997
+ classification: "unsupported-colour",
998
+ reason: "changes_element_opacity"
999
+ },
1000
+ "gradient-text": {
1001
+ classification: "unsupported-colour",
1002
+ reason: "changes_foreground"
1003
+ },
1004
+ "text-color-change": {
1005
+ classification: "unsupported-colour",
1006
+ reason: "changes_foreground"
1007
+ },
1008
+ "border-color-change": {
1009
+ classification: "unsupported-colour",
1010
+ reason: "changes_border"
1011
+ },
1012
+ "aurora-glow": {
1013
+ classification: "unsupported-background-overlay",
1014
+ reason: "unresolved_background_overlay"
1015
+ },
1016
+ "cosmic-stardust": {
1017
+ classification: "unsupported-background-overlay",
1018
+ reason: "unresolved_background_overlay"
1019
+ },
1020
+ "gradient-border-fill": {
1021
+ classification: "unsupported-background-overlay",
1022
+ reason: "unresolved_background_overlay"
1023
+ },
1024
+ "animated-gradient-border-fill": {
1025
+ classification: "unsupported-background-overlay",
1026
+ reason: "unresolved_background_overlay"
1027
+ },
1028
+ "nav-underline": {
1029
+ classification: "not-button",
1030
+ reason: "not_button_effect"
1031
+ },
1032
+ "nav-frosted-base": {
1033
+ classification: "not-button",
1034
+ reason: "not_button_effect"
1035
+ },
1036
+ "nav-frosted-hover": {
1037
+ classification: "not-button",
1038
+ reason: "not_button_effect"
1039
+ },
1040
+ "nav-frosted-active": {
1041
+ classification: "not-button",
1042
+ reason: "not_button_effect"
1043
+ },
1044
+ "animated-gradient": { classification: "non-colour" },
1045
+ "border-glow": {
1046
+ classification: "unsupported-colour",
1047
+ reason: "changes_shadow_colour"
1048
+ },
1049
+ "drop-shadow": { classification: "decorative-colour" },
1050
+ "inner-shadow": { classification: "decorative-colour" },
1051
+ "neumorphic-shadow": { classification: "decorative-colour" },
1052
+ "outer-glow": {
1053
+ classification: "unsupported-colour",
1054
+ reason: "changes_shadow_colour"
1055
+ },
1056
+ "retro-3d-shadow": { classification: "decorative-colour" },
1057
+ "scale-up": { classification: "non-colour" },
1058
+ "scale-down": { classification: "non-colour" },
1059
+ lift: { classification: "non-colour" },
1060
+ "press-down": { classification: "non-colour" },
1061
+ pop: { classification: "non-colour" },
1062
+ "border-bottom-grow": { classification: "non-colour" },
1063
+ "sketchy-border": { classification: "decorative-colour" },
1064
+ "pulse-animation": {
1065
+ classification: "unsupported-colour",
1066
+ reason: "changes_element_opacity"
1067
+ }
1068
+ };
1069
+ function validateButtonEffectOptions(effect) {
1070
+ const parsed = buttonEffectApplicationSchema.safeParse(effect);
1071
+ if (parsed.success) return { ok: true };
1072
+ const firstIssue = parsed.error.issues[0];
1073
+ const firstPathPart = firstIssue?.path[firstIssue.path.length - 1];
1074
+ return {
1075
+ ok: false,
1076
+ option: (firstIssue?.code === "unrecognized_keys" ? firstIssue.keys[0] : void 0) ?? (typeof firstPathPart === "string" ? firstPathPart : "options")
1077
+ };
1078
+ }
1079
+ function getButtonEffectPolicy(effectId) {
1080
+ return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
699
1081
  }
700
- function normalizeLegacyButtonEffects(input, backgroundType) {
701
- if (!isUnknownRecord(input)) return input;
702
- const normalized = {};
1082
+ //#endregion
1083
+ //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
1084
+ const BUTTON_VISUAL_STATES = [
1085
+ "base",
1086
+ "hover",
1087
+ "focus",
1088
+ "active",
1089
+ "disabled"
1090
+ ];
1091
+ const BUTTON_CONTRAST_POLICY = {
1092
+ textMinimumRatio: 4.5,
1093
+ nonTextMinimumRatio: 3,
1094
+ gradientSamplesPerSegment: 20,
1095
+ alphaBackgrounds: "reject-without-known-surface",
1096
+ disabled: "retain-full-colour-contract"
1097
+ };
1098
+ function compileButtonColourStates(input) {
1099
+ const baseTransition = applyColourEffects({
1100
+ variantId: input.variant.id,
1101
+ state: "base",
1102
+ treatment: input.variant.background,
1103
+ effects: input.variant.effects?.base,
1104
+ palette: input.palette
1105
+ });
1106
+ if (!baseTransition.ok) return baseTransition;
1107
+ const stateTreatments = {
1108
+ base: baseTransition.treatment,
1109
+ hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
1110
+ focus: baseTransition.treatment,
1111
+ active: baseTransition.treatment,
1112
+ disabled: baseTransition.treatment
1113
+ };
1114
+ const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
703
1115
  for (const state of [
704
- "base",
705
1116
  "hover",
706
- "active",
707
- "focus"
1117
+ "focus",
1118
+ "active"
708
1119
  ]) {
709
- const effects = input[state];
710
- if (!Array.isArray(effects)) continue;
711
- const safeEffects = effects.flatMap((effect) => {
712
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
713
- const classification = classifyButtonEffectId(effect.effectId);
714
- if (classification === "non-colour") return [effect];
715
- if (classification !== "colour-transition") return [];
716
- if (effect.effectId === "background-on-hover") {
717
- const options = isUnknownRecord(effect.options) ? effect.options : null;
718
- const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
719
- return colorToken === null ? [] : [{
720
- ...effect,
721
- options: {
722
- ...options,
1120
+ const transition = applyColourEffects({
1121
+ variantId: input.variant.id,
1122
+ state,
1123
+ treatment: stateTreatments[state],
1124
+ effects: input.variant.effects?.[state],
1125
+ palette: input.palette
1126
+ });
1127
+ if (!transition.ok) return transition;
1128
+ stateTreatments[state] = transition.treatment;
1129
+ if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
1130
+ }
1131
+ const resolved = {};
1132
+ for (const state of BUTTON_VISUAL_STATES) {
1133
+ const result = resolveTreatment({
1134
+ variantId: input.variant.id,
1135
+ state,
1136
+ treatment: stateTreatments[state],
1137
+ palette: input.palette
1138
+ });
1139
+ if (!result.ok) return result;
1140
+ resolved[state] = result.colour;
1141
+ }
1142
+ return {
1143
+ ok: true,
1144
+ states: resolved,
1145
+ focusIndicator: resolveFocusIndicator(resolved.focus),
1146
+ nonColourEffects
1147
+ };
1148
+ }
1149
+ function hoverTreatment(variant, global, fallback) {
1150
+ const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
1151
+ return token === void 0 ? fallback : {
1152
+ type: "solid",
1153
+ colorToken: token
1154
+ };
1155
+ }
1156
+ function applyColourEffects(input) {
1157
+ let treatment = input.treatment;
1158
+ const nonColourEffects = [];
1159
+ for (const effect of input.effects ?? []) {
1160
+ const policy = getButtonEffectPolicy(effect.effectId);
1161
+ if (policy === null) return unknownEffect(input, effect);
1162
+ if (policy.classification === "unsupported-colour" || policy.classification === "unsupported-background-overlay" || policy.classification === "not-button") return unsupportedEffect(input, effect, policy.reason);
1163
+ const options = validateButtonEffectOptions(effect);
1164
+ if (!options.ok) return invalidEffectOption(input, effect, options.option);
1165
+ switch (policy.classification) {
1166
+ case "non-colour":
1167
+ case "decorative-colour":
1168
+ nonColourEffects.push(effect);
1169
+ break;
1170
+ case "colour-transition":
1171
+ if (policy.transition.kind === "adjust-shade") {
1172
+ if (effect.effectId !== "darken-background" && effect.effectId !== "lighten-background") return unknownEffect(input, effect);
1173
+ if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
1174
+ const amount = readShadeAmount(effect);
1175
+ if (amount === null) return invalidEffectOption(input, effect, "amount");
1176
+ const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
1177
+ treatment = {
1178
+ type: "solid",
1179
+ colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
1180
+ };
1181
+ break;
1182
+ }
1183
+ if (policy.transition.kind === "replace-background") {
1184
+ if (effect.effectId !== "background-on-hover") return unknownEffect(input, effect);
1185
+ const colorToken = readOpaqueToken(effect);
1186
+ if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
1187
+ treatment = {
1188
+ type: "solid",
723
1189
  colorToken
724
- }
725
- }];
726
- }
727
- if (backgroundType !== "solid") return [];
728
- const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
729
- const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
730
- return [{
731
- ...effect,
732
- options: {
733
- ...isUnknownRecord(effect.options) ? effect.options : {},
734
- amount
1190
+ };
1191
+ break;
735
1192
  }
736
- }];
737
- });
738
- if (safeEffects.length > 0) normalized[state] = safeEffects;
1193
+ return unknownEffect(input, effect);
1194
+ }
739
1195
  }
740
- return Object.keys(normalized).length === 0 ? void 0 : normalized;
1196
+ return {
1197
+ ok: true,
1198
+ treatment,
1199
+ nonColourEffects
1200
+ };
741
1201
  }
742
- function isUnknownRecord(value) {
743
- return typeof value === "object" && value !== null && !Array.isArray(value);
1202
+ function resolveTreatment(input) {
1203
+ const treatment = input.treatment;
1204
+ switch (treatment.type) {
1205
+ case "surface": return {
1206
+ ok: true,
1207
+ colour: {
1208
+ kind: "surface",
1209
+ role: treatment.role,
1210
+ backgroundCss: "transparent",
1211
+ foregroundCss: surfaceForegroundValue(treatment.role)
1212
+ }
1213
+ };
1214
+ case "solid": return resolveSolid(input, treatment.colorToken);
1215
+ case "gradient": return resolveGradient(input, treatment);
1216
+ }
744
1217
  }
745
- function isUnknownArray(value) {
746
- return Array.isArray(value);
1218
+ function resolveFocusIndicator(colour) {
1219
+ switch (colour.kind) {
1220
+ case "solid": return {
1221
+ kind: "two-tone",
1222
+ innerCss: colour.foregroundCss,
1223
+ outerCss: colour.backgroundCss,
1224
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
1225
+ };
1226
+ case "gradient": return {
1227
+ kind: "two-tone",
1228
+ innerCss: colour.foregroundCss,
1229
+ outerCss: tokenCss(colour.backgroundTokens[0]),
1230
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
1231
+ };
1232
+ case "surface": return {
1233
+ kind: "surface-role",
1234
+ colorCss: colour.foregroundCss,
1235
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
1236
+ };
1237
+ }
747
1238
  }
748
- buttonSizeNameSchema.options;
749
- //#endregion
750
- //#region ../theme-core/src/buttons/personalities/types.ts
751
- function asButtonPersonalityId(value) {
752
- if (value.length === 0) throw new Error("ButtonPersonalityId must be a non-empty string");
753
- return value;
1239
+ function resolveSolid(input, backgroundToken) {
1240
+ const backgroundHex = paletteHex(input, backgroundToken);
1241
+ if (!backgroundHex.ok) return backgroundHex;
1242
+ const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
1243
+ const foregroundHex = input.palette[foregroundToken];
1244
+ if (typeof foregroundHex !== "string") return {
1245
+ ok: false,
1246
+ error: {
1247
+ tag: "missing_on_colour",
1248
+ variantId: input.variantId,
1249
+ state: input.state,
1250
+ backgroundToken,
1251
+ foregroundToken
1252
+ }
1253
+ };
1254
+ const contrast = validateContrast({
1255
+ ...input,
1256
+ background: backgroundToken,
1257
+ backgroundHex: backgroundHex.hex,
1258
+ foreground: foregroundToken,
1259
+ foregroundHex
1260
+ });
1261
+ if (!contrast.ok) return contrast;
1262
+ return {
1263
+ ok: true,
1264
+ colour: {
1265
+ kind: "solid",
1266
+ backgroundToken,
1267
+ foregroundToken,
1268
+ backgroundCss: tokenCss(backgroundToken),
1269
+ foregroundCss: tokenCss(foregroundToken)
1270
+ }
1271
+ };
754
1272
  }
755
- const buttonPersonalityIdSchema = z$1.string().min(1).transform((value) => asButtonPersonalityId(value));
756
- z$1.object({
757
- id: buttonPersonalityIdSchema,
758
- name: z$1.string().min(1),
759
- description: z$1.string().min(1),
760
- tags: styleTagsSchema,
761
- buttonSystem: buttonSystemSchema
762
- });
763
- /**
764
- * Helper for personality file modules to keep the call site short and
765
- * type-safe. `as const satisfies ...` would also work but produces wider
766
- * types at use sites.
767
- */
768
- function defineButtonPersonality(input) {
1273
+ function resolveGradient(input, treatment) {
1274
+ const foregroundHex = paletteHex(input, treatment.foregroundToken);
1275
+ if (!foregroundHex.ok) return foregroundHex;
1276
+ const stopHexes = [];
1277
+ for (const token of treatment.stops) {
1278
+ const stop = paletteHex(input, token);
1279
+ if (!stop.ok) return stop;
1280
+ stopHexes.push(stop.hex);
1281
+ }
1282
+ for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
1283
+ const start = stopHexes[segment];
1284
+ const end = stopHexes[segment + 1];
1285
+ const startToken = treatment.stops[segment];
1286
+ const endToken = treatment.stops[segment + 1];
1287
+ if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
1288
+ for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
1289
+ const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
1290
+ const sampleHex = interpolateHex(start, end, position);
1291
+ const contrast = validateContrast({
1292
+ ...input,
1293
+ background: `${startToken}→${endToken}@${position.toFixed(2)}`,
1294
+ backgroundHex: sampleHex,
1295
+ foreground: treatment.foregroundToken,
1296
+ foregroundHex: foregroundHex.hex
1297
+ });
1298
+ if (!contrast.ok) return contrast;
1299
+ }
1300
+ }
1301
+ const direction = GRADIENT_DIRECTION_MAP[treatment.direction] ?? "to right";
769
1302
  return {
770
- id: asButtonPersonalityId(input.id),
771
- name: input.name,
772
- description: input.description,
773
- tags: input.tags,
774
- buttonSystem: buttonSystemSchema.parse(input.buttonSystem)
1303
+ ok: true,
1304
+ colour: {
1305
+ kind: "gradient",
1306
+ backgroundTokens: treatment.stops,
1307
+ foregroundToken: treatment.foregroundToken,
1308
+ backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
1309
+ foregroundCss: tokenCss(treatment.foregroundToken)
1310
+ }
1311
+ };
1312
+ }
1313
+ function paletteHex(input, token) {
1314
+ const hex = input.palette[token];
1315
+ return typeof hex === "string" ? {
1316
+ ok: true,
1317
+ hex
1318
+ } : {
1319
+ ok: false,
1320
+ error: {
1321
+ tag: "unknown_palette_token",
1322
+ variantId: input.variantId,
1323
+ state: input.state,
1324
+ token
1325
+ }
1326
+ };
1327
+ }
1328
+ function validateContrast(input) {
1329
+ const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
1330
+ return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
1331
+ ok: false,
1332
+ error: {
1333
+ tag: "insufficient_contrast",
1334
+ variantId: input.variantId,
1335
+ state: input.state,
1336
+ background: input.background,
1337
+ foreground: input.foreground,
1338
+ ratio,
1339
+ requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
1340
+ }
1341
+ };
1342
+ }
1343
+ function tokenCss(token) {
1344
+ return `rgb(var(--tb-${token}))`;
1345
+ }
1346
+ function adjustShade(token, amount, palette) {
1347
+ const match = token.match(/^(.*)-(\d{2,3})$/);
1348
+ const base = match?.[1] ?? token;
1349
+ const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
1350
+ const availableShades = [
1351
+ 50,
1352
+ 100,
1353
+ 200,
1354
+ 300,
1355
+ 400,
1356
+ 500,
1357
+ 600,
1358
+ 700,
1359
+ 800,
1360
+ 900,
1361
+ 950
1362
+ ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
1363
+ if (availableShades.length === 0) return token;
1364
+ const firstShade = availableShades[0];
1365
+ if (firstShade === void 0) return token;
1366
+ const exactIndex = availableShades.indexOf(currentShade);
1367
+ const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
1368
+ const stepCount = Math.abs(amount) / 100;
1369
+ const direction = amount < 0 ? -1 : 1;
1370
+ const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
1371
+ return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
1372
+ }
1373
+ function readShadeAmount(effect) {
1374
+ const value = effect.options?.amount ?? 100;
1375
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
1376
+ }
1377
+ function readOpaqueToken(effect) {
1378
+ return parseOpaqueButtonColourToken(effect.options?.colorToken ?? "neutral-800");
1379
+ }
1380
+ function unsupportedEffect(input, effect, reason) {
1381
+ return {
1382
+ ok: false,
1383
+ error: {
1384
+ tag: "unsupported_colour_effect",
1385
+ variantId: input.variantId,
1386
+ state: input.state,
1387
+ effectId: effect.effectId,
1388
+ reason
1389
+ }
1390
+ };
1391
+ }
1392
+ function invalidEffectOption(input, effect, option) {
1393
+ return {
1394
+ ok: false,
1395
+ error: {
1396
+ tag: "invalid_colour_effect_options",
1397
+ variantId: input.variantId,
1398
+ state: input.state,
1399
+ effectId: effect.effectId,
1400
+ option
1401
+ }
1402
+ };
1403
+ }
1404
+ function inapplicableEffect(input, effect, treatment) {
1405
+ return {
1406
+ ok: false,
1407
+ error: {
1408
+ tag: "inapplicable_colour_effect",
1409
+ variantId: input.variantId,
1410
+ state: input.state,
1411
+ effectId: effect.effectId,
1412
+ treatment: treatment.type
1413
+ }
775
1414
  };
776
1415
  }
1416
+ function unknownEffect(input, effect) {
1417
+ return {
1418
+ ok: false,
1419
+ error: {
1420
+ tag: "unknown_button_effect",
1421
+ variantId: input.variantId,
1422
+ state: input.state,
1423
+ effectId: effect.effectId
1424
+ }
1425
+ };
1426
+ }
1427
+ function interpolateHex(startHex, endHex, position) {
1428
+ const start = hexToRgb(startHex);
1429
+ const end = hexToRgb(endHex);
1430
+ const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
1431
+ return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
1432
+ }
777
1433
  //#endregion
778
- //#region ../theme-core/src/buttons/personalities/brushed-wash.ts
1434
+ //#region ../theme-core/src/buttons/parseThemeForPersistence.ts
779
1435
  /**
780
- * Brushed Wash — gradient backgrounds that read as watercolour washes.
781
- * Generous padding, soft shadows; pairs with vibrant organic brands.
1436
+ * Proves that every persisted button variant compiles against the theme's
1437
+ * concrete palette. Public rendering may still recover from malformed
1438
+ * external/runtime input, but persistence boundaries must reject it.
782
1439
  */
783
- const brushedWash = defineButtonPersonality({
784
- id: "brushed-wash",
785
- name: "Brushed Wash",
786
- description: "Watercolour-gradient buttons with soft shadows and generous padding.",
1440
+ function parseThemeForPersistence(value) {
1441
+ const parsed = themeSchema$1.safeParse(value);
1442
+ if (!parsed.success) return {
1443
+ ok: false,
1444
+ error: {
1445
+ kind: "invalid-theme-schema",
1446
+ issues: parsed.error.issues
1447
+ }
1448
+ };
1449
+ const theme = parsed.data;
1450
+ if (!theme.buttons) return {
1451
+ ok: true,
1452
+ value: theme
1453
+ };
1454
+ const palette = expandPalette(theme.palette);
1455
+ for (const variant of theme.buttons.variants) {
1456
+ const result = compileButtonColourStates({
1457
+ variant,
1458
+ global: theme.buttons.global,
1459
+ palette
1460
+ });
1461
+ if (!result.ok) return {
1462
+ ok: false,
1463
+ error: {
1464
+ kind: "invalid-button-colour-contract",
1465
+ variantId: variant.id,
1466
+ cause: result.error
1467
+ }
1468
+ };
1469
+ }
1470
+ return {
1471
+ ok: true,
1472
+ value: theme
1473
+ };
1474
+ }
1475
+ buttonSizeNameSchema.options;
1476
+ //#endregion
1477
+ //#region ../theme-core/src/buttons/styles/domain.ts
1478
+ const PUBLIC_BUTTON_ROLES = [
1479
+ "primary",
1480
+ "secondary",
1481
+ "outline",
1482
+ "ghost",
1483
+ "accent"
1484
+ ];
1485
+ function defineButtonStyleRecipe(recipe) {
1486
+ return recipe;
1487
+ }
1488
+ function materializeButtonRoles(definitions) {
1489
+ const primary = materializeCompleteRole("primary", definitions.primary.appearance);
1490
+ const secondary = materializeCompleteRole("secondary", definitions.secondary.appearance);
1491
+ const completeRoles = {
1492
+ primary,
1493
+ secondary
1494
+ };
1495
+ return {
1496
+ primary,
1497
+ secondary,
1498
+ outline: materializeSupportingRole("outline", definitions.outline, completeRoles),
1499
+ ghost: materializeSupportingRole("ghost", definitions.ghost, completeRoles),
1500
+ accent: materializeSupportingRole("accent", definitions.accent, completeRoles)
1501
+ };
1502
+ }
1503
+ function resolveButtonStyleRecipe(recipe, parameters) {
1504
+ const parsedParameters = recipe.parameterSchema.safeParse(parameters);
1505
+ if (!parsedParameters.success) return {
1506
+ kind: "error",
1507
+ error: {
1508
+ kind: "invalid-parameters",
1509
+ styleId: recipe.id,
1510
+ issues: parsedParameters.error.issues.map((issue) => issue.message)
1511
+ }
1512
+ };
1513
+ const definition = recipe.defineSystem(parsedParameters.data);
1514
+ const roles = materializeButtonRoles(definition.roles);
1515
+ const buttonSystem = {
1516
+ global: definition.global,
1517
+ variants: PUBLIC_BUTTON_ROLES.map((role) => roles[role]),
1518
+ ...definition.sizes ? { sizes: definition.sizes } : {}
1519
+ };
1520
+ const parsedButtonSystem = buttonSystemSchema.safeParse(buttonSystem);
1521
+ if (!parsedButtonSystem.success) return {
1522
+ kind: "error",
1523
+ error: {
1524
+ kind: "invalid-button-system",
1525
+ styleId: recipe.id,
1526
+ issues: parsedButtonSystem.error.issues.map((issue) => issue.message)
1527
+ }
1528
+ };
1529
+ return {
1530
+ kind: "resolved",
1531
+ buttonSystem: parsedButtonSystem.data
1532
+ };
1533
+ }
1534
+ function materializeSupportingRole(role, definition, completeRoles) {
1535
+ if (definition.kind === "complete") return materializeCompleteRole(role, definition.appearance);
1536
+ return {
1537
+ ...completeRoles[definition.from],
1538
+ ...definition.overrides,
1539
+ id: role,
1540
+ name: roleName(role),
1541
+ enabled: true,
1542
+ priority: 3
1543
+ };
1544
+ }
1545
+ function materializeCompleteRole(role, appearance) {
1546
+ return {
1547
+ ...appearance,
1548
+ id: role,
1549
+ name: roleName(role),
1550
+ enabled: true,
1551
+ priority: rolePriority(role)
1552
+ };
1553
+ }
1554
+ function roleName(role) {
1555
+ return role.charAt(0).toUpperCase() + role.slice(1);
1556
+ }
1557
+ function rolePriority(role) {
1558
+ if (role === "primary") return 1;
1559
+ if (role === "secondary") return 2;
1560
+ return 3;
1561
+ }
1562
+ //#endregion
1563
+ //#region ../theme-core/src/buttons/styles/selection.ts
1564
+ const BUTTON_STYLE_IDS = [
1565
+ "soft-pill",
1566
+ "earth-pill",
1567
+ "confident-chip",
1568
+ "editorial-link",
1569
+ "pebble",
1570
+ "ink-stamp",
1571
+ "flat-ink",
1572
+ "brushed-wash",
1573
+ "showtime-pill",
1574
+ "joy-stamp",
1575
+ "ornate-ritual",
1576
+ "quiet-arrow"
1577
+ ];
1578
+ const quietArrowParametersSchema = z$1.object({ secondaryTreatment: z$1.enum(["outlined", "solid-accent"]) }).strict();
1579
+ const emptyParametersSchema$1 = z$1.object({}).strict();
1580
+ function selectionBranch(styleId, parameters) {
1581
+ return z$1.object({
1582
+ styleId: z$1.literal(styleId),
1583
+ parameters
1584
+ }).strict();
1585
+ }
1586
+ z$1.discriminatedUnion("styleId", [
1587
+ selectionBranch("soft-pill", emptyParametersSchema$1),
1588
+ selectionBranch("earth-pill", emptyParametersSchema$1),
1589
+ selectionBranch("confident-chip", emptyParametersSchema$1),
1590
+ selectionBranch("editorial-link", emptyParametersSchema$1),
1591
+ selectionBranch("pebble", emptyParametersSchema$1),
1592
+ selectionBranch("ink-stamp", emptyParametersSchema$1),
1593
+ selectionBranch("flat-ink", emptyParametersSchema$1),
1594
+ selectionBranch("brushed-wash", emptyParametersSchema$1),
1595
+ selectionBranch("showtime-pill", emptyParametersSchema$1),
1596
+ selectionBranch("joy-stamp", emptyParametersSchema$1),
1597
+ selectionBranch("ornate-ritual", emptyParametersSchema$1),
1598
+ selectionBranch("quiet-arrow", quietArrowParametersSchema)
1599
+ ]);
1600
+ //#endregion
1601
+ //#region ../theme-core/src/buttons/styles/recipes/quietArrow.ts
1602
+ const quietArrowRecipe = defineButtonStyleRecipe({
1603
+ id: "quiet-arrow",
1604
+ accessibilityLabel: "Wide uppercase buttons with trailing arrows",
787
1605
  tags: {
788
1606
  tone: "classic",
789
- energy: "vibrant",
790
- motion: "standard",
791
- era: "organic"
1607
+ energy: "calm",
1608
+ motion: "subtle",
1609
+ era: "editorial"
792
1610
  },
793
- buttonSystem: {
1611
+ parameterSchema: quietArrowParametersSchema,
1612
+ defaultParameters: { secondaryTreatment: "outlined" },
1613
+ controls: [{
1614
+ kind: "choice",
1615
+ parameter: "secondaryTreatment",
1616
+ label: "Secondary button treatment",
1617
+ options: [{
1618
+ value: "outlined",
1619
+ label: "Outlined"
1620
+ }, {
1621
+ value: "solid-accent",
1622
+ label: "Solid accent"
1623
+ }]
1624
+ }],
1625
+ defineSystem: (parameters) => ({
794
1626
  global: {
795
1627
  cornerStyle: "rounded",
796
- shadow: "low",
797
- textTransform: "none",
1628
+ textTransform: "uppercase",
798
1629
  fontWeight: 500,
799
- borderWidth: 1,
1630
+ typography: "body",
1631
+ fontSize: "text-sm",
1632
+ letterSpacing: "loose",
800
1633
  hoverTransform: "none",
801
1634
  hoverColor: "none"
802
1635
  },
803
1636
  sizes: {
804
1637
  sm: {
805
- padding: "0.75rem 2rem",
1638
+ padding: "0.625rem 1.25rem",
1639
+ fontSize: "text-xs"
1640
+ },
1641
+ md: {
1642
+ padding: "0.8125rem 1.75rem",
806
1643
  fontSize: "text-sm"
807
1644
  },
808
- md: { padding: "1rem 2.5rem" },
809
1645
  lg: {
810
- padding: "1.25rem 3rem",
811
- fontSize: "text-lg"
1646
+ padding: "0.9375rem 2.125rem",
1647
+ fontSize: "text-sm"
812
1648
  },
813
1649
  xl: {
814
- padding: "1.625rem 3.75rem",
815
- fontSize: "text-xl"
1650
+ padding: "1.0625rem 2.5rem",
1651
+ fontSize: "text-base"
816
1652
  }
817
1653
  },
818
- variants: [
819
- {
820
- id: "primary",
821
- name: "Primary",
822
- enabled: true,
823
- priority: 1,
824
- background: {
825
- type: "gradient",
826
- stops: ["primary", "accent"],
827
- direction: "to-r",
828
- foregroundToken: "primaryForeground"
829
- },
830
- borderRadius: "rounded-lg",
831
- shadow: {
832
- elevation: "low",
833
- softness: "soft",
834
- position: "bottom"
1654
+ roles: {
1655
+ primary: {
1656
+ kind: "complete",
1657
+ appearance: {
1658
+ background: {
1659
+ type: "solid",
1660
+ colorToken: unsafeAsOpaqueButtonColourToken("primary")
1661
+ },
1662
+ borderRadius: "rounded-xl",
1663
+ affordance: { iconId: "ArrowRight" },
1664
+ effects: { hover: [{
1665
+ effectId: "darken-background",
1666
+ options: { amount: 100 }
1667
+ }] }
1668
+ }
1669
+ },
1670
+ secondary: {
1671
+ kind: "complete",
1672
+ appearance: parameters.secondaryTreatment === "solid-accent" ? {
1673
+ background: {
1674
+ type: "solid",
1675
+ colorToken: unsafeAsOpaqueButtonColourToken("accent")
1676
+ },
1677
+ borderRadius: "rounded-xl",
1678
+ affordance: { iconId: "ArrowRight" },
1679
+ effects: { hover: [{
1680
+ effectId: "darken-background",
1681
+ options: { amount: 100 }
1682
+ }] }
1683
+ } : {
1684
+ background: {
1685
+ type: "surface",
1686
+ role: "link"
1687
+ },
1688
+ borderRadius: "rounded-xl",
1689
+ border: { widthClass: "border" },
1690
+ affordance: { iconId: "ArrowRight" },
1691
+ effects: { hover: [{
1692
+ effectId: "background-on-hover",
1693
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
1694
+ }] }
835
1695
  }
836
1696
  },
837
- {
838
- id: "secondary",
839
- name: "Secondary",
840
- enabled: true,
841
- priority: 2,
842
- background: {
843
- type: "gradient",
844
- stops: ["secondary", "accent"],
845
- direction: "to-br",
846
- foregroundToken: "primaryForeground"
847
- },
848
- borderRadius: "rounded-lg",
849
- shadow: {
850
- elevation: "low",
851
- softness: "soft",
852
- position: "bottom"
1697
+ outline: {
1698
+ kind: "complete",
1699
+ appearance: {
1700
+ background: {
1701
+ type: "surface",
1702
+ role: "link"
1703
+ },
1704
+ borderRadius: "rounded-xl",
1705
+ border: { widthClass: "border" }
853
1706
  }
854
1707
  },
855
- {
856
- id: "outline",
857
- name: "Outline",
858
- enabled: true,
859
- priority: 3,
860
- background: {
861
- type: "surface",
862
- role: "link"
863
- },
864
- borderRadius: "rounded-lg",
865
- border: { widthClass: "border" },
866
- effects: { hover: [{
867
- effectId: "background-on-hover",
868
- options: { colorToken: "surface" }
869
- }] }
1708
+ ghost: {
1709
+ kind: "complete",
1710
+ appearance: {
1711
+ background: {
1712
+ type: "surface",
1713
+ role: "link"
1714
+ },
1715
+ borderRadius: "rounded-xl"
1716
+ }
870
1717
  },
871
- {
872
- id: "ghost",
873
- name: "Ghost",
874
- enabled: true,
875
- priority: 3,
876
- background: {
877
- type: "surface",
878
- role: "link"
879
- },
880
- borderRadius: "rounded-lg",
881
- effects: { hover: [{
882
- effectId: "background-on-hover",
883
- options: { colorToken: "surface" }
884
- }] }
1718
+ accent: {
1719
+ kind: "derive",
1720
+ from: "primary",
1721
+ overrides: { background: {
1722
+ type: "solid",
1723
+ colorToken: unsafeAsOpaqueButtonColourToken("accent")
1724
+ } }
885
1725
  }
886
- ]
887
- }
1726
+ }
1727
+ })
888
1728
  });
889
1729
  //#endregion
890
- //#region ../theme-core/src/buttons/personalities/confident-chip.ts
891
- /**
892
- * Confident Chip — moderate-radius solid buttons that read as competent and
893
- * direct. Default for Clear Professional; works for any modern brand voice.
894
- */
895
- const confidentChip = defineButtonPersonality({
896
- id: "confident-chip",
897
- name: "Confident Chip",
898
- description: "Mid-radius solid buttons with a crisp hover.",
1730
+ //#region ../theme-core/src/buttons/styles/recipes/defineStaticButtonStyleRecipe.ts
1731
+ const emptyParametersSchema = z$1.object({}).strict();
1732
+ function defineStaticButtonStyleRecipe(input) {
1733
+ const system = normalizeSystem(input.system);
1734
+ return defineButtonStyleRecipe({
1735
+ id: input.id,
1736
+ accessibilityLabel: input.accessibilityLabel,
1737
+ tags: input.tags,
1738
+ parameterSchema: emptyParametersSchema,
1739
+ defaultParameters: {},
1740
+ controls: [],
1741
+ defineSystem: () => system
1742
+ });
1743
+ }
1744
+ function normalizeSystem(input) {
1745
+ return {
1746
+ global: buttonGlobalSettingsSchema.parse(input.global),
1747
+ ...input.sizes ? { sizes: input.sizes } : {},
1748
+ roles: {
1749
+ primary: normalizeCompleteRole(input.roles.primary),
1750
+ secondary: normalizeCompleteRole(input.roles.secondary),
1751
+ outline: normalizeSupportingRole(input.roles.outline),
1752
+ ghost: normalizeSupportingRole(input.roles.ghost),
1753
+ accent: normalizeSupportingRole(input.roles.accent)
1754
+ }
1755
+ };
1756
+ }
1757
+ function normalizeCompleteRole(role) {
1758
+ return {
1759
+ kind: "complete",
1760
+ appearance: normalizeAppearance(role.appearance)
1761
+ };
1762
+ }
1763
+ function normalizeSupportingRole(role) {
1764
+ if (role.kind === "complete") return normalizeCompleteRole(role);
1765
+ return {
1766
+ kind: "derive",
1767
+ from: role.from,
1768
+ ...role.overrides ? { overrides: normalizeAppearanceOverrides(role.overrides) } : {}
1769
+ };
1770
+ }
1771
+ function normalizeAppearance(appearance) {
1772
+ const { background, hoverBackgroundToken, ...rest } = appearance;
1773
+ return {
1774
+ ...rest,
1775
+ background: buttonColourTreatmentSchema.parse(background),
1776
+ ...hoverBackgroundToken ? { hoverBackgroundToken: opaqueButtonColourTokenSchema.parse(hoverBackgroundToken) } : {}
1777
+ };
1778
+ }
1779
+ function normalizeAppearanceOverrides(overrides) {
1780
+ const { background, hoverBackgroundToken, ...rest } = overrides;
1781
+ return {
1782
+ ...rest,
1783
+ ...background ? { background: buttonColourTreatmentSchema.parse(background) } : {},
1784
+ ...hoverBackgroundToken ? { hoverBackgroundToken: opaqueButtonColourTokenSchema.parse(hoverBackgroundToken) } : {}
1785
+ };
1786
+ }
1787
+ //#endregion
1788
+ //#region ../theme-core/src/buttons/styles/recipes/staticRecipes.ts
1789
+ const softPillRecipe = defineStaticButtonStyleRecipe({
1790
+ id: "soft-pill",
1791
+ accessibilityLabel: "Rounded pill buttons with a gentle hover lift",
899
1792
  tags: {
900
1793
  tone: "classic",
901
1794
  energy: "calm",
902
- motion: "standard",
903
- era: "modern"
1795
+ motion: "subtle",
1796
+ era: "organic"
904
1797
  },
905
- buttonSystem: {
1798
+ system: {
906
1799
  global: {
907
- cornerStyle: "rounded",
908
- shadow: "medium",
1800
+ cornerStyle: "pill",
1801
+ shadow: "low",
909
1802
  textTransform: "none",
910
- fontWeight: 600,
911
- borderWidth: 1,
912
- hoverTransform: "none",
1803
+ fontWeight: 500,
1804
+ hoverTransform: "lift",
913
1805
  hoverColor: "none"
914
1806
  },
915
- variants: [
916
- {
917
- id: "primary",
918
- name: "Primary",
919
- enabled: true,
920
- priority: 1,
921
- background: {
922
- type: "solid",
923
- colorToken: "primary"
924
- },
925
- borderRadius: "rounded-md",
926
- shadow: {
927
- elevation: "medium",
928
- softness: "soft",
929
- position: "bottom"
930
- },
931
- effects: {
932
- hover: [{
933
- effectId: "darken-background",
934
- options: { amount: 100 }
935
- }],
936
- active: [{
937
- effectId: "scale-down",
938
- options: { scale: "0.97" }
939
- }]
1807
+ roles: {
1808
+ primary: {
1809
+ kind: "complete",
1810
+ appearance: {
1811
+ background: {
1812
+ type: "solid",
1813
+ colorToken: "primary"
1814
+ },
1815
+ borderRadius: "rounded-full",
1816
+ shadow: {
1817
+ elevation: "low",
1818
+ softness: "soft",
1819
+ position: "bottom"
1820
+ },
1821
+ effects: {
1822
+ hover: [{ effectId: "lift" }],
1823
+ active: [{
1824
+ effectId: "scale-down",
1825
+ options: { scale: "0.98" }
1826
+ }]
1827
+ }
940
1828
  }
941
1829
  },
942
- {
943
- id: "secondary",
944
- name: "Secondary",
945
- enabled: true,
946
- priority: 2,
947
- background: {
948
- type: "solid",
949
- colorToken: "surface"
950
- },
951
- borderRadius: "rounded-md",
952
- border: { widthClass: "border" },
953
- effects: { hover: [{
954
- effectId: "background-on-hover",
955
- options: { colorToken: "background" }
956
- }] }
1830
+ secondary: {
1831
+ kind: "complete",
1832
+ appearance: {
1833
+ background: {
1834
+ type: "surface",
1835
+ role: "link"
1836
+ },
1837
+ borderRadius: "rounded-full",
1838
+ border: { widthClass: "border-2" },
1839
+ effects: { hover: [{
1840
+ effectId: "background-on-hover",
1841
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
1842
+ }] }
1843
+ }
1844
+ },
1845
+ outline: {
1846
+ kind: "complete",
1847
+ appearance: {
1848
+ background: {
1849
+ type: "surface",
1850
+ role: "link"
1851
+ },
1852
+ borderRadius: "rounded-full",
1853
+ border: { widthClass: "border" },
1854
+ effects: { hover: [{
1855
+ effectId: "background-on-hover",
1856
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
1857
+ }] }
1858
+ }
957
1859
  },
958
- {
959
- id: "outline",
960
- name: "Outline",
961
- enabled: true,
962
- priority: 3,
963
- background: {
964
- type: "surface",
965
- role: "link"
966
- },
967
- borderRadius: "rounded-md",
968
- border: { widthClass: "border" },
969
- effects: { hover: [{
970
- effectId: "background-on-hover",
971
- options: { colorToken: "surface" }
972
- }] }
1860
+ ghost: {
1861
+ kind: "complete",
1862
+ appearance: {
1863
+ background: {
1864
+ type: "surface",
1865
+ role: "link"
1866
+ },
1867
+ borderRadius: "rounded-full",
1868
+ effects: { hover: [{
1869
+ effectId: "background-on-hover",
1870
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
1871
+ }] }
1872
+ }
973
1873
  },
974
- {
975
- id: "ghost",
976
- name: "Ghost",
977
- enabled: true,
978
- priority: 3,
979
- background: {
980
- type: "surface",
981
- role: "link"
982
- },
983
- borderRadius: "rounded-md",
984
- effects: { hover: [{
985
- effectId: "background-on-hover",
986
- options: { colorToken: "surface" }
987
- }] }
1874
+ accent: {
1875
+ kind: "derive",
1876
+ from: "primary",
1877
+ overrides: { background: {
1878
+ type: "solid",
1879
+ colorToken: "accent"
1880
+ } }
988
1881
  }
989
- ]
1882
+ }
990
1883
  }
991
1884
  });
992
- //#endregion
993
- //#region ../theme-core/src/buttons/personalities/earth-pill.ts
994
- /**
995
- * Earth Pill - warm studio pills with compact, primary-coloured ghost CTAs.
996
- * Built for grounded editorial wellness styles such as Soft Earth.
997
- */
998
- const earthPill = defineButtonPersonality({
1885
+ const earthPillRecipe = defineStaticButtonStyleRecipe({
999
1886
  id: "earth-pill",
1000
- name: "Earth Pill",
1001
- description: "Compact terracotta pills with quiet primary-coloured ghost CTAs.",
1887
+ accessibilityLabel: "Compact rounded buttons with a quiet hover lift",
1002
1888
  tags: {
1003
1889
  tone: "classic",
1004
1890
  energy: "calm",
1005
1891
  motion: "subtle",
1006
1892
  era: "organic"
1007
1893
  },
1008
- buttonSystem: {
1894
+ system: {
1009
1895
  global: {
1010
1896
  cornerStyle: "pill",
1011
1897
  shadow: "low",
1012
1898
  textTransform: "none",
1013
1899
  fontWeight: 500,
1014
- borderWidth: 1,
1015
1900
  hoverTransform: "lift",
1016
1901
  hoverColor: "none",
1017
1902
  typography: "body",
@@ -1032,948 +1917,786 @@ const earthPill = defineButtonPersonality({
1032
1917
  fontSize: "text-xl"
1033
1918
  }
1034
1919
  },
1035
- variants: [
1036
- {
1037
- id: "primary",
1038
- name: "Primary",
1039
- enabled: true,
1040
- priority: 1,
1041
- background: {
1042
- type: "solid",
1043
- colorToken: "primary"
1044
- },
1045
- borderRadius: "rounded-full",
1046
- shadow: {
1047
- elevation: "low",
1048
- softness: "soft",
1049
- position: "bottom"
1050
- },
1051
- effects: {
1052
- hover: [{
1053
- effectId: "lift",
1054
- options: { translateY: "-1px" }
1055
- }, {
1056
- effectId: "background-on-hover",
1057
- options: { colorToken: "accent" }
1058
- }],
1059
- active: [{
1060
- effectId: "scale-down",
1061
- options: { scale: "0.98" }
1062
- }]
1920
+ roles: {
1921
+ primary: {
1922
+ kind: "complete",
1923
+ appearance: {
1924
+ background: {
1925
+ type: "solid",
1926
+ colorToken: "primary"
1927
+ },
1928
+ borderRadius: "rounded-full",
1929
+ shadow: {
1930
+ elevation: "low",
1931
+ softness: "soft",
1932
+ position: "bottom"
1933
+ },
1934
+ effects: {
1935
+ hover: [{ effectId: "lift" }, {
1936
+ effectId: "background-on-hover",
1937
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
1938
+ }],
1939
+ active: [{
1940
+ effectId: "scale-down",
1941
+ options: { scale: "0.98" }
1942
+ }]
1943
+ }
1063
1944
  }
1064
1945
  },
1065
- {
1066
- id: "secondary",
1067
- name: "Secondary",
1068
- enabled: true,
1069
- priority: 2,
1070
- background: {
1071
- type: "surface",
1072
- role: "link"
1073
- },
1074
- borderRadius: "rounded-full",
1075
- border: { widthClass: "border" },
1076
- effects: { hover: [{
1077
- effectId: "background-on-hover",
1078
- options: { colorToken: "muted" }
1079
- }] }
1946
+ secondary: {
1947
+ kind: "complete",
1948
+ appearance: {
1949
+ background: {
1950
+ type: "surface",
1951
+ role: "link"
1952
+ },
1953
+ borderRadius: "rounded-full",
1954
+ border: { widthClass: "border" },
1955
+ effects: { hover: [{
1956
+ effectId: "background-on-hover",
1957
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
1958
+ }] }
1959
+ }
1080
1960
  },
1081
- {
1082
- id: "outline",
1083
- name: "Outline",
1084
- enabled: true,
1085
- priority: 3,
1086
- background: {
1087
- type: "surface",
1088
- role: "link"
1089
- },
1090
- borderRadius: "rounded-full",
1091
- border: { widthClass: "border" },
1092
- effects: { hover: [{
1093
- effectId: "background-on-hover",
1094
- options: { colorToken: "surfaceAlt" }
1095
- }] }
1961
+ outline: {
1962
+ kind: "complete",
1963
+ appearance: {
1964
+ background: {
1965
+ type: "surface",
1966
+ role: "link"
1967
+ },
1968
+ borderRadius: "rounded-full",
1969
+ border: { widthClass: "border" },
1970
+ effects: { hover: [{
1971
+ effectId: "background-on-hover",
1972
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
1973
+ }] }
1974
+ }
1096
1975
  },
1097
- {
1098
- id: "ghost",
1099
- name: "Ghost",
1100
- enabled: true,
1101
- priority: 3,
1102
- background: {
1103
- type: "surface",
1104
- role: "link"
1105
- },
1106
- borderRadius: "rounded-full",
1107
- shadow: {
1108
- elevation: "none",
1109
- softness: null,
1110
- position: "bottom"
1111
- },
1112
- sizes: {
1113
- sm: {
1114
- padding: "0.25rem 0",
1115
- fontSize: "text-sm"
1976
+ ghost: {
1977
+ kind: "complete",
1978
+ appearance: {
1979
+ background: {
1980
+ type: "surface",
1981
+ role: "link"
1116
1982
  },
1117
- md: { padding: "0.375rem 0" },
1118
- lg: {
1119
- padding: "0.5rem 0",
1120
- fontSize: "text-lg"
1983
+ borderRadius: "rounded-full",
1984
+ shadow: {
1985
+ elevation: "none",
1986
+ softness: null,
1987
+ position: "bottom"
1121
1988
  },
1122
- xl: {
1123
- padding: "0.625rem 0",
1124
- fontSize: "text-xl"
1989
+ sizes: {
1990
+ sm: {
1991
+ padding: "0.25rem 0",
1992
+ fontSize: "text-sm"
1993
+ },
1994
+ md: { padding: "0.375rem 0" },
1995
+ lg: {
1996
+ padding: "0.5rem 0",
1997
+ fontSize: "text-lg"
1998
+ },
1999
+ xl: {
2000
+ padding: "0.625rem 0",
2001
+ fontSize: "text-xl"
2002
+ }
1125
2003
  }
1126
2004
  }
1127
- }
1128
- ]
1129
- }
1130
- });
1131
- //#endregion
1132
- //#region ../theme-core/src/buttons/personalities/editorial-link.ts
1133
- /**
1134
- * Editorial Link — all variants are link-style with a growing underline.
1135
- * Default for Warm Editorial and Quiet Luxury; reads as refined and quiet.
1136
- */
1137
- const editorialLink = defineButtonPersonality({
1138
- id: "editorial-link",
1139
- name: "Editorial Link",
1140
- description: "Link-style with a growing underline. Refined, typographic.",
1141
- tags: {
1142
- tone: "minimal",
1143
- energy: "calm",
1144
- motion: "subtle",
1145
- era: "editorial"
1146
- },
1147
- buttonSystem: {
1148
- global: {
1149
- cornerStyle: "square",
1150
- textTransform: "none",
1151
- fontWeight: 500,
1152
- borderWidth: 1,
1153
- hoverTransform: "none",
1154
- hoverColor: "none"
1155
- },
1156
- variants: [
1157
- {
1158
- id: "primary",
1159
- name: "Primary",
1160
- enabled: true,
1161
- priority: 1,
1162
- background: {
1163
- type: "surface",
1164
- role: "link"
1165
- },
1166
- borderRadius: "rounded-none",
1167
- sizes: {
1168
- sm: { padding: "0.25rem 0" },
1169
- md: { padding: "0.5rem 0" },
1170
- lg: { padding: "0.625rem 0" },
1171
- xl: { padding: "0.75rem 0" }
1172
- },
1173
- effects: { base: [{
1174
- effectId: "border-bottom-grow",
1175
- options: {
1176
- height: 2,
1177
- colorToken: "text",
1178
- origin: "left"
1179
- }
1180
- }] }
1181
- },
1182
- {
1183
- id: "secondary",
1184
- name: "Secondary",
1185
- enabled: true,
1186
- priority: 2,
1187
- background: {
1188
- type: "surface",
1189
- role: "link"
1190
- },
1191
- borderRadius: "rounded-none",
1192
- sizes: {
1193
- sm: { padding: "0.25rem 0" },
1194
- md: { padding: "0.5rem 0" },
1195
- lg: { padding: "0.625rem 0" },
1196
- xl: { padding: "0.75rem 0" }
1197
- },
1198
- effects: { base: [{
1199
- effectId: "border-bottom-grow",
1200
- options: {
1201
- height: 1,
1202
- colorToken: "mutedText",
1203
- origin: "left"
1204
- }
1205
- }] }
1206
- },
1207
- {
1208
- id: "outline",
1209
- name: "Outline",
1210
- enabled: true,
1211
- priority: 3,
1212
- background: {
1213
- type: "surface",
1214
- role: "link"
1215
- },
1216
- borderRadius: "rounded-none",
1217
- sizes: {
1218
- sm: { padding: "0.25rem 0" },
1219
- md: { padding: "0.5rem 0" },
1220
- lg: { padding: "0.625rem 0" },
1221
- xl: { padding: "0.75rem 0" }
1222
- },
1223
- effects: { base: [{
1224
- effectId: "border-bottom-grow",
1225
- options: {
1226
- height: 1,
1227
- colorToken: "accent",
1228
- origin: "left"
1229
- }
1230
- }] }
1231
2005
  },
1232
- {
1233
- id: "ghost",
1234
- name: "Ghost",
1235
- enabled: true,
1236
- priority: 3,
1237
- background: {
1238
- type: "surface",
1239
- role: "link"
1240
- },
1241
- borderRadius: "rounded-none",
1242
- sizes: {
1243
- sm: { padding: "0.25rem 0" },
1244
- md: { padding: "0.5rem 0" },
1245
- lg: { padding: "0.625rem 0" },
1246
- xl: { padding: "0.75rem 0" }
1247
- }
2006
+ accent: {
2007
+ kind: "derive",
2008
+ from: "primary",
2009
+ overrides: { background: {
2010
+ type: "solid",
2011
+ colorToken: "accent"
2012
+ } }
1248
2013
  }
1249
- ]
2014
+ }
1250
2015
  }
1251
2016
  });
1252
- //#endregion
1253
- //#region ../theme-core/src/buttons/personalities/flat-ink.ts
1254
- /**
1255
- * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
1256
- * amber accent climax, all completely flat (no drop shadow) with a modest
1257
- * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
1258
- * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
1259
- * every variant lifts 2px; the amber accent is meant to be used sparingly as
1260
- * the page's one conversion climax.
1261
- */
1262
- const flatInk = defineButtonPersonality({
1263
- id: "flat-ink",
1264
- name: "Flat Ink",
1265
- description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
2017
+ const confidentChipRecipe = defineStaticButtonStyleRecipe({
2018
+ id: "confident-chip",
2019
+ accessibilityLabel: "Moderately rounded solid buttons with a crisp hover",
1266
2020
  tags: {
1267
2021
  tone: "classic",
1268
2022
  energy: "calm",
1269
- motion: "subtle",
1270
- era: "editorial"
2023
+ motion: "standard",
2024
+ era: "modern"
1271
2025
  },
1272
- buttonSystem: {
2026
+ system: {
1273
2027
  global: {
1274
2028
  cornerStyle: "rounded",
2029
+ shadow: "medium",
1275
2030
  textTransform: "none",
1276
2031
  fontWeight: 600,
1277
- borderWidth: 1,
1278
- typography: "body",
1279
- letterSpacing: "tight",
1280
- paddingPreset: "default",
1281
- hoverTransform: "lift",
2032
+ hoverTransform: "none",
1282
2033
  hoverColor: "none"
1283
2034
  },
1284
- sizes: {
1285
- sm: {
1286
- padding: "0.625rem 1.125rem",
1287
- fontSize: "text-sm"
2035
+ roles: {
2036
+ primary: {
2037
+ kind: "complete",
2038
+ appearance: {
2039
+ background: {
2040
+ type: "solid",
2041
+ colorToken: "primary"
2042
+ },
2043
+ borderRadius: "rounded-md",
2044
+ shadow: {
2045
+ elevation: "medium",
2046
+ softness: "soft",
2047
+ position: "bottom"
2048
+ },
2049
+ effects: {
2050
+ hover: [{
2051
+ effectId: "darken-background",
2052
+ options: { amount: 100 }
2053
+ }],
2054
+ active: [{
2055
+ effectId: "scale-down",
2056
+ options: { scale: "0.97" }
2057
+ }]
2058
+ }
2059
+ }
1288
2060
  },
1289
- md: { padding: "0.8125rem 1.375rem" },
1290
- lg: {
1291
- padding: "0.9375rem 1.625rem",
1292
- fontSize: "text-lg"
2061
+ secondary: {
2062
+ kind: "complete",
2063
+ appearance: {
2064
+ background: {
2065
+ type: "solid",
2066
+ colorToken: "surface"
2067
+ },
2068
+ borderRadius: "rounded-md",
2069
+ border: { widthClass: "border" },
2070
+ effects: { hover: [{
2071
+ effectId: "background-on-hover",
2072
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
2073
+ }] }
2074
+ }
1293
2075
  },
1294
- xl: {
1295
- padding: "1.0625rem 2rem",
1296
- fontSize: "text-xl"
1297
- }
1298
- },
1299
- variants: [
1300
- {
1301
- id: "primary",
1302
- name: "Primary",
1303
- enabled: true,
1304
- priority: 1,
1305
- background: {
1306
- type: "solid",
1307
- colorToken: "primary"
1308
- },
1309
- borderRadius: "rounded-lg",
1310
- affordance: { iconId: "ArrowRight" },
1311
- effects: { hover: [{
1312
- effectId: "lift",
1313
- options: { translateY: "-2px" }
1314
- }, {
1315
- effectId: "background-on-hover",
1316
- options: { colorToken: "secondary" }
1317
- }] }
2076
+ outline: {
2077
+ kind: "complete",
2078
+ appearance: {
2079
+ background: {
2080
+ type: "surface",
2081
+ role: "link"
2082
+ },
2083
+ borderRadius: "rounded-md",
2084
+ border: { widthClass: "border" },
2085
+ effects: { hover: [{
2086
+ effectId: "background-on-hover",
2087
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2088
+ }] }
2089
+ }
1318
2090
  },
1319
- {
1320
- id: "secondary",
1321
- name: "Secondary",
1322
- enabled: true,
1323
- priority: 2,
1324
- background: {
1325
- type: "surface",
1326
- role: "link"
1327
- },
1328
- borderRadius: "rounded-lg",
1329
- border: { widthClass: "border" },
1330
- effects: { hover: [{
1331
- effectId: "lift",
1332
- options: { translateY: "-2px" }
1333
- }, {
1334
- effectId: "background-on-hover",
1335
- options: { colorToken: "surfaceAlt" }
1336
- }] }
2091
+ ghost: {
2092
+ kind: "complete",
2093
+ appearance: {
2094
+ background: {
2095
+ type: "surface",
2096
+ role: "link"
2097
+ },
2098
+ borderRadius: "rounded-md",
2099
+ effects: { hover: [{
2100
+ effectId: "background-on-hover",
2101
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2102
+ }] }
2103
+ }
1337
2104
  },
1338
- {
1339
- id: "accent",
1340
- name: "Accent",
1341
- enabled: true,
1342
- priority: 3,
1343
- background: {
2105
+ accent: {
2106
+ kind: "derive",
2107
+ from: "primary",
2108
+ overrides: { background: {
1344
2109
  type: "solid",
1345
2110
  colorToken: "accent"
1346
- },
1347
- borderRadius: "rounded-lg",
1348
- affordance: { iconId: "ArrowRight" },
1349
- effects: { hover: [{
1350
- effectId: "lift",
1351
- options: { translateY: "-2px" }
1352
- }] }
2111
+ } }
1353
2112
  }
1354
- ]
2113
+ }
1355
2114
  }
1356
2115
  });
1357
- //#endregion
1358
- //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
1359
- /**
1360
- * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
1361
- * feel. Reads as bold and handmade; suits expressive craft brands.
1362
- */
1363
- const inkStamp = defineButtonPersonality({
1364
- id: "ink-stamp",
1365
- name: "Ink Stamp",
1366
- description: "Hand-stamped block-print buttons with a slight lift on hover.",
2116
+ const editorialSizes = {
2117
+ sm: { padding: "0.25rem 0" },
2118
+ md: { padding: "0.5rem 0" },
2119
+ lg: { padding: "0.625rem 0" },
2120
+ xl: { padding: "0.75rem 0" }
2121
+ };
2122
+ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
2123
+ id: "editorial-link",
2124
+ accessibilityLabel: "Flat typographic buttons with an underline treatment",
1367
2125
  tags: {
1368
- tone: "bold",
1369
- energy: "vibrant",
1370
- motion: "standard",
1371
- era: "organic"
2126
+ tone: "minimal",
2127
+ energy: "calm",
2128
+ motion: "subtle",
2129
+ era: "editorial"
1372
2130
  },
1373
- buttonSystem: {
2131
+ system: {
1374
2132
  global: {
1375
- cornerStyle: "rounded",
1376
- textTransform: "uppercase",
1377
- fontWeight: 600,
1378
- borderWidth: 1,
2133
+ cornerStyle: "square",
2134
+ textTransform: "none",
2135
+ fontWeight: 500,
1379
2136
  hoverTransform: "none",
1380
- hoverColor: "none",
1381
- letterSpacing: "loose"
1382
- },
1383
- sizes: {
1384
- sm: {
1385
- padding: "0.5rem 1.25rem",
1386
- fontSize: "text-xs"
1387
- },
1388
- md: {
1389
- padding: "0.75rem 1.75rem",
1390
- fontSize: "text-sm"
1391
- },
1392
- lg: {
1393
- padding: "0.875rem 2.25rem",
1394
- fontSize: "text-base"
1395
- },
1396
- xl: {
1397
- padding: "1.125rem 2.75rem",
1398
- fontSize: "text-lg"
1399
- }
2137
+ hoverColor: "none"
1400
2138
  },
1401
- variants: [
1402
- {
1403
- id: "primary",
1404
- name: "Primary",
1405
- enabled: true,
1406
- priority: 1,
1407
- background: {
1408
- type: "solid",
1409
- colorToken: "primary"
1410
- },
1411
- borderRadius: "rounded-md",
1412
- effects: {
1413
- hover: [{
1414
- effectId: "drop-shadow",
1415
- options: { intensity: "medium" }
1416
- }, {
1417
- effectId: "scale-up",
1418
- options: { scale: "1.02" }
1419
- }],
1420
- active: [{
1421
- effectId: "scale-down",
1422
- options: { scale: "0.98" }
1423
- }]
2139
+ roles: {
2140
+ primary: {
2141
+ kind: "complete",
2142
+ appearance: {
2143
+ background: {
2144
+ type: "surface",
2145
+ role: "link"
2146
+ },
2147
+ borderRadius: "rounded-none",
2148
+ sizes: editorialSizes,
2149
+ effects: { base: [{
2150
+ effectId: "border-bottom-grow",
2151
+ options: {
2152
+ height: 2,
2153
+ origin: "left"
2154
+ }
2155
+ }] }
1424
2156
  }
1425
2157
  },
1426
- {
1427
- id: "secondary",
1428
- name: "Secondary",
1429
- enabled: true,
1430
- priority: 2,
1431
- background: {
1432
- type: "surface",
1433
- role: "link"
1434
- },
1435
- borderRadius: "rounded-md",
1436
- border: { widthClass: "border-2" },
1437
- effects: { hover: [{
1438
- effectId: "background-on-hover",
1439
- options: { colorToken: "surface" }
1440
- }] }
1441
- },
1442
- {
1443
- id: "outline",
1444
- name: "Outline",
1445
- enabled: true,
1446
- priority: 3,
1447
- background: {
1448
- type: "surface",
1449
- role: "link"
1450
- },
1451
- borderRadius: "rounded-md",
1452
- border: { widthClass: "border-2" },
1453
- effects: { hover: [{
1454
- effectId: "background-on-hover",
1455
- options: { colorToken: "surface" }
1456
- }] }
1457
- },
1458
- {
1459
- id: "ghost",
1460
- name: "Ghost",
1461
- enabled: true,
1462
- priority: 3,
1463
- background: {
1464
- type: "surface",
1465
- role: "link"
1466
- },
1467
- borderRadius: "rounded-none",
1468
- sizes: {
1469
- sm: {
1470
- padding: "0.375rem 0",
1471
- fontSize: "text-xs"
2158
+ secondary: {
2159
+ kind: "complete",
2160
+ appearance: {
2161
+ background: {
2162
+ type: "surface",
2163
+ role: "link"
1472
2164
  },
1473
- md: {
1474
- padding: "0.5rem 0",
1475
- fontSize: "text-sm"
2165
+ borderRadius: "rounded-none",
2166
+ sizes: editorialSizes,
2167
+ effects: { base: [{
2168
+ effectId: "border-bottom-grow",
2169
+ options: {
2170
+ height: 1,
2171
+ origin: "left"
2172
+ }
2173
+ }] }
2174
+ }
2175
+ },
2176
+ outline: {
2177
+ kind: "complete",
2178
+ appearance: {
2179
+ background: {
2180
+ type: "surface",
2181
+ role: "link"
1476
2182
  },
1477
- lg: {
1478
- padding: "0.625rem 0",
1479
- fontSize: "text-base"
2183
+ borderRadius: "rounded-none",
2184
+ sizes: editorialSizes,
2185
+ effects: { base: [{
2186
+ effectId: "border-bottom-grow",
2187
+ options: {
2188
+ height: 1,
2189
+ origin: "left"
2190
+ }
2191
+ }] }
2192
+ }
2193
+ },
2194
+ ghost: {
2195
+ kind: "complete",
2196
+ appearance: {
2197
+ background: {
2198
+ type: "surface",
2199
+ role: "link"
1480
2200
  },
1481
- xl: {
1482
- padding: "0.75rem 0",
1483
- fontSize: "text-lg"
1484
- }
1485
- },
1486
- effects: { base: [{
2201
+ borderRadius: "rounded-none",
2202
+ sizes: editorialSizes
2203
+ }
2204
+ },
2205
+ accent: {
2206
+ kind: "derive",
2207
+ from: "primary",
2208
+ overrides: { effects: { base: [{
1487
2209
  effectId: "border-bottom-grow",
1488
2210
  options: {
1489
- height: 1,
1490
- colorToken: "accent",
2211
+ height: 2,
1491
2212
  origin: "left"
1492
2213
  }
1493
- }] }
2214
+ }] } }
1494
2215
  }
1495
- ]
2216
+ }
1496
2217
  }
1497
2218
  });
1498
- //#endregion
1499
- //#region ../theme-core/src/buttons/personalities/joy-stamp.ts
1500
- /**
1501
- * Joy Stamp — neo-brutalist pills with a hard, brand-coloured offset shadow
1502
- * that collapses as the button presses into it on hover. Authored for the
1503
- * Original Joy site style: a cream "stamp" shadow under the solid primary, and
1504
- * a brown shadow under the cream outline. Reads as bold, warm, and handmade.
1505
- *
1506
- * The coloured offset shadow relies on the per-variant `shadow.colorToken`
1507
- * extension; the press is the shared `press-down` effect, which matches its
1508
- * translate to each variant's own shadow offset.
1509
- */
1510
- const joyStamp = defineButtonPersonality({
1511
- id: "joy-stamp",
1512
- name: "Joy Stamp",
1513
- description: "Pill buttons with a hard, brand-coloured offset shadow that presses in on hover.",
2219
+ const pebbleRecipe = defineStaticButtonStyleRecipe({
2220
+ id: "pebble",
2221
+ accessibilityLabel: "Generously padded pill buttons",
1514
2222
  tags: {
1515
- tone: "bold",
1516
- energy: "vibrant",
1517
- motion: "expressive",
2223
+ tone: "classic",
2224
+ energy: "calm",
2225
+ motion: "standard",
1518
2226
  era: "organic"
1519
2227
  },
1520
- buttonSystem: {
2228
+ system: {
1521
2229
  global: {
1522
2230
  cornerStyle: "pill",
1523
- shadow: "high",
1524
2231
  textTransform: "none",
1525
- typography: "heading",
1526
- fontWeight: 700,
1527
- fontSize: "text-lg",
1528
- borderWidth: 2,
2232
+ fontWeight: 500,
1529
2233
  hoverTransform: "none",
1530
2234
  hoverColor: "none"
1531
2235
  },
1532
2236
  sizes: {
1533
2237
  sm: {
1534
- padding: "0.625rem 1.25rem",
1535
- fontSize: "text-base"
1536
- },
1537
- md: {
1538
- padding: "0.875rem 1.75rem",
1539
- fontSize: "text-lg"
2238
+ padding: "0.625rem 1.75rem",
2239
+ fontSize: "text-sm"
1540
2240
  },
2241
+ md: { padding: "0.875rem 2.25rem" },
1541
2242
  lg: {
1542
- padding: "1.125rem 2.25rem",
1543
- fontSize: "text-xl"
2243
+ padding: "1.125rem 2.75rem",
2244
+ fontSize: "text-lg"
1544
2245
  },
1545
2246
  xl: {
1546
- padding: "1.25rem 2.75rem",
1547
- fontSize: "text-2xl"
2247
+ padding: "1.5rem 3.5rem",
2248
+ fontSize: "text-xl"
1548
2249
  }
1549
2250
  },
1550
- variants: [{
1551
- id: "primary",
1552
- name: "Primary",
1553
- enabled: true,
1554
- priority: 1,
1555
- background: {
1556
- type: "solid",
1557
- colorToken: "primary"
2251
+ roles: {
2252
+ primary: {
2253
+ kind: "complete",
2254
+ appearance: {
2255
+ background: {
2256
+ type: "solid",
2257
+ colorToken: "primary"
2258
+ },
2259
+ borderRadius: "rounded-full",
2260
+ effects: { hover: [{
2261
+ effectId: "darken-background",
2262
+ options: { amount: 100 }
2263
+ }] }
2264
+ }
1558
2265
  },
1559
- borderRadius: "rounded-full",
1560
- shadow: {
1561
- elevation: "high",
1562
- softness: "hard",
1563
- position: "bottom-right",
1564
- colorToken: "text"
2266
+ secondary: {
2267
+ kind: "complete",
2268
+ appearance: {
2269
+ background: {
2270
+ type: "surface",
2271
+ role: "link"
2272
+ },
2273
+ borderRadius: "rounded-full",
2274
+ border: { widthClass: "border-2" },
2275
+ effects: { hover: [{
2276
+ effectId: "background-on-hover",
2277
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
2278
+ }] }
2279
+ }
1565
2280
  },
1566
- effects: { hover: [{ effectId: "press-down" }] }
1567
- }, {
1568
- id: "secondary",
1569
- name: "Secondary",
1570
- enabled: true,
1571
- priority: 2,
1572
- background: {
1573
- type: "surface",
1574
- role: "link"
2281
+ outline: {
2282
+ kind: "complete",
2283
+ appearance: {
2284
+ background: {
2285
+ type: "surface",
2286
+ role: "link"
2287
+ },
2288
+ borderRadius: "rounded-full",
2289
+ border: { widthClass: "border" },
2290
+ effects: { hover: [{
2291
+ effectId: "background-on-hover",
2292
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2293
+ }] }
2294
+ }
1575
2295
  },
1576
- borderRadius: "rounded-full",
1577
- border: { widthClass: "border-2" },
1578
- shadow: {
1579
- elevation: "high",
1580
- softness: "hard",
1581
- position: "bottom-right",
1582
- colorToken: "secondary"
2296
+ ghost: {
2297
+ kind: "complete",
2298
+ appearance: {
2299
+ background: {
2300
+ type: "surface",
2301
+ role: "link"
2302
+ },
2303
+ borderRadius: "rounded-full",
2304
+ sizes: {
2305
+ sm: {
2306
+ padding: "0.375rem 0.875rem",
2307
+ fontSize: "text-sm"
2308
+ },
2309
+ md: { padding: "0.5rem 1rem" },
2310
+ lg: {
2311
+ padding: "0.625rem 1.25rem",
2312
+ fontSize: "text-lg"
2313
+ },
2314
+ xl: {
2315
+ padding: "0.75rem 1.5rem",
2316
+ fontSize: "text-xl"
2317
+ }
2318
+ }
2319
+ }
1583
2320
  },
1584
- effects: { hover: [{ effectId: "press-down" }] }
1585
- }]
2321
+ accent: {
2322
+ kind: "derive",
2323
+ from: "primary",
2324
+ overrides: { background: {
2325
+ type: "solid",
2326
+ colorToken: "accent"
2327
+ } }
2328
+ }
2329
+ }
1586
2330
  }
1587
2331
  });
1588
- //#endregion
1589
- //#region ../theme-core/src/buttons/personalities/ornate-ritual.ts
1590
- /**
1591
- * Ornate Ritual — framed, ceremonial buttons with jewel-toned fills and
1592
- * deliberate spacing. Designed for music, ceremony, and soulful event sites.
1593
- */
1594
- const ornateRitual = defineButtonPersonality({
1595
- id: "ornate-ritual",
1596
- name: "Ornate Ritual",
1597
- description: "Ceremonial framed buttons with jewel-toned fills and quiet color-change hover states.",
2332
+ const brushedWashRecipe = defineStaticButtonStyleRecipe({
2333
+ id: "brushed-wash",
2334
+ accessibilityLabel: "Generously padded gradient buttons with soft shadows",
1598
2335
  tags: {
1599
2336
  tone: "classic",
1600
2337
  energy: "vibrant",
1601
- motion: "subtle",
2338
+ motion: "standard",
1602
2339
  era: "organic"
1603
2340
  },
1604
- buttonSystem: {
2341
+ system: {
1605
2342
  global: {
1606
2343
  cornerStyle: "rounded",
1607
- textTransform: "uppercase",
1608
- fontWeight: 600,
1609
- borderWidth: 1,
2344
+ shadow: "low",
2345
+ textTransform: "none",
2346
+ fontWeight: 500,
1610
2347
  hoverTransform: "none",
1611
- hoverColor: "none",
1612
- letterSpacing: "loose"
2348
+ hoverColor: "none"
1613
2349
  },
1614
2350
  sizes: {
1615
2351
  sm: {
1616
- padding: "0.5625rem 1.25rem",
1617
- fontSize: "text-xs"
1618
- },
1619
- md: {
1620
- padding: "0.75rem 1.75rem",
1621
- fontSize: "text-xs"
2352
+ padding: "0.75rem 2rem",
2353
+ fontSize: "text-sm"
1622
2354
  },
2355
+ md: { padding: "1rem 2.5rem" },
1623
2356
  lg: {
1624
- padding: "0.875rem 2.25rem",
1625
- fontSize: "text-sm"
2357
+ padding: "1.25rem 3rem",
2358
+ fontSize: "text-lg"
1626
2359
  },
1627
2360
  xl: {
1628
- padding: "1rem 2.75rem",
1629
- fontSize: "text-base"
2361
+ padding: "1.625rem 3.75rem",
2362
+ fontSize: "text-xl"
1630
2363
  }
1631
2364
  },
1632
- variants: [
1633
- {
1634
- id: "primary",
1635
- name: "Primary",
1636
- enabled: true,
1637
- priority: 1,
1638
- background: {
1639
- type: "solid",
1640
- colorToken: "primary"
1641
- },
1642
- borderRadius: "rounded-md",
1643
- border: { widthClass: "border" },
1644
- effects: {
1645
- hover: [{
1646
- effectId: "background-on-hover",
1647
- options: { colorToken: "primary-100" }
1648
- }],
1649
- active: [{
1650
- effectId: "scale-down",
1651
- options: { scale: "0.98" }
1652
- }]
2365
+ roles: {
2366
+ primary: {
2367
+ kind: "complete",
2368
+ appearance: {
2369
+ background: {
2370
+ type: "gradient",
2371
+ stops: ["primary", "accent"],
2372
+ direction: "to-r",
2373
+ foregroundToken: "primaryForeground"
2374
+ },
2375
+ borderRadius: "rounded-lg",
2376
+ shadow: {
2377
+ elevation: "low",
2378
+ softness: "soft",
2379
+ position: "bottom"
2380
+ }
1653
2381
  }
1654
2382
  },
1655
- {
1656
- id: "secondary",
1657
- name: "Secondary",
1658
- enabled: true,
1659
- priority: 2,
1660
- background: {
1661
- type: "solid",
1662
- colorToken: "secondary"
1663
- },
1664
- borderRadius: "rounded-md",
1665
- border: { widthClass: "border" },
1666
- effects: { hover: [{
1667
- effectId: "background-on-hover",
1668
- options: { colorToken: "surface" }
1669
- }] }
1670
- },
1671
- {
1672
- id: "outline",
1673
- name: "Outline",
1674
- enabled: true,
1675
- priority: 3,
1676
- background: {
1677
- type: "surface",
1678
- role: "link"
1679
- },
1680
- borderRadius: "rounded-md",
1681
- border: { widthClass: "border" },
1682
- effects: { hover: [{
1683
- effectId: "background-on-hover",
1684
- options: { colorToken: "muted" }
1685
- }] }
1686
- },
1687
- {
1688
- id: "ghost",
1689
- name: "Ghost",
1690
- enabled: true,
1691
- priority: 3,
1692
- background: {
1693
- type: "surface",
1694
- role: "link"
1695
- },
1696
- borderRadius: "rounded-none",
1697
- sizes: {
1698
- sm: {
1699
- padding: "0.375rem 0",
1700
- fontSize: "text-xs"
1701
- },
1702
- md: {
1703
- padding: "0.5rem 0",
1704
- fontSize: "text-sm"
2383
+ secondary: {
2384
+ kind: "complete",
2385
+ appearance: {
2386
+ background: {
2387
+ type: "gradient",
2388
+ stops: ["secondary", "accent"],
2389
+ direction: "to-br",
2390
+ foregroundToken: "primaryForeground"
1705
2391
  },
1706
- lg: {
1707
- padding: "0.625rem 0",
1708
- fontSize: "text-base"
1709
- },
1710
- xl: {
1711
- padding: "0.75rem 0",
1712
- fontSize: "text-lg"
1713
- }
1714
- },
1715
- effects: { base: [{
1716
- effectId: "border-bottom-grow",
1717
- options: {
1718
- height: 1,
1719
- colorToken: "accent",
1720
- origin: "left"
2392
+ borderRadius: "rounded-lg",
2393
+ shadow: {
2394
+ elevation: "low",
2395
+ softness: "soft",
2396
+ position: "bottom"
1721
2397
  }
1722
- }] }
2398
+ }
2399
+ },
2400
+ outline: {
2401
+ kind: "complete",
2402
+ appearance: {
2403
+ background: {
2404
+ type: "surface",
2405
+ role: "link"
2406
+ },
2407
+ borderRadius: "rounded-lg",
2408
+ border: { widthClass: "border" },
2409
+ effects: { hover: [{
2410
+ effectId: "background-on-hover",
2411
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2412
+ }] }
2413
+ }
2414
+ },
2415
+ ghost: {
2416
+ kind: "complete",
2417
+ appearance: {
2418
+ background: {
2419
+ type: "surface",
2420
+ role: "link"
2421
+ },
2422
+ borderRadius: "rounded-lg",
2423
+ effects: { hover: [{
2424
+ effectId: "background-on-hover",
2425
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2426
+ }] }
2427
+ }
2428
+ },
2429
+ accent: {
2430
+ kind: "derive",
2431
+ from: "primary",
2432
+ overrides: { background: {
2433
+ type: "solid",
2434
+ colorToken: "accent"
2435
+ } }
1723
2436
  }
1724
- ]
2437
+ }
1725
2438
  }
1726
2439
  });
1727
- //#endregion
1728
- //#region ../theme-core/src/buttons/personalities/pebble.ts
1729
- /**
1730
- * Pebble — large pill primaries with generous padding. Reads as spa-luxurious
1731
- * and natural; pairs well with wellness/yoga/dance brands.
1732
- */
1733
- const pebble = defineButtonPersonality({
1734
- id: "pebble",
1735
- name: "Pebble",
1736
- description: "Generous pill buttons with a quiet colour-softening hover.",
2440
+ const inkStampRecipe = defineStaticButtonStyleRecipe({
2441
+ id: "ink-stamp",
2442
+ accessibilityLabel: "Uppercase block-print buttons with a playful hover",
1737
2443
  tags: {
1738
- tone: "classic",
1739
- energy: "calm",
2444
+ tone: "bold",
2445
+ energy: "vibrant",
1740
2446
  motion: "standard",
1741
2447
  era: "organic"
1742
2448
  },
1743
- buttonSystem: {
2449
+ system: {
1744
2450
  global: {
1745
- cornerStyle: "pill",
1746
- textTransform: "none",
1747
- fontWeight: 500,
1748
- borderWidth: 2,
2451
+ cornerStyle: "rounded",
2452
+ textTransform: "uppercase",
2453
+ fontWeight: 600,
1749
2454
  hoverTransform: "none",
1750
- hoverColor: "none"
2455
+ hoverColor: "none",
2456
+ letterSpacing: "loose"
1751
2457
  },
1752
2458
  sizes: {
1753
2459
  sm: {
1754
- padding: "0.625rem 1.75rem",
2460
+ padding: "0.5rem 1.25rem",
2461
+ fontSize: "text-xs"
2462
+ },
2463
+ md: {
2464
+ padding: "0.75rem 1.75rem",
1755
2465
  fontSize: "text-sm"
1756
2466
  },
1757
- md: { padding: "0.875rem 2.25rem" },
1758
2467
  lg: {
1759
- padding: "1.125rem 2.75rem",
1760
- fontSize: "text-lg"
2468
+ padding: "0.875rem 2.25rem",
2469
+ fontSize: "text-base"
1761
2470
  },
1762
2471
  xl: {
1763
- padding: "1.5rem 3.5rem",
1764
- fontSize: "text-xl"
2472
+ padding: "1.125rem 2.75rem",
2473
+ fontSize: "text-lg"
1765
2474
  }
1766
2475
  },
1767
- variants: [
1768
- {
1769
- id: "primary",
1770
- name: "Primary",
1771
- enabled: true,
1772
- priority: 1,
1773
- background: {
1774
- type: "solid",
1775
- colorToken: "primary"
1776
- },
1777
- borderRadius: "rounded-full",
1778
- effects: { hover: [{
1779
- effectId: "darken-background",
1780
- options: { amount: 100 }
1781
- }] }
2476
+ roles: {
2477
+ primary: {
2478
+ kind: "complete",
2479
+ appearance: {
2480
+ background: {
2481
+ type: "solid",
2482
+ colorToken: "primary"
2483
+ },
2484
+ borderRadius: "rounded-md",
2485
+ effects: {
2486
+ hover: [{
2487
+ effectId: "drop-shadow",
2488
+ options: { intensity: "medium" }
2489
+ }, {
2490
+ effectId: "scale-up",
2491
+ options: { scale: "1.02" }
2492
+ }],
2493
+ active: [{
2494
+ effectId: "scale-down",
2495
+ options: { scale: "0.98" }
2496
+ }]
2497
+ }
2498
+ }
1782
2499
  },
1783
- {
1784
- id: "secondary",
1785
- name: "Secondary",
1786
- enabled: true,
1787
- priority: 2,
1788
- background: {
1789
- type: "surface",
1790
- role: "link"
1791
- },
1792
- borderRadius: "rounded-full",
1793
- border: { widthClass: "border-2" },
1794
- effects: { hover: [{
1795
- effectId: "background-on-hover",
1796
- options: { colorToken: "accent" }
1797
- }] }
2500
+ secondary: {
2501
+ kind: "complete",
2502
+ appearance: {
2503
+ background: {
2504
+ type: "surface",
2505
+ role: "link"
2506
+ },
2507
+ borderRadius: "rounded-md",
2508
+ border: { widthClass: "border-2" },
2509
+ effects: { hover: [{
2510
+ effectId: "background-on-hover",
2511
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2512
+ }] }
2513
+ }
1798
2514
  },
1799
- {
1800
- id: "outline",
1801
- name: "Outline",
1802
- enabled: true,
1803
- priority: 3,
1804
- background: {
1805
- type: "surface",
1806
- role: "link"
1807
- },
1808
- borderRadius: "rounded-full",
1809
- border: { widthClass: "border" },
1810
- effects: { hover: [{
1811
- effectId: "background-on-hover",
1812
- options: { colorToken: "surface" }
1813
- }] }
2515
+ outline: {
2516
+ kind: "complete",
2517
+ appearance: {
2518
+ background: {
2519
+ type: "surface",
2520
+ role: "link"
2521
+ },
2522
+ borderRadius: "rounded-md",
2523
+ border: { widthClass: "border-2" },
2524
+ effects: { hover: [{
2525
+ effectId: "background-on-hover",
2526
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2527
+ }] }
2528
+ }
1814
2529
  },
1815
- {
1816
- id: "ghost",
1817
- name: "Ghost",
1818
- enabled: true,
1819
- priority: 3,
1820
- background: {
1821
- type: "surface",
1822
- role: "link"
1823
- },
1824
- borderRadius: "rounded-full",
1825
- sizes: {
1826
- sm: {
1827
- padding: "0.375rem 0.875rem",
1828
- fontSize: "text-sm"
2530
+ ghost: {
2531
+ kind: "complete",
2532
+ appearance: {
2533
+ background: {
2534
+ type: "surface",
2535
+ role: "link"
1829
2536
  },
1830
- md: { padding: "0.5rem 1rem" },
1831
- lg: {
1832
- padding: "0.625rem 1.25rem",
1833
- fontSize: "text-lg"
2537
+ borderRadius: "rounded-none",
2538
+ sizes: {
2539
+ sm: {
2540
+ padding: "0.375rem 0",
2541
+ fontSize: "text-xs"
2542
+ },
2543
+ md: {
2544
+ padding: "0.5rem 0",
2545
+ fontSize: "text-sm"
2546
+ },
2547
+ lg: {
2548
+ padding: "0.625rem 0",
2549
+ fontSize: "text-base"
2550
+ },
2551
+ xl: {
2552
+ padding: "0.75rem 0",
2553
+ fontSize: "text-lg"
2554
+ }
1834
2555
  },
1835
- xl: {
1836
- padding: "0.75rem 1.5rem",
1837
- fontSize: "text-xl"
1838
- }
2556
+ effects: { base: [{
2557
+ effectId: "border-bottom-grow",
2558
+ options: {
2559
+ height: 1,
2560
+ origin: "left"
2561
+ }
2562
+ }] }
1839
2563
  }
2564
+ },
2565
+ accent: {
2566
+ kind: "derive",
2567
+ from: "primary",
2568
+ overrides: { background: {
2569
+ type: "solid",
2570
+ colorToken: "accent"
2571
+ } }
1840
2572
  }
1841
- ]
2573
+ }
1842
2574
  }
1843
2575
  });
1844
- //#endregion
1845
- //#region ../theme-core/src/buttons/personalities/quiet-arrow.ts
1846
- /**
1847
- * Quiet Arrow — wide, flat editorial buttons for grounded service businesses.
1848
- * Primary and secondary keep stable semantic colours; surrounding surfaces are
1849
- * responsible for contrast rather than reversing the button roles.
1850
- */
1851
- const quietArrow = defineButtonPersonality({
1852
- id: "quiet-arrow",
1853
- name: "Quiet Arrow",
1854
- description: "Wide, flat arrow buttons with a solid primary and a primary-colour outlined secondary.",
2576
+ const flatInkRecipe = defineStaticButtonStyleRecipe({
2577
+ id: "flat-ink",
2578
+ accessibilityLabel: "Flat rounded buttons with arrow affordances",
1855
2579
  tags: {
1856
2580
  tone: "classic",
1857
2581
  energy: "calm",
1858
2582
  motion: "subtle",
1859
2583
  era: "editorial"
1860
2584
  },
1861
- buttonSystem: {
2585
+ system: {
1862
2586
  global: {
1863
2587
  cornerStyle: "rounded",
1864
- textTransform: "uppercase",
1865
- fontWeight: 500,
1866
- borderWidth: 1,
2588
+ textTransform: "none",
2589
+ fontWeight: 600,
1867
2590
  typography: "body",
1868
- fontSize: "text-sm",
1869
- letterSpacing: "loose",
1870
- hoverTransform: "none",
2591
+ letterSpacing: "tight",
2592
+ paddingPreset: "default",
2593
+ hoverTransform: "lift",
1871
2594
  hoverColor: "none"
1872
2595
  },
1873
2596
  sizes: {
1874
2597
  sm: {
1875
- padding: "0.625rem 1.25rem",
1876
- fontSize: "text-xs"
1877
- },
1878
- md: {
1879
- padding: "0.8125rem 1.75rem",
2598
+ padding: "0.625rem 1.125rem",
1880
2599
  fontSize: "text-sm"
1881
2600
  },
2601
+ md: { padding: "0.8125rem 1.375rem" },
1882
2602
  lg: {
1883
- padding: "0.9375rem 2.125rem",
1884
- fontSize: "text-sm"
2603
+ padding: "0.9375rem 1.625rem",
2604
+ fontSize: "text-lg"
1885
2605
  },
1886
2606
  xl: {
1887
- padding: "1.0625rem 2.5rem",
1888
- fontSize: "text-base"
2607
+ padding: "1.0625rem 2rem",
2608
+ fontSize: "text-xl"
1889
2609
  }
1890
2610
  },
1891
- variants: [
1892
- {
1893
- id: "primary",
1894
- name: "Primary",
1895
- enabled: true,
1896
- priority: 1,
1897
- background: {
1898
- type: "solid",
1899
- colorToken: "primary"
1900
- },
1901
- borderRadius: "rounded-xl",
1902
- affordance: { iconId: "ArrowRight" },
1903
- effects: { hover: [{
1904
- effectId: "darken-background",
1905
- options: { amount: 100 }
1906
- }] }
2611
+ roles: {
2612
+ primary: {
2613
+ kind: "complete",
2614
+ appearance: {
2615
+ background: {
2616
+ type: "solid",
2617
+ colorToken: "primary"
2618
+ },
2619
+ borderRadius: "rounded-lg",
2620
+ affordance: { iconId: "ArrowRight" },
2621
+ effects: { hover: [{ effectId: "lift" }, {
2622
+ effectId: "background-on-hover",
2623
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("secondary") }
2624
+ }] }
2625
+ }
2626
+ },
2627
+ secondary: {
2628
+ kind: "complete",
2629
+ appearance: {
2630
+ background: {
2631
+ type: "surface",
2632
+ role: "link"
2633
+ },
2634
+ borderRadius: "rounded-lg",
2635
+ border: { widthClass: "border" },
2636
+ effects: { hover: [{ effectId: "lift" }, {
2637
+ effectId: "background-on-hover",
2638
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
2639
+ }] }
2640
+ }
1907
2641
  },
1908
- {
1909
- id: "secondary",
1910
- name: "Secondary",
1911
- enabled: true,
1912
- priority: 2,
1913
- background: {
1914
- type: "surface",
1915
- role: "link"
1916
- },
1917
- borderRadius: "rounded-xl",
1918
- border: { widthClass: "border" },
1919
- affordance: { iconId: "ArrowRight" },
1920
- effects: { hover: [{
1921
- effectId: "background-on-hover",
1922
- options: { colorToken: "surfaceAlt" }
1923
- }] }
2642
+ outline: {
2643
+ kind: "complete",
2644
+ appearance: {
2645
+ background: {
2646
+ type: "surface",
2647
+ role: "link"
2648
+ },
2649
+ borderRadius: "rounded-lg",
2650
+ border: { widthClass: "border" },
2651
+ effects: { hover: [{ effectId: "lift" }, {
2652
+ effectId: "background-on-hover",
2653
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
2654
+ }] }
2655
+ }
1924
2656
  },
1925
- {
1926
- id: "outline",
1927
- name: "Outline",
1928
- enabled: true,
1929
- priority: 3,
1930
- background: {
1931
- type: "surface",
1932
- role: "link"
1933
- },
1934
- borderRadius: "rounded-xl",
1935
- border: { widthClass: "border" }
2657
+ ghost: {
2658
+ kind: "complete",
2659
+ appearance: {
2660
+ background: {
2661
+ type: "surface",
2662
+ role: "link"
2663
+ },
2664
+ borderRadius: "rounded-lg",
2665
+ effects: { hover: [{ effectId: "lift" }] }
2666
+ }
1936
2667
  },
1937
- {
1938
- id: "ghost",
1939
- name: "Ghost",
1940
- enabled: true,
1941
- priority: 3,
1942
- background: {
1943
- type: "surface",
1944
- role: "link"
1945
- },
1946
- borderRadius: "rounded-xl"
2668
+ accent: {
2669
+ kind: "complete",
2670
+ appearance: {
2671
+ background: {
2672
+ type: "solid",
2673
+ colorToken: "accent"
2674
+ },
2675
+ borderRadius: "rounded-lg",
2676
+ affordance: { iconId: "ArrowRight" },
2677
+ effects: { hover: [{ effectId: "lift" }] }
2678
+ }
1947
2679
  }
1948
- ]
2680
+ }
1949
2681
  }
1950
2682
  });
1951
- //#endregion
1952
- //#region ../theme-core/src/buttons/personalities/showtime-pill.ts
1953
- /**
1954
- * Showtime Pill — bold, spacious, fully rounded buttons with playful pop
1955
- * motion. Suits energetic schools, performers, festivals, and community brands
1956
- * that need calls to action to feel loud without becoming visually chaotic.
1957
- */
1958
- const showtimePill = defineButtonPersonality({
2683
+ const showtimePillRecipe = defineStaticButtonStyleRecipe({
1959
2684
  id: "showtime-pill",
1960
- name: "Showtime Pill",
1961
- description: "Bold pill buttons with uppercase labels, generous padding, and a playful pop.",
2685
+ accessibilityLabel: "Bold uppercase pill buttons with playful movement",
1962
2686
  tags: {
1963
2687
  tone: "bold",
1964
2688
  energy: "vibrant",
1965
2689
  motion: "expressive",
1966
2690
  era: "modern"
1967
2691
  },
1968
- buttonSystem: {
2692
+ system: {
1969
2693
  global: {
1970
2694
  cornerStyle: "pill",
1971
2695
  shadow: "low",
1972
2696
  textTransform: "uppercase",
1973
2697
  fontWeight: 700,
1974
- borderWidth: 2,
1975
2698
  hoverTransform: "lift",
1976
- hoverColor: "darken",
2699
+ hoverColor: "none",
1977
2700
  typography: "body",
1978
2701
  paddingPreset: "spacious",
1979
2702
  italic: false
@@ -1996,272 +2719,373 @@ const showtimePill = defineButtonPersonality({
1996
2719
  fontSize: "1.125rem"
1997
2720
  }
1998
2721
  },
1999
- variants: [
2000
- {
2001
- id: "primary",
2002
- name: "Primary",
2003
- enabled: true,
2004
- priority: 1,
2005
- background: {
2006
- type: "solid",
2007
- colorToken: "primary"
2008
- },
2009
- borderRadius: "rounded-full",
2010
- effects: { hover: [{ effectId: "pop" }] }
2011
- },
2012
- {
2013
- id: "secondary",
2014
- name: "Secondary",
2015
- enabled: true,
2016
- priority: 2,
2017
- background: {
2018
- type: "surface",
2019
- role: "link"
2020
- },
2021
- borderRadius: "rounded-full",
2022
- border: { widthClass: "border-2" },
2023
- effects: { hover: [{ effectId: "pop" }] }
2722
+ roles: {
2723
+ primary: {
2724
+ kind: "complete",
2725
+ appearance: {
2726
+ background: {
2727
+ type: "solid",
2728
+ colorToken: "primary"
2729
+ },
2730
+ borderRadius: "rounded-full",
2731
+ effects: { hover: [{ effectId: "pop" }] }
2732
+ }
2024
2733
  },
2025
- {
2026
- id: "hero",
2027
- name: "Hero",
2028
- enabled: true,
2029
- priority: 1,
2030
- background: {
2031
- type: "solid",
2032
- colorToken: "secondary"
2033
- },
2034
- borderRadius: "rounded-full",
2035
- effects: { hover: [{ effectId: "pop" }] }
2734
+ secondary: {
2735
+ kind: "complete",
2736
+ appearance: {
2737
+ background: {
2738
+ type: "surface",
2739
+ role: "link"
2740
+ },
2741
+ borderRadius: "rounded-full",
2742
+ border: { widthClass: "border-2" },
2743
+ effects: { hover: [{ effectId: "pop" }] }
2744
+ }
2036
2745
  },
2037
- {
2038
- id: "header",
2039
- name: "Header",
2040
- enabled: true,
2041
- priority: 2,
2042
- background: {
2043
- type: "solid",
2044
- colorToken: "background"
2045
- },
2046
- borderRadius: "rounded-full",
2047
- border: { widthClass: "border-2" },
2048
- effects: { hover: [{
2049
- effectId: "background-on-hover",
2050
- options: { colorToken: "surface" }
2051
- }] }
2746
+ outline: {
2747
+ kind: "complete",
2748
+ appearance: {
2749
+ background: {
2750
+ type: "surface",
2751
+ role: "link"
2752
+ },
2753
+ borderRadius: "rounded-full",
2754
+ border: { widthClass: "border-2" },
2755
+ effects: { hover: [{
2756
+ effectId: "background-on-hover",
2757
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
2758
+ }] }
2759
+ }
2052
2760
  },
2053
- {
2054
- id: "outline",
2055
- name: "Outline",
2056
- enabled: true,
2057
- priority: 3,
2058
- background: {
2059
- type: "surface",
2060
- role: "link"
2061
- },
2062
- borderRadius: "rounded-full",
2063
- border: { widthClass: "border-2" },
2064
- effects: { hover: [{
2065
- effectId: "background-on-hover",
2066
- options: { colorToken: "background" }
2067
- }] }
2761
+ ghost: {
2762
+ kind: "complete",
2763
+ appearance: {
2764
+ background: {
2765
+ type: "surface",
2766
+ role: "link"
2767
+ },
2768
+ borderRadius: "rounded-full",
2769
+ effects: { hover: [{
2770
+ effectId: "background-on-hover",
2771
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2772
+ }] }
2773
+ }
2068
2774
  },
2069
- {
2070
- id: "ghost",
2071
- name: "Ghost",
2072
- enabled: true,
2073
- priority: 3,
2074
- background: {
2075
- type: "surface",
2076
- role: "link"
2077
- },
2078
- borderRadius: "rounded-full",
2079
- effects: { hover: [{
2080
- effectId: "background-on-hover",
2081
- options: { colorToken: "surface" }
2082
- }] }
2775
+ accent: {
2776
+ kind: "complete",
2777
+ appearance: {
2778
+ background: {
2779
+ type: "solid",
2780
+ colorToken: "secondary"
2781
+ },
2782
+ borderRadius: "rounded-full",
2783
+ effects: { hover: [{ effectId: "pop" }] }
2784
+ }
2083
2785
  }
2084
- ]
2786
+ }
2085
2787
  }
2086
2788
  });
2087
- //#endregion
2088
- //#region ../theme-core/src/buttons/personalities/soft-pill.ts
2089
- /**
2090
- * Soft Pill — rounded-full pill buttons with a hover lift. The default for
2091
- * Calm Studio and Modern Wellness; reads as quietly modern and welcoming.
2092
- */
2093
- const softPill = defineButtonPersonality({
2094
- id: "soft-pill",
2095
- name: "Soft Pill",
2096
- description: "Rounded pills with a gentle lift on hover. Quietly modern.",
2789
+ const joyStampRecipe = defineStaticButtonStyleRecipe({
2790
+ id: "joy-stamp",
2791
+ accessibilityLabel: "Bold pill buttons with a hard offset shadow",
2097
2792
  tags: {
2098
- tone: "classic",
2099
- energy: "calm",
2100
- motion: "subtle",
2793
+ tone: "bold",
2794
+ energy: "vibrant",
2795
+ motion: "expressive",
2101
2796
  era: "organic"
2102
2797
  },
2103
- buttonSystem: {
2798
+ system: {
2104
2799
  global: {
2105
2800
  cornerStyle: "pill",
2106
- shadow: "low",
2801
+ shadow: "high",
2107
2802
  textTransform: "none",
2108
- fontWeight: 500,
2109
- borderWidth: 2,
2110
- hoverTransform: "lift",
2803
+ typography: "heading",
2804
+ fontWeight: 700,
2805
+ fontSize: "text-lg",
2806
+ hoverTransform: "none",
2111
2807
  hoverColor: "none"
2112
2808
  },
2113
- variants: [
2114
- {
2115
- id: "primary",
2116
- name: "Primary",
2117
- enabled: true,
2118
- priority: 1,
2119
- background: {
2120
- type: "solid",
2121
- colorToken: "primary"
2122
- },
2123
- borderRadius: "rounded-full",
2124
- shadow: {
2125
- elevation: "low",
2126
- softness: "soft",
2127
- position: "bottom"
2128
- },
2129
- effects: {
2130
- hover: [{
2131
- effectId: "lift",
2132
- options: { translateY: "-1px" }
2133
- }],
2134
- active: [{
2135
- effectId: "scale-down",
2136
- options: { scale: "0.98" }
2137
- }]
2809
+ sizes: {
2810
+ sm: {
2811
+ padding: "0.625rem 1.25rem",
2812
+ fontSize: "text-base"
2813
+ },
2814
+ md: {
2815
+ padding: "0.875rem 1.75rem",
2816
+ fontSize: "text-lg"
2817
+ },
2818
+ lg: {
2819
+ padding: "1.125rem 2.25rem",
2820
+ fontSize: "text-xl"
2821
+ },
2822
+ xl: {
2823
+ padding: "1.25rem 2.75rem",
2824
+ fontSize: "text-2xl"
2825
+ }
2826
+ },
2827
+ roles: {
2828
+ primary: {
2829
+ kind: "complete",
2830
+ appearance: {
2831
+ background: {
2832
+ type: "solid",
2833
+ colorToken: "primary"
2834
+ },
2835
+ borderRadius: "rounded-full",
2836
+ shadow: {
2837
+ elevation: "high",
2838
+ softness: "hard",
2839
+ position: "bottom-right",
2840
+ colorToken: unsafeAsOpaqueButtonColourToken("text")
2841
+ },
2842
+ effects: { hover: [{ effectId: "press-down" }] }
2843
+ }
2844
+ },
2845
+ secondary: {
2846
+ kind: "complete",
2847
+ appearance: {
2848
+ background: {
2849
+ type: "surface",
2850
+ role: "link"
2851
+ },
2852
+ borderRadius: "rounded-full",
2853
+ border: { widthClass: "border-2" },
2854
+ shadow: {
2855
+ elevation: "high",
2856
+ softness: "hard",
2857
+ position: "bottom-right",
2858
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
2859
+ },
2860
+ effects: { hover: [{ effectId: "press-down" }] }
2138
2861
  }
2139
2862
  },
2140
- {
2141
- id: "secondary",
2142
- name: "Secondary",
2143
- enabled: true,
2144
- priority: 2,
2145
- background: {
2146
- type: "surface",
2147
- role: "link"
2148
- },
2149
- borderRadius: "rounded-full",
2150
- border: { widthClass: "border-2" },
2151
- effects: { hover: [{
2152
- effectId: "background-on-hover",
2153
- options: { colorToken: "surface" }
2154
- }] }
2863
+ outline: {
2864
+ kind: "derive",
2865
+ from: "secondary"
2155
2866
  },
2156
- {
2157
- id: "outline",
2158
- name: "Outline",
2159
- enabled: true,
2160
- priority: 3,
2161
- background: {
2162
- type: "surface",
2163
- role: "link"
2164
- },
2165
- borderRadius: "rounded-full",
2166
- border: { widthClass: "border" },
2167
- effects: { hover: [{
2168
- effectId: "background-on-hover",
2169
- options: { colorToken: "surface" }
2170
- }] }
2867
+ ghost: {
2868
+ kind: "complete",
2869
+ appearance: {
2870
+ background: {
2871
+ type: "surface",
2872
+ role: "link"
2873
+ },
2874
+ borderRadius: "rounded-full",
2875
+ effects: { hover: [{ effectId: "press-down" }] }
2876
+ }
2171
2877
  },
2172
- {
2173
- id: "ghost",
2174
- name: "Ghost",
2175
- enabled: true,
2176
- priority: 3,
2177
- background: {
2178
- type: "surface",
2179
- role: "link"
2180
- },
2181
- borderRadius: "rounded-full",
2182
- effects: { hover: [{
2183
- effectId: "background-on-hover",
2184
- options: { colorToken: "surface" }
2185
- }] }
2878
+ accent: {
2879
+ kind: "derive",
2880
+ from: "primary",
2881
+ overrides: { background: {
2882
+ type: "solid",
2883
+ colorToken: "accent"
2884
+ } }
2186
2885
  }
2187
- ]
2886
+ }
2188
2887
  }
2189
2888
  });
2190
- //#endregion
2191
- //#region ../theme-core/src/buttons/personalities/two-tone-pill.ts
2192
- const solidSecondary = {
2193
- id: "secondary",
2194
- name: "Secondary",
2195
- enabled: true,
2196
- priority: 2,
2197
- background: {
2198
- type: "solid",
2199
- colorToken: unsafeAsOpaqueButtonColourToken("secondary")
2200
- },
2201
- borderRadius: "rounded-full",
2202
- shadow: {
2203
- elevation: "none",
2204
- softness: null,
2205
- position: "bottom"
2889
+ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
2890
+ id: "ornate-ritual",
2891
+ accessibilityLabel: "Uppercase framed buttons with restrained movement",
2892
+ tags: {
2893
+ tone: "classic",
2894
+ energy: "vibrant",
2895
+ motion: "subtle",
2896
+ era: "organic"
2206
2897
  },
2207
- effects: {
2208
- hover: [{
2209
- effectId: "background-on-hover-alpha",
2210
- options: {
2211
- colorToken: "secondary",
2212
- opacity: 88
2898
+ system: {
2899
+ global: {
2900
+ cornerStyle: "rounded",
2901
+ textTransform: "uppercase",
2902
+ fontWeight: 600,
2903
+ hoverTransform: "none",
2904
+ hoverColor: "none",
2905
+ letterSpacing: "loose"
2906
+ },
2907
+ sizes: {
2908
+ sm: {
2909
+ padding: "0.5625rem 1.25rem",
2910
+ fontSize: "text-xs"
2911
+ },
2912
+ md: {
2913
+ padding: "0.75rem 1.75rem",
2914
+ fontSize: "text-xs"
2915
+ },
2916
+ lg: {
2917
+ padding: "0.875rem 2.25rem",
2918
+ fontSize: "text-sm"
2919
+ },
2920
+ xl: {
2921
+ padding: "1rem 2.75rem",
2922
+ fontSize: "text-base"
2213
2923
  }
2214
- }],
2215
- active: [{
2216
- effectId: "scale-down",
2217
- options: { scale: "0.98" }
2218
- }]
2924
+ },
2925
+ roles: {
2926
+ primary: {
2927
+ kind: "complete",
2928
+ appearance: {
2929
+ background: {
2930
+ type: "solid",
2931
+ colorToken: "primary"
2932
+ },
2933
+ borderRadius: "rounded-md",
2934
+ border: { widthClass: "border" },
2935
+ effects: {
2936
+ hover: [{
2937
+ effectId: "background-on-hover",
2938
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("primary-100") }
2939
+ }],
2940
+ active: [{
2941
+ effectId: "scale-down",
2942
+ options: { scale: "0.98" }
2943
+ }]
2944
+ }
2945
+ }
2946
+ },
2947
+ secondary: {
2948
+ kind: "complete",
2949
+ appearance: {
2950
+ background: {
2951
+ type: "solid",
2952
+ colorToken: "secondary"
2953
+ },
2954
+ borderRadius: "rounded-md",
2955
+ border: { widthClass: "border" },
2956
+ effects: { hover: [{
2957
+ effectId: "background-on-hover",
2958
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
2959
+ }] }
2960
+ }
2961
+ },
2962
+ outline: {
2963
+ kind: "complete",
2964
+ appearance: {
2965
+ background: {
2966
+ type: "surface",
2967
+ role: "link"
2968
+ },
2969
+ borderRadius: "rounded-md",
2970
+ border: { widthClass: "border" },
2971
+ effects: { hover: [{
2972
+ effectId: "background-on-hover",
2973
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
2974
+ }] }
2975
+ }
2976
+ },
2977
+ ghost: {
2978
+ kind: "complete",
2979
+ appearance: {
2980
+ background: {
2981
+ type: "surface",
2982
+ role: "link"
2983
+ },
2984
+ borderRadius: "rounded-none",
2985
+ sizes: {
2986
+ sm: {
2987
+ padding: "0.375rem 0",
2988
+ fontSize: "text-xs"
2989
+ },
2990
+ md: {
2991
+ padding: "0.5rem 0",
2992
+ fontSize: "text-sm"
2993
+ },
2994
+ lg: {
2995
+ padding: "0.625rem 0",
2996
+ fontSize: "text-base"
2997
+ },
2998
+ xl: {
2999
+ padding: "0.75rem 0",
3000
+ fontSize: "text-lg"
3001
+ }
3002
+ },
3003
+ effects: { base: [{
3004
+ effectId: "border-bottom-grow",
3005
+ options: {
3006
+ height: 1,
3007
+ origin: "left"
3008
+ }
3009
+ }] }
3010
+ }
3011
+ },
3012
+ accent: {
3013
+ kind: "derive",
3014
+ from: "primary",
3015
+ overrides: { background: {
3016
+ type: "solid",
3017
+ colorToken: "accent"
3018
+ } }
3019
+ }
3020
+ }
2219
3021
  }
2220
- };
3022
+ });
2221
3023
  //#endregion
2222
- //#region ../theme-core/src/buttons/personalities/index.ts
2223
- const buttonPersonalities = [
2224
- softPill,
2225
- defineButtonPersonality({
2226
- id: "two-tone-pill",
2227
- name: "Two-tone Pill",
2228
- description: "Rounded pills with distinct solid primary and secondary brand colours.",
2229
- tags: softPill.tags,
2230
- buttonSystem: {
2231
- ...softPill.buttonSystem,
2232
- variants: softPill.buttonSystem.variants.map((variant) => variant.id === "secondary" ? solidSecondary : variant)
2233
- }
2234
- }),
2235
- earthPill,
2236
- confidentChip,
2237
- editorialLink,
2238
- pebble,
2239
- inkStamp,
2240
- flatInk,
2241
- brushedWash,
2242
- showtimePill,
2243
- joyStamp,
2244
- ornateRitual,
2245
- quietArrow
3024
+ //#region ../theme-core/src/buttons/styles/registry.ts
3025
+ const buttonStyleRecipes = [
3026
+ softPillRecipe,
3027
+ earthPillRecipe,
3028
+ confidentChipRecipe,
3029
+ editorialLinkRecipe,
3030
+ pebbleRecipe,
3031
+ inkStampRecipe,
3032
+ flatInkRecipe,
3033
+ brushedWashRecipe,
3034
+ showtimePillRecipe,
3035
+ joyStampRecipe,
3036
+ ornateRitualRecipe,
3037
+ quietArrowRecipe
2246
3038
  ];
2247
- new Map(buttonPersonalities.map((p) => [p.id, p]));
2248
- buttonPersonalities.map((personality) => ({
2249
- id: personality.id,
2250
- name: personality.name,
2251
- description: personality.description,
2252
- category: presetCategory(personality),
2253
- buttonSystem: personality.buttonSystem,
2254
- compatibility: {
2255
- ...personality.tags.tone ? { tones: [personality.tags.tone] } : {},
2256
- ...personality.tags.energy ? { energies: [personality.tags.energy] } : {}
3039
+ const recipesById = new Map(buttonStyleRecipes.map((recipe) => [recipe.id, recipe]));
3040
+ function findButtonStyleRecipe(id) {
3041
+ const recipe = recipesById.get(id);
3042
+ if (recipe === void 0) throw new Error(`Button style registry is missing known style ${id}`);
3043
+ return recipe;
3044
+ }
3045
+ function resolveButtonStyleSelection(selection) {
3046
+ switch (selection.styleId) {
3047
+ case "soft-pill": return resolveButtonStyleRecipe(softPillRecipe, selection.parameters);
3048
+ case "earth-pill": return resolveButtonStyleRecipe(earthPillRecipe, selection.parameters);
3049
+ case "confident-chip": return resolveButtonStyleRecipe(confidentChipRecipe, selection.parameters);
3050
+ case "editorial-link": return resolveButtonStyleRecipe(editorialLinkRecipe, selection.parameters);
3051
+ case "pebble": return resolveButtonStyleRecipe(pebbleRecipe, selection.parameters);
3052
+ case "ink-stamp": return resolveButtonStyleRecipe(inkStampRecipe, selection.parameters);
3053
+ case "flat-ink": return resolveButtonStyleRecipe(flatInkRecipe, selection.parameters);
3054
+ case "brushed-wash": return resolveButtonStyleRecipe(brushedWashRecipe, selection.parameters);
3055
+ case "showtime-pill": return resolveButtonStyleRecipe(showtimePillRecipe, selection.parameters);
3056
+ case "joy-stamp": return resolveButtonStyleRecipe(joyStampRecipe, selection.parameters);
3057
+ case "ornate-ritual": return resolveButtonStyleRecipe(ornateRitualRecipe, selection.parameters);
3058
+ case "quiet-arrow": return resolveButtonStyleRecipe(quietArrowRecipe, selection.parameters);
2257
3059
  }
2258
- }));
2259
- function presetCategory(personality) {
2260
- switch (personality.tags.era) {
3060
+ }
3061
+ function defaultButtonStyleSelectionFor(styleId) {
3062
+ return {
3063
+ styleId,
3064
+ parameters: findButtonStyleRecipe(styleId).defaultParameters
3065
+ };
3066
+ }
3067
+ 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");
3068
+ buttonStyleRecipes.map((recipe) => {
3069
+ const resolved = resolveButtonStyleSelection(defaultButtonStyleSelectionFor(recipe.id));
3070
+ if (resolved.kind === "error") throw new Error(`Invalid registered button style ${recipe.id}: ${resolved.error.issues.join(", ")}`);
3071
+ return {
3072
+ id: recipe.id,
3073
+ name: recipe.accessibilityLabel,
3074
+ description: recipe.accessibilityLabel,
3075
+ category: presetCategory(recipe),
3076
+ buttonSystem: resolved.buttonSystem,
3077
+ compatibility: {
3078
+ ...recipe.tags.tone ? { tones: [recipe.tags.tone] } : {},
3079
+ ...recipe.tags.energy ? { energies: [recipe.tags.energy] } : {}
3080
+ }
3081
+ };
3082
+ });
3083
+ function presetCategory(recipe) {
3084
+ switch (recipe.tags.era) {
2261
3085
  case "retro": return "retro";
2262
3086
  case "editorial": return "minimal";
2263
3087
  case "organic": return "artistic";
2264
- case "modern": return personality.tags.tone === "bold" ? "modern" : "technical";
3088
+ case "modern": return recipe.tags.tone === "bold" ? "modern" : "technical";
2265
3089
  case "technical": return "technical";
2266
3090
  case void 0: return "modern";
2267
3091
  }
@@ -4120,13 +4944,13 @@ async function loadLocalTheme(contentDir, themeName = "current") {
4120
4944
  } catch (error) {
4121
4945
  throw new Error(`Invalid JSON in theme file: ${filePath}\n${error instanceof Error ? error.message : String(error)}`);
4122
4946
  }
4123
- const result = themeSchema$1.safeParse(parsedContent);
4124
- if (!result.success) {
4125
- const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
4126
- throw new Error(`Invalid theme at ${filePath}\n${issues}`);
4947
+ const result = parseThemeForPersistence(parsedContent);
4948
+ if (!result.ok) {
4949
+ const detail = result.error.kind === "invalid-theme-schema" ? result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n") : ` - buttons.${result.error.variantId}: ${result.error.cause.tag}`;
4950
+ throw new Error(`Invalid theme at ${filePath}\n${detail}`);
4127
4951
  }
4128
4952
  return {
4129
- theme: result.data,
4953
+ theme: result.value,
4130
4954
  name: themeName,
4131
4955
  filePath: path.resolve(filePath)
4132
4956
  };
@@ -4159,43 +4983,5 @@ async function listLocalThemes(contentDir) {
4159
4983
  * These utilities help you work with Builder themes in your application.
4160
4984
  */
4161
4985
  const themeSchema = themeSchema$1;
4162
- /**
4163
- * Apply a Builder theme to the DOM by injecting CSS variables
4164
- *
4165
- * @param theme - The theme object from Riverbank CMS
4166
- * @param element - The element to apply the theme to (default: document.documentElement)
4167
- *
4168
- * @example
4169
- * ```ts
4170
- * const site = await client.getSite({ slug: 'my-site' });
4171
- * applyTheme(site.theme);
4172
- * ```
4173
- */
4174
- function applyTheme(theme, element) {
4175
- const targetElement = element ?? (typeof document !== "undefined" ? document.documentElement : null);
4176
- if (!targetElement) {
4177
- console.warn("[Builder SDK] Cannot apply theme: no document available");
4178
- return;
4179
- }
4180
- targetElement.setAttribute("data-builder-theme", JSON.stringify(theme));
4181
- }
4182
- /**
4183
- * Create a theme object with type-safe defaults
4184
- *
4185
- * @param partial - Partial theme configuration
4186
- * @returns Complete theme object
4187
- *
4188
- * @example
4189
- * ```ts
4190
- * const customTheme = createTheme({
4191
- * colors: {
4192
- * primary: '#007bff',
4193
- * },
4194
- * });
4195
- * ```
4196
- */
4197
- function createTheme(partial = {}) {
4198
- return { ...partial };
4199
- }
4200
4986
  //#endregion
4201
- export { applyTheme, createTheme, listLocalThemes, loadLocalTheme, themeSchema };
4987
+ export { listLocalThemes, loadLocalTheme, themeSchema };