@riverbankcms/sdk 0.4.2 → 0.5.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 (180) hide show
  1. package/README.md +84 -0
  2. package/dist/cli/index.js +3104 -120
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/analytics.js +1 -1
  5. package/dist/client/analytics.js.map +1 -1
  6. package/dist/client/analytics.mjs +1 -1
  7. package/dist/client/analytics.mjs.map +1 -1
  8. package/dist/client/bookings.js +6 -6
  9. package/dist/client/bookings.js.map +1 -1
  10. package/dist/client/bookings.mjs +6 -6
  11. package/dist/client/bookings.mjs.map +1 -1
  12. package/dist/client/client.d.mts +2 -2
  13. package/dist/client/client.d.ts +2 -2
  14. package/dist/client/client.js +1368 -520
  15. package/dist/client/client.js.map +1 -1
  16. package/dist/client/client.mjs +1368 -520
  17. package/dist/client/client.mjs.map +1 -1
  18. package/dist/client/hooks.d.mts +2 -2
  19. package/dist/client/hooks.d.ts +2 -2
  20. package/dist/client/hooks.js +26 -11
  21. package/dist/client/hooks.js.map +1 -1
  22. package/dist/client/hooks.mjs +26 -11
  23. package/dist/client/hooks.mjs.map +1 -1
  24. package/dist/client/rendering/client.js +24 -14
  25. package/dist/client/rendering/client.js.map +1 -1
  26. package/dist/client/rendering/client.mjs +24 -14
  27. package/dist/client/rendering/client.mjs.map +1 -1
  28. package/dist/client/usePage--fGlyrgj.d.mts +6439 -0
  29. package/dist/client/usePage-BTPnCuWC.d.mts +6511 -0
  30. package/dist/client/usePage-BafOS9UT.d.mts +6512 -0
  31. package/dist/client/usePage-Bnx-kA6x.d.mts +6670 -0
  32. package/dist/client/usePage-CE7X5NcN.d.ts +6439 -0
  33. package/dist/client/usePage-DoPI6b8V.d.ts +6511 -0
  34. package/dist/client/usePage-QNWArrVO.d.ts +6670 -0
  35. package/dist/client/usePage-fBgPB6Oq.d.ts +6512 -0
  36. package/dist/server/{Layout-kRv5sU81.d.ts → Layout-B-q2Py4v.d.ts} +4 -4
  37. package/dist/server/{Layout-ByUnm35V.d.mts → Layout-Cc5HUXAH.d.mts} +4 -4
  38. package/dist/server/{chunk-6JBKKV3G.js → chunk-2KCF2DNK.js} +30 -10
  39. package/dist/server/chunk-2KCF2DNK.js.map +1 -0
  40. package/dist/server/{chunk-N3PX76AP.mjs → chunk-4HIRA33Z.mjs} +247 -135
  41. package/dist/server/chunk-4HIRA33Z.mjs.map +1 -0
  42. package/dist/server/chunk-5STV4MWD.js +189 -0
  43. package/dist/server/chunk-5STV4MWD.js.map +1 -0
  44. package/dist/server/{chunk-R5B6IOFQ.js → chunk-6OSNCH4F.js} +247 -135
  45. package/dist/server/chunk-6OSNCH4F.js.map +1 -0
  46. package/dist/server/{chunk-TKMA6D6U.js → chunk-7UPVCT3K.js} +1215 -497
  47. package/dist/server/chunk-7UPVCT3K.js.map +1 -0
  48. package/dist/server/{chunk-7DS4Q3GA.mjs → chunk-AEFWG657.mjs} +3 -3
  49. package/dist/server/chunk-AEFWG657.mjs.map +1 -0
  50. package/dist/server/{chunk-USQF2XTU.mjs → chunk-BYBJA6SP.mjs} +26 -11
  51. package/dist/server/chunk-BYBJA6SP.mjs.map +1 -0
  52. package/dist/server/{chunk-ZEAJW6T3.mjs → chunk-C6FIJC7T.mjs} +4 -3
  53. package/dist/server/chunk-C6FIJC7T.mjs.map +1 -0
  54. package/dist/server/{chunk-TO7FD6TQ.js → chunk-I2D7KOEA.js} +4 -4
  55. package/dist/server/{chunk-TO7FD6TQ.js.map → chunk-I2D7KOEA.js.map} +1 -1
  56. package/dist/server/chunk-KFLZGNPO.mjs +189 -0
  57. package/dist/server/chunk-KFLZGNPO.mjs.map +1 -0
  58. package/dist/server/chunk-L5EA4FXU.mjs +134 -0
  59. package/dist/server/chunk-L5EA4FXU.mjs.map +1 -0
  60. package/dist/server/{chunk-TNRADRPH.mjs → chunk-LNOUXALA.mjs} +1137 -419
  61. package/dist/server/chunk-LNOUXALA.mjs.map +1 -0
  62. package/dist/server/{chunk-SPXMMX3C.mjs → chunk-OSF34JTQ.mjs} +4 -4
  63. package/dist/server/{chunk-SWPHIUVE.js → chunk-P3NNN73G.js} +5 -4
  64. package/dist/server/chunk-P3NNN73G.js.map +1 -0
  65. package/dist/server/{chunk-I6K5REFT.mjs → chunk-P4K63SBZ.mjs} +24 -4
  66. package/dist/server/chunk-P4K63SBZ.mjs.map +1 -0
  67. package/dist/server/{chunk-HOY77YBF.js → chunk-RVDS7VSP.js} +5 -5
  68. package/dist/server/chunk-RVDS7VSP.js.map +1 -0
  69. package/dist/server/{chunk-NW5KHH4A.js → chunk-TT5JWA4X.js} +9 -9
  70. package/dist/server/{chunk-NW5KHH4A.js.map → chunk-TT5JWA4X.js.map} +1 -1
  71. package/dist/server/chunk-VSFQRHYZ.js +134 -0
  72. package/dist/server/chunk-VSFQRHYZ.js.map +1 -0
  73. package/dist/server/{chunk-EGTDJ4PL.js → chunk-YYO3RIFO.js} +26 -11
  74. package/dist/server/chunk-YYO3RIFO.js.map +1 -0
  75. package/dist/server/{chunk-OP2GHK27.mjs → chunk-Z5ZA6Q4D.mjs} +2 -2
  76. package/dist/server/{components-D1Z2mSDr.d.ts → components-CU46ZkAv.d.mts} +20 -75
  77. package/dist/server/{components-CY8jDQjv.d.mts → components-DvozDwRN.d.ts} +20 -75
  78. package/dist/server/components.d.mts +11 -8
  79. package/dist/server/components.d.ts +11 -8
  80. package/dist/server/components.js +5 -4
  81. package/dist/server/components.js.map +1 -1
  82. package/dist/server/components.mjs +4 -3
  83. package/dist/server/config-validation.d.mts +3 -3
  84. package/dist/server/config-validation.d.ts +3 -3
  85. package/dist/server/config-validation.js +9 -5
  86. package/dist/server/config-validation.js.map +1 -1
  87. package/dist/server/config-validation.mjs +8 -4
  88. package/dist/server/config.d.mts +243 -5
  89. package/dist/server/config.d.ts +243 -5
  90. package/dist/server/config.js +72 -5
  91. package/dist/server/config.js.map +1 -1
  92. package/dist/server/config.mjs +72 -5
  93. package/dist/server/config.mjs.map +1 -1
  94. package/dist/server/core-DsNWrl3o.d.mts +44 -0
  95. package/dist/server/core-DsNWrl3o.d.ts +44 -0
  96. package/dist/server/data.d.mts +4 -3
  97. package/dist/server/data.d.ts +4 -3
  98. package/dist/server/data.js +3 -3
  99. package/dist/server/data.mjs +2 -2
  100. package/dist/server/{index-DCIz9Ptv.d.ts → index-CJfMXZQr.d.ts} +2 -1
  101. package/dist/server/{index-DFQwtj3J.d.mts → index-Q7RLMAQ6.d.mts} +2 -1
  102. package/dist/server/index.d.mts +63 -6
  103. package/dist/server/index.d.ts +63 -6
  104. package/dist/server/index.js +91 -2
  105. package/dist/server/index.js.map +1 -1
  106. package/dist/server/index.mjs +90 -1
  107. package/dist/server/index.mjs.map +1 -1
  108. package/dist/server/link-DjxLyC82.d.mts +23 -0
  109. package/dist/server/link-DjxLyC82.d.ts +23 -0
  110. package/dist/server/{loadContent-CWuE8FCx.d.mts → loadContent-DgpSKWqY.d.mts} +4 -4
  111. package/dist/server/{loadContent-DynBuR5f.d.ts → loadContent-GPvUI1bN.d.ts} +4 -4
  112. package/dist/server/{loadPage-B8RmlYgV.d.mts → loadPage-DGnIK7s4.d.mts} +17 -47
  113. package/dist/server/loadPage-DNQTTRHL.mjs +11 -0
  114. package/dist/server/{loadPage-BTkKpizX.d.ts → loadPage-DW9WB-u9.d.ts} +17 -47
  115. package/dist/server/loadPage-IDGVDFBB.js +11 -0
  116. package/dist/server/{loadPage-DUHBXDEW.js.map → loadPage-IDGVDFBB.js.map} +1 -1
  117. package/dist/server/metadata.d.mts +6 -4
  118. package/dist/server/metadata.d.ts +6 -4
  119. package/dist/server/navigation.d.mts +199 -29
  120. package/dist/server/navigation.d.ts +199 -29
  121. package/dist/server/navigation.js +27 -43
  122. package/dist/server/navigation.js.map +1 -1
  123. package/dist/server/navigation.mjs +20 -36
  124. package/dist/server/navigation.mjs.map +1 -1
  125. package/dist/server/rendering/server.d.mts +8 -6
  126. package/dist/server/rendering/server.d.ts +8 -6
  127. package/dist/server/rendering/server.js +7 -6
  128. package/dist/server/rendering/server.js.map +1 -1
  129. package/dist/server/rendering/server.mjs +6 -5
  130. package/dist/server/rendering.d.mts +14 -10
  131. package/dist/server/rendering.d.ts +14 -10
  132. package/dist/server/rendering.js +9 -8
  133. package/dist/server/rendering.js.map +1 -1
  134. package/dist/server/rendering.mjs +8 -7
  135. package/dist/server/richTextSchema-DURiozvD.d.mts +62 -0
  136. package/dist/server/richTextSchema-DURiozvD.d.ts +62 -0
  137. package/dist/server/routing.d.mts +178 -11
  138. package/dist/server/routing.d.ts +178 -11
  139. package/dist/server/routing.js +95 -2
  140. package/dist/server/routing.js.map +1 -1
  141. package/dist/server/routing.mjs +94 -1
  142. package/dist/server/routing.mjs.map +1 -1
  143. package/dist/server/{schema-Bpy9N5ZI.d.ts → schema-Z6-afHJG.d.mts} +1 -1
  144. package/dist/server/{schema-Bpy9N5ZI.d.mts → schema-Z6-afHJG.d.ts} +1 -1
  145. package/dist/server/server.d.mts +9 -7
  146. package/dist/server/server.d.ts +9 -7
  147. package/dist/server/server.js +6 -6
  148. package/dist/server/server.mjs +5 -5
  149. package/dist/server/theme-bridge.js +8 -8
  150. package/dist/server/theme-bridge.mjs +2 -2
  151. package/dist/server/{types-oCM-fw4O.d.ts → types-0f4PIlgx.d.mts} +55 -2
  152. package/dist/server/{types-txWsSxN7.d.mts → types-BjgZt8xJ.d.mts} +63 -2
  153. package/dist/server/{types-BiRZnxDx.d.ts → types-C28kMfa1.d.ts} +256 -82
  154. package/dist/server/{types-CL916r6x.d.ts → types-DLBhEPSt.d.ts} +63 -2
  155. package/dist/server/{types-CdrJqlKx.d.mts → types-DuzJZKJI.d.mts} +256 -82
  156. package/dist/server/{types-DkKEctWn.d.mts → types-kOQyCFXO.d.ts} +55 -2
  157. package/dist/server/{validation-DzvDwwRo.d.mts → validation-BGuRo8P1.d.mts} +18 -5
  158. package/dist/server/{validation-CoU8uAiu.d.ts → validation-DU2YE7u5.d.ts} +18 -5
  159. package/package.json +5 -3
  160. package/dist/server/chunk-6JBKKV3G.js.map +0 -1
  161. package/dist/server/chunk-7BOIO2S7.mjs +0 -833
  162. package/dist/server/chunk-7BOIO2S7.mjs.map +0 -1
  163. package/dist/server/chunk-7DS4Q3GA.mjs.map +0 -1
  164. package/dist/server/chunk-BLKVTULP.js +0 -833
  165. package/dist/server/chunk-BLKVTULP.js.map +0 -1
  166. package/dist/server/chunk-EGTDJ4PL.js.map +0 -1
  167. package/dist/server/chunk-HOY77YBF.js.map +0 -1
  168. package/dist/server/chunk-I6K5REFT.mjs.map +0 -1
  169. package/dist/server/chunk-N3PX76AP.mjs.map +0 -1
  170. package/dist/server/chunk-R5B6IOFQ.js.map +0 -1
  171. package/dist/server/chunk-SWPHIUVE.js.map +0 -1
  172. package/dist/server/chunk-TKMA6D6U.js.map +0 -1
  173. package/dist/server/chunk-TNRADRPH.mjs.map +0 -1
  174. package/dist/server/chunk-USQF2XTU.mjs.map +0 -1
  175. package/dist/server/chunk-ZEAJW6T3.mjs.map +0 -1
  176. package/dist/server/loadPage-DUHBXDEW.js +0 -11
  177. package/dist/server/loadPage-LYVKY3WZ.mjs +0 -11
  178. /package/dist/server/{chunk-SPXMMX3C.mjs.map → chunk-OSF34JTQ.mjs.map} +0 -0
  179. /package/dist/server/{chunk-OP2GHK27.mjs.map → chunk-Z5ZA6Q4D.mjs.map} +0 -0
  180. /package/dist/server/{loadPage-LYVKY3WZ.mjs.map → loadPage-DNQTTRHL.mjs.map} +0 -0
@@ -872,7 +872,7 @@ function parseToken(source) {
872
872
  if (source.includes("/")) {
873
873
  const [token, opacity] = source.split("/");
874
874
  const alpha = Number(opacity) / 100;
875
- if (!Number.isNaN(alpha)) {
875
+ if (!Number.isNaN(alpha) && token) {
876
876
  return { token, alpha };
877
877
  }
878
878
  return { token: source };
@@ -912,7 +912,8 @@ function headingGroup(opts) {
912
912
  containerClass = "text-center",
913
913
  className,
914
914
  eyebrowClass = "heading-eyebrow text-sm font-semibold tracking-wide",
915
- titleClass = "heading-title text-3xl font-semibold sm:text-4xl",
915
+ // h2 now gets size/weight from theme typography CSS
916
+ titleClass = "heading-title",
916
917
  eyebrowStyle = textColorStyle("neutral-500"),
917
918
  titleStyle = textColorStyle("neutral-900")
918
919
  } = opts;
@@ -1493,6 +1494,8 @@ var bodyCopyFragment = defineFragment({
1493
1494
  text(
1494
1495
  {
1495
1496
  as: "h2",
1497
+ // h2 now gets size/weight from theme typography CSS
1498
+ // Only dynamic class here is for text alignment
1496
1499
  className: {
1497
1500
  $bind: {
1498
1501
  from: "content.alignment",
@@ -1500,11 +1503,11 @@ var bodyCopyFragment = defineFragment({
1500
1503
  {
1501
1504
  id: "ui.headingClassFromAlignment",
1502
1505
  options: {
1503
- base: "text-3xl font-semibold sm:text-4xl"
1506
+ base: ""
1504
1507
  }
1505
1508
  }
1506
1509
  ],
1507
- fallback: "text-3xl font-semibold sm:text-4xl"
1510
+ fallback: ""
1508
1511
  }
1509
1512
  },
1510
1513
  style: textColorStyle("neutral-900")
@@ -1577,7 +1580,9 @@ var heroCopyFragment = defineFragment({
1577
1580
  text(
1578
1581
  {
1579
1582
  as: "h1",
1580
- className: "hero-headline text-4xl font-semibold sm:text-5xl md:text-6xl",
1583
+ // heading-display: uses fluid typography from theme (--fs-h1-display-fluid)
1584
+ // Removes hardcoded text-4xl/5xl/6xl - size now controlled by theme
1585
+ className: "hero-headline heading-display",
1581
1586
  style: textColorStyle("neutral-900")
1582
1587
  },
1583
1588
  bind("content.headline")
@@ -1756,7 +1761,8 @@ var testimonialsHeadingFragment = defineFragment({
1756
1761
  { gap: "md", className: "mx-auto max-w-2xl text-center" },
1757
1762
  [
1758
1763
  text(
1759
- { as: "h2", className: "text-3xl font-semibold sm:text-4xl", style: textColorStyle("neutral-900") },
1764
+ // h2 now gets size/weight from theme typography CSS
1765
+ { as: "h2", style: textColorStyle("neutral-900") },
1760
1766
  when("content.heading"),
1761
1767
  bind("content.heading")
1762
1768
  ),
@@ -2206,7 +2212,7 @@ var blogFeaturedPostFragment = defineFragment({
2206
2212
  text(
2207
2213
  {
2208
2214
  as: "h2",
2209
- className: "text-2xl font-semibold md:text-3xl",
2215
+ // h2 now gets size/weight from theme typography CSS
2210
2216
  style: textColorStyle("neutral-900")
2211
2217
  },
2212
2218
  bind("title", { fallback: "Latest post" })
@@ -2603,7 +2609,8 @@ var faqHeadingFragment = defineFragment({
2603
2609
  text(
2604
2610
  {
2605
2611
  as: "h2",
2606
- className: "faq-title text-3xl font-semibold sm:text-4xl",
2612
+ // h2 now gets size/weight from theme typography CSS
2613
+ className: "faq-title",
2607
2614
  style: textColorStyle("neutral-900")
2608
2615
  },
2609
2616
  when("content.title"),
@@ -4546,7 +4553,11 @@ var simpleFooterLayout = stack(
4546
4553
  }
4547
4554
  },
4548
4555
  [
4549
- navRow({ align: "center", className: "flex flex-wrap justify-center gap-x-6 gap-y-3" }),
4556
+ navRow({
4557
+ align: "center",
4558
+ className: "flex flex-wrap justify-center gap-x-6 gap-y-3",
4559
+ linkClassName: "footer-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard"
4560
+ }),
4550
4561
  ...bottomTextLayout()
4551
4562
  ],
4552
4563
  when("$root.theme.footer.variant", { equals: "simple" })
@@ -4574,7 +4585,11 @@ var columnsFooterLayout = stack(
4574
4585
  { className: "flex w-full flex-wrap items-center justify-between gap-4" },
4575
4586
  [
4576
4587
  text({ as: "span", className: "text-sm font-semibold", style: textColorStyle("text") }, bind("site.title", { fallback: "Your Site" })),
4577
- navRow({ className: "flex flex-wrap justify-end gap-x-6 gap-y-3", align: "end" })
4588
+ navRow({
4589
+ className: "flex flex-wrap justify-end gap-x-6 gap-y-3",
4590
+ align: "end",
4591
+ linkClassName: "footer-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard"
4592
+ })
4578
4593
  ]
4579
4594
  ),
4580
4595
  ...bottomTextLayout()
@@ -5865,7 +5880,7 @@ function splitPath(path) {
5865
5880
  for (const raw of parts) {
5866
5881
  if (!raw) continue;
5867
5882
  const baseMatch = raw.match(/^([^\[]+)/);
5868
- if (baseMatch) {
5883
+ if (baseMatch && baseMatch[1]) {
5869
5884
  segments.push(baseMatch[1]);
5870
5885
  }
5871
5886
  const bracketMatches = raw.matchAll(BRACKET_ACCESS_REGEX2);
@@ -6647,6 +6662,10 @@ var resolveLinkTransform = {
6647
6662
  const href = link2.href.trim();
6648
6663
  return href.length > 0 ? href : null;
6649
6664
  }
6665
+ if (!kind && typeof link2.href === "string") {
6666
+ const href = link2.href.trim();
6667
+ return href.length > 0 ? href : null;
6668
+ }
6650
6669
  return null;
6651
6670
  }
6652
6671
  };
@@ -7143,7 +7162,14 @@ function getDirectImageUrl(supabaseUrl, storagePath, storageBucket = "media", op
7143
7162
 
7144
7163
  // ../blocks/src/system/runtime/nodes/media.tsx
7145
7164
  import { jsx as jsx9 } from "react/jsx-runtime";
7165
+ var _contextSupabaseUrl;
7166
+ function setContextSupabaseUrl(url) {
7167
+ _contextSupabaseUrl = url?.replace(/\/$/, "");
7168
+ }
7146
7169
  function getSupabaseUrl() {
7170
+ if (_contextSupabaseUrl) {
7171
+ return _contextSupabaseUrl;
7172
+ }
7147
7173
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
7148
7174
  if (!url) {
7149
7175
  throw new Error(
@@ -7463,7 +7489,7 @@ var COLUMN_CLASSES = {
7463
7489
  "4": "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
7464
7490
  };
7465
7491
  function formatDateForUrl(isoTimestamp) {
7466
- return isoTimestamp.split("T")[0];
7492
+ return isoTimestamp.split("T")[0] ?? "";
7467
7493
  }
7468
7494
  function buildEventUrl(basePath, slug, startsAt) {
7469
7495
  const date = formatDateForUrl(startsAt);
@@ -7648,7 +7674,7 @@ function getContainerClass(layout, columns) {
7648
7674
  return `grid gap-6 ${COLUMN_CLASSES2[columns] || COLUMN_CLASSES2["3"]}`;
7649
7675
  }
7650
7676
  function formatDateForUrl2(isoTimestamp) {
7651
- return isoTimestamp.split("T")[0];
7677
+ return isoTimestamp.split("T")[0] ?? "";
7652
7678
  }
7653
7679
  function buildEventUrl2(basePath, slug, startsAt) {
7654
7680
  const date = formatDateForUrl2(startsAt);
@@ -7867,7 +7893,7 @@ function extractSegmentsFromPart(part) {
7867
7893
  if (!part) return [];
7868
7894
  const segments = [];
7869
7895
  const baseMatch = part.match(/^([^\[]+)/);
7870
- if (baseMatch) {
7896
+ if (baseMatch && baseMatch[1]) {
7871
7897
  segments.push(baseMatch[1]);
7872
7898
  }
7873
7899
  const bracketMatches = Array.from(part.matchAll(BRACKET_ACCESS_REGEX));
@@ -8833,8 +8859,8 @@ function getLabWhitePoint() {
8833
8859
  var lab2rgb = (...args) => {
8834
8860
  args = unpack_default(args, "lab");
8835
8861
  const [L, a, b] = args;
8836
- const [x, y, z19] = lab2xyz(L, a, b);
8837
- const [r2, g, b_] = xyz2rgb(x, y, z19);
8862
+ const [x, y, z20] = lab2xyz(L, a, b);
8863
+ const [r2, g, b_] = xyz2rgb(x, y, z20);
8838
8864
  return [r2, g, b_, args.length > 3 ? args[3] : 1];
8839
8865
  };
8840
8866
  var lab2xyz = (L, a, b) => {
@@ -8849,15 +8875,15 @@ var lab2xyz = (L, a, b) => {
8849
8875
  const zr = fz3 > kE ? fz3 : (116 * fz - 16) / kK;
8850
8876
  const x = xr * Xn;
8851
8877
  const y = yr * Yn;
8852
- const z19 = zr * Zn;
8853
- return [x, y, z19];
8878
+ const z20 = zr * Zn;
8879
+ return [x, y, z20];
8854
8880
  };
8855
8881
  var compand = (linear) => {
8856
8882
  const sign = Math.sign(linear);
8857
8883
  linear = Math.abs(linear);
8858
8884
  return (linear <= 31308e-7 ? linear * 12.92 : 1.055 * Math.pow(linear, 1 / 2.4) - 0.055) * sign;
8859
8885
  };
8860
- var xyz2rgb = (x, y, z19) => {
8886
+ var xyz2rgb = (x, y, z20) => {
8861
8887
  const { MtxAdaptMa, MtxAdaptMaI, MtxXYZ2RGB, RefWhiteRGB, Xn, Yn, Zn } = lab_constants_default;
8862
8888
  const As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;
8863
8889
  const Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;
@@ -8865,9 +8891,9 @@ var xyz2rgb = (x, y, z19) => {
8865
8891
  const Ad = RefWhiteRGB.X * MtxAdaptMa.m00 + RefWhiteRGB.Y * MtxAdaptMa.m10 + RefWhiteRGB.Z * MtxAdaptMa.m20;
8866
8892
  const Bd = RefWhiteRGB.X * MtxAdaptMa.m01 + RefWhiteRGB.Y * MtxAdaptMa.m11 + RefWhiteRGB.Z * MtxAdaptMa.m21;
8867
8893
  const Cd = RefWhiteRGB.X * MtxAdaptMa.m02 + RefWhiteRGB.Y * MtxAdaptMa.m12 + RefWhiteRGB.Z * MtxAdaptMa.m22;
8868
- const X1 = (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z19 * MtxAdaptMa.m20) * (Ad / As);
8869
- const Y1 = (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z19 * MtxAdaptMa.m21) * (Bd / Bs);
8870
- const Z1 = (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z19 * MtxAdaptMa.m22) * (Cd / Cs);
8894
+ const X1 = (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z20 * MtxAdaptMa.m20) * (Ad / As);
8895
+ const Y1 = (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z20 * MtxAdaptMa.m21) * (Bd / Bs);
8896
+ const Z1 = (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z20 * MtxAdaptMa.m22) * (Cd / Cs);
8871
8897
  const X2 = X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;
8872
8898
  const Y2 = X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;
8873
8899
  const Z2 = X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;
@@ -8887,15 +8913,15 @@ var lab2rgb_default = lab2rgb;
8887
8913
  // ../../node_modules/.pnpm/chroma-js@3.1.2/node_modules/chroma-js/src/io/lab/rgb2lab.js
8888
8914
  var rgb2lab = (...args) => {
8889
8915
  const [r2, g, b, ...rest] = unpack_default(args, "rgb");
8890
- const [x, y, z19] = rgb2xyz(r2, g, b);
8891
- const [L, a, b_] = xyz2lab(x, y, z19);
8916
+ const [x, y, z20] = rgb2xyz(r2, g, b);
8917
+ const [L, a, b_] = xyz2lab(x, y, z20);
8892
8918
  return [L, a, b_, ...rest.length > 0 && rest[0] < 1 ? [rest[0]] : []];
8893
8919
  };
8894
- function xyz2lab(x, y, z19) {
8920
+ function xyz2lab(x, y, z20) {
8895
8921
  const { Xn, Yn, Zn, kE, kK } = lab_constants_default;
8896
8922
  const xr = x / Xn;
8897
8923
  const yr = y / Yn;
8898
- const zr = z19 / Zn;
8924
+ const zr = z20 / Zn;
8899
8925
  const fx = xr > kE ? Math.pow(xr, 1 / 3) : (kK * xr + 16) / 116;
8900
8926
  const fy = yr > kE ? Math.pow(yr, 1 / 3) : (kK * yr + 16) / 116;
8901
8927
  const fz = zr > kE ? Math.pow(zr, 1 / 3) : (kK * zr + 16) / 116;
@@ -8914,20 +8940,20 @@ var rgb2xyz = (r2, g, b) => {
8914
8940
  const { MtxRGB2XYZ, MtxAdaptMa, MtxAdaptMaI, Xn, Yn, Zn, As, Bs, Cs } = lab_constants_default;
8915
8941
  let x = r2 * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;
8916
8942
  let y = r2 * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;
8917
- let z19 = r2 * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;
8943
+ let z20 = r2 * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;
8918
8944
  const Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;
8919
8945
  const Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;
8920
8946
  const Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;
8921
- let X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z19 * MtxAdaptMa.m20;
8922
- let Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z19 * MtxAdaptMa.m21;
8923
- let Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z19 * MtxAdaptMa.m22;
8947
+ let X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z20 * MtxAdaptMa.m20;
8948
+ let Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z20 * MtxAdaptMa.m21;
8949
+ let Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z20 * MtxAdaptMa.m22;
8924
8950
  X *= Ad / As;
8925
8951
  Y *= Bd / Bs;
8926
8952
  Z *= Cd / Cs;
8927
8953
  x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;
8928
8954
  y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;
8929
- z19 = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;
8930
- return [x, y, z19];
8955
+ z20 = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;
8956
+ return [x, y, z20];
8931
8957
  };
8932
8958
  var rgb2lab_default = rgb2lab;
8933
8959
 
@@ -11256,7 +11282,7 @@ function generateShades(hexColor, name) {
11256
11282
  900: 0.1,
11257
11283
  950: 0.05
11258
11284
  };
11259
- const lightness = lightnessMap[shadeNumber];
11285
+ const lightness = lightnessMap[shadeNumber] ?? 0.5;
11260
11286
  const shadeColor = base.luminance(lightness).hex();
11261
11287
  return {
11262
11288
  name: shadeName,
@@ -11327,7 +11353,7 @@ function expandPalette(palette) {
11327
11353
  if (base.text) expanded.body = base.text;
11328
11354
  const isDark = base.background ? isBackgroundDark(base.background) : false;
11329
11355
  const isDarkMode = palette.meta?.mode === "dark" || isDark;
11330
- expanded.mutedText = isDarkMode ? expanded["neutral-600"] : expanded["neutral-500"];
11356
+ expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
11331
11357
  return expanded;
11332
11358
  }
11333
11359
 
@@ -11395,6 +11421,83 @@ function generateShadowCssVars(theme) {
11395
11421
  };
11396
11422
  }
11397
11423
 
11424
+ // ../blocks/src/theme/tokens/resolver.ts
11425
+ var TokenResolver = class {
11426
+ constructor(theme) {
11427
+ this.theme = theme;
11428
+ }
11429
+ /**
11430
+ * Resolve a color token to a CSS variable reference wrapped in rgb()
11431
+ *
11432
+ * @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
11433
+ * @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
11434
+ *
11435
+ * Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
11436
+ * so we must wrap them with rgb() to create a valid color value.
11437
+ *
11438
+ * @example
11439
+ * resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
11440
+ * resolver.getColor('white') // => 'rgb(var(--tb-white))'
11441
+ */
11442
+ getColor(token) {
11443
+ if (!this.tokenExists(token)) {
11444
+ console.warn(
11445
+ `[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`
11446
+ );
11447
+ }
11448
+ return `rgb(var(--tb-${token}))`;
11449
+ }
11450
+ /**
11451
+ * Adjust a color token's shade by a given amount
11452
+ *
11453
+ * @param token - Base token (e.g., 'primary-500')
11454
+ * @param amount - Shade adjustment (+100 = darker, -100 = lighter)
11455
+ * @returns Adjusted token name (e.g., 'primary-600')
11456
+ *
11457
+ * @example
11458
+ * resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
11459
+ * resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
11460
+ */
11461
+ adjustShade(token, amount) {
11462
+ const parts = token.split("-");
11463
+ if (parts.length < 2) {
11464
+ const newShade2 = Math.max(100, Math.min(900, 500 + amount));
11465
+ return `${token}-${newShade2}`;
11466
+ }
11467
+ const base = parts.slice(0, -1).join("-");
11468
+ const shade = parseInt(parts[parts.length - 1] ?? "", 10);
11469
+ if (isNaN(shade)) {
11470
+ console.warn(
11471
+ `[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`
11472
+ );
11473
+ return token;
11474
+ }
11475
+ const newShade = Math.max(100, Math.min(900, shade + amount));
11476
+ return `${base}-${newShade}`;
11477
+ }
11478
+ /**
11479
+ * Check if a token exists in the theme palette
11480
+ *
11481
+ * @param token - Token name to check
11482
+ * @returns True if token exists in theme
11483
+ */
11484
+ tokenExists(token) {
11485
+ const commonTokens = ["white", "black", "transparent"];
11486
+ if (commonTokens.includes(token)) {
11487
+ return true;
11488
+ }
11489
+ const parts = token.split("-");
11490
+ if (parts.length >= 2) {
11491
+ const lastPart = parts[parts.length - 1] ?? "";
11492
+ const shade = parseInt(lastPart, 10);
11493
+ if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
11494
+ return true;
11495
+ }
11496
+ }
11497
+ return true;
11498
+ }
11499
+ };
11500
+
11398
11501
  // ../blocks/src/theme/buttons/effects/presets/background.ts
11399
11502
  var darkenBackgroundEffect = {
11400
11503
  id: "darken-background",
@@ -11402,6 +11505,7 @@ var darkenBackgroundEffect = {
11402
11505
  description: "Darkens the background color on hover",
11403
11506
  category: "background",
11404
11507
  pseudoElement: "none",
11508
+ applicableTo: ["button"],
11405
11509
  customizableOption: {
11406
11510
  name: "amount",
11407
11511
  type: "number",
@@ -11433,6 +11537,7 @@ var lightenBackgroundEffect = {
11433
11537
  description: "Lightens the background color on hover",
11434
11538
  category: "background",
11435
11539
  pseudoElement: "none",
11540
+ applicableTo: ["button"],
11436
11541
  customizableOption: {
11437
11542
  name: "amount",
11438
11543
  type: "number",
@@ -11464,6 +11569,7 @@ var fadeBackgroundEffect = {
11464
11569
  description: "Reduces background opacity on hover",
11465
11570
  category: "background",
11466
11571
  pseudoElement: "none",
11572
+ applicableTo: ["button", "nav-link"],
11467
11573
  customizableOption: {
11468
11574
  name: "opacity",
11469
11575
  type: "percentage",
@@ -11486,6 +11592,7 @@ var opacityReduceEffect = {
11486
11592
  description: "Reduces button opacity on hover by a percentage",
11487
11593
  category: "background",
11488
11594
  pseudoElement: "none",
11595
+ applicableTo: ["button", "nav-link"],
11489
11596
  customizableOption: {
11490
11597
  name: "reduction",
11491
11598
  type: "percentage",
@@ -11509,6 +11616,7 @@ var animatedGradientEffect = {
11509
11616
  description: "Animates gradient position on hover",
11510
11617
  category: "background",
11511
11618
  pseudoElement: "none",
11619
+ applicableTo: ["button"],
11512
11620
  customizableOption: {
11513
11621
  name: "direction",
11514
11622
  type: "select",
@@ -11548,6 +11656,7 @@ var backgroundOnHoverEffect = {
11548
11656
  description: "Adds background color on hover",
11549
11657
  category: "background",
11550
11658
  pseudoElement: "none",
11659
+ applicableTo: ["button", "nav-link"],
11551
11660
  customizableOption: {
11552
11661
  name: "colorToken",
11553
11662
  type: "select",
@@ -11573,6 +11682,7 @@ var backgroundOnHoverAlphaEffect = {
11573
11682
  description: "Adds semi-transparent background on hover",
11574
11683
  category: "background",
11575
11684
  pseudoElement: "none",
11685
+ applicableTo: ["button", "nav-link"],
11576
11686
  customizableOption: {
11577
11687
  name: "opacity",
11578
11688
  type: "percentage",
@@ -11594,6 +11704,33 @@ var backgroundOnHoverAlphaEffect = {
11594
11704
  }
11595
11705
  };
11596
11706
 
11707
+ // ../blocks/src/theme/buttons/effects/cssUtils.ts
11708
+ var STATE_SELECTORS = {
11709
+ hover: ':hover, [data-force-hover="true"]',
11710
+ active: ":active",
11711
+ focus: ":focus-visible",
11712
+ base: ""
11713
+ };
11714
+ function getStateSelector(themeId, variantId, state) {
11715
+ const stateSelector = STATE_SELECTORS[state];
11716
+ if (state === "base") {
11717
+ return `:where([data-theme-scope="${themeId}"]) .${variantId}`;
11718
+ }
11719
+ if (state === "hover") {
11720
+ return `:where([data-theme-scope="${themeId}"]) .${variantId}:hover,
11721
+ :where([data-theme-scope="${themeId}"]) .${variantId}[data-force-hover="true"]`;
11722
+ }
11723
+ return `:where([data-theme-scope="${themeId}"]) .${variantId}${stateSelector}`;
11724
+ }
11725
+ function generateStateCSS(themeId, variantId, state, cssProperties) {
11726
+ const selector = getStateSelector(themeId, variantId, state);
11727
+ return `
11728
+ ${selector} {
11729
+ ${cssProperties}
11730
+ }
11731
+ `;
11732
+ }
11733
+
11597
11734
  // ../blocks/src/theme/buttons/effects/presets/shadow.ts
11598
11735
  var borderGlowEffect = {
11599
11736
  id: "border-glow",
@@ -11601,6 +11738,7 @@ var borderGlowEffect = {
11601
11738
  description: "Adds a glowing shadow on hover",
11602
11739
  category: "shadow",
11603
11740
  pseudoElement: "none",
11741
+ applicableTo: ["button", "nav-link"],
11604
11742
  customizableOption: {
11605
11743
  name: "glowColorToken",
11606
11744
  type: "colorToken",
@@ -11611,11 +11749,7 @@ var borderGlowEffect = {
11611
11749
  const { themeId, variantId, options, tokens } = ctx;
11612
11750
  const glowToken = options.glowColorToken || "primary-400";
11613
11751
  const glowColor = tokens.getColor(glowToken);
11614
- return `
11615
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11616
- box-shadow: 0 0 15px ${glowColor};
11617
- }
11618
- `;
11752
+ return generateStateCSS(themeId, variantId, "hover", `box-shadow: 0 0 15px ${glowColor};`);
11619
11753
  }
11620
11754
  };
11621
11755
  var dropShadowEffect = {
@@ -11624,6 +11758,7 @@ var dropShadowEffect = {
11624
11758
  description: "Adds an elevated shadow on hover",
11625
11759
  category: "shadow",
11626
11760
  pseudoElement: "none",
11761
+ applicableTo: ["button"],
11627
11762
  customizableOption: {
11628
11763
  name: "intensity",
11629
11764
  type: "select",
@@ -11639,11 +11774,7 @@ var dropShadowEffect = {
11639
11774
  medium: "0 10px 15px rgba(0, 0, 0, 0.15)",
11640
11775
  strong: "0 20px 25px rgba(0, 0, 0, 0.2)"
11641
11776
  };
11642
- return `
11643
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11644
- box-shadow: ${shadows[intensity]};
11645
- }
11646
- `;
11777
+ return generateStateCSS(themeId, variantId, "hover", `box-shadow: ${shadows[intensity]};`);
11647
11778
  }
11648
11779
  };
11649
11780
  var innerShadowEffect = {
@@ -11652,6 +11783,7 @@ var innerShadowEffect = {
11652
11783
  description: "Adds an inset shadow for a pressed appearance",
11653
11784
  category: "shadow",
11654
11785
  pseudoElement: "none",
11786
+ applicableTo: ["button"],
11655
11787
  generateCSS: (ctx) => {
11656
11788
  const { themeId, variantId } = ctx;
11657
11789
  return `
@@ -11667,6 +11799,7 @@ var neumorphicShadowEffect = {
11667
11799
  description: "Dual shadows for soft UI design (raised or pressed)",
11668
11800
  category: "shadow",
11669
11801
  pseudoElement: "none",
11802
+ applicableTo: ["button", "nav-link"],
11670
11803
  customizableOption: {
11671
11804
  name: "mode",
11672
11805
  type: "select",
@@ -11709,6 +11842,7 @@ var outerGlowEffect = {
11709
11842
  description: "Creates an outer glow around the button",
11710
11843
  category: "shadow",
11711
11844
  pseudoElement: "none",
11845
+ applicableTo: ["button", "nav-link"],
11712
11846
  customizableOption: {
11713
11847
  name: "spread",
11714
11848
  type: "number",
@@ -11743,6 +11877,7 @@ var retro3DShadowEffect = {
11743
11877
  description: "3D pressed shadow with vertical movement (retro style)",
11744
11878
  category: "shadow",
11745
11879
  pseudoElement: "none",
11880
+ applicableTo: ["button"],
11746
11881
  generateCSS: (ctx) => {
11747
11882
  const { themeId, variantId } = ctx;
11748
11883
  return `
@@ -11775,6 +11910,7 @@ var scaleUpEffect = {
11775
11910
  description: "Slightly enlarges the button on hover",
11776
11911
  category: "transform",
11777
11912
  pseudoElement: "none",
11913
+ applicableTo: ["button", "nav-link"],
11778
11914
  customizableOption: {
11779
11915
  name: "scale",
11780
11916
  type: "select",
@@ -11785,11 +11921,7 @@ var scaleUpEffect = {
11785
11921
  generateCSS: (ctx) => {
11786
11922
  const { themeId, variantId, options } = ctx;
11787
11923
  const scale = options.scale || "1.05";
11788
- return `
11789
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11790
- transform: scale(${scale});
11791
- }
11792
- `;
11924
+ return generateStateCSS(themeId, variantId, "hover", `transform: scale(${scale});`);
11793
11925
  }
11794
11926
  };
11795
11927
  var scaleDownEffect = {
@@ -11798,6 +11930,7 @@ var scaleDownEffect = {
11798
11930
  description: "Slightly shrinks the button when clicked",
11799
11931
  category: "transform",
11800
11932
  pseudoElement: "none",
11933
+ applicableTo: ["button", "nav-link"],
11801
11934
  customizableOption: {
11802
11935
  name: "scale",
11803
11936
  type: "select",
@@ -11821,6 +11954,7 @@ var liftEffect = {
11821
11954
  description: "Lifts the button upward on hover",
11822
11955
  category: "transform",
11823
11956
  pseudoElement: "none",
11957
+ applicableTo: ["button", "nav-link"],
11824
11958
  customizableOption: {
11825
11959
  name: "amount",
11826
11960
  type: "select",
@@ -11831,11 +11965,7 @@ var liftEffect = {
11831
11965
  generateCSS: (ctx) => {
11832
11966
  const { themeId, variantId, options } = ctx;
11833
11967
  const amount = options.amount || "0.5";
11834
- return `
11835
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11836
- transform: translateY(-${amount}rem);
11837
- }
11838
- `;
11968
+ return generateStateCSS(themeId, variantId, "hover", `transform: translateY(-${amount}rem);`);
11839
11969
  }
11840
11970
  };
11841
11971
  var pressDownEffect = {
@@ -11844,6 +11974,7 @@ var pressDownEffect = {
11844
11974
  description: "Translates button to shadow position and removes shadow on hover",
11845
11975
  category: "transform",
11846
11976
  pseudoElement: "none",
11977
+ applicableTo: ["button"],
11847
11978
  generateCSS: (ctx) => {
11848
11979
  const { themeId, variantId, theme } = ctx;
11849
11980
  const shadowConfig = theme.shadow;
@@ -11856,12 +11987,12 @@ var pressDownEffect = {
11856
11987
  const offset = elevationOffsets[shadowConfig.elevation];
11857
11988
  const xOffset = (shadowConfig.position || "bottom") === "bottom-right" ? offset : 0;
11858
11989
  const yOffset = offset;
11859
- return `
11860
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11861
- transform: translate(${xOffset}px, ${yOffset}px);
11862
- box-shadow: none;
11863
- }
11864
- `;
11990
+ return generateStateCSS(
11991
+ themeId,
11992
+ variantId,
11993
+ "hover",
11994
+ `transform: translate(${xOffset}px, ${yOffset}px); box-shadow: none;`
11995
+ );
11865
11996
  }
11866
11997
  };
11867
11998
  var popEffect = {
@@ -11870,6 +12001,7 @@ var popEffect = {
11870
12001
  description: "Scales up and tilts the button on hover for a playful pop effect",
11871
12002
  category: "transform",
11872
12003
  pseudoElement: "none",
12004
+ applicableTo: ["button"],
11873
12005
  customizableOption: {
11874
12006
  name: "intensity",
11875
12007
  type: "select",
@@ -11886,11 +12018,12 @@ var popEffect = {
11886
12018
  dramatic: { scale: 1.08, rotate: 3 }
11887
12019
  };
11888
12020
  const config = intensityConfig[intensity] || intensityConfig.medium;
11889
- return `
11890
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11891
- transform: scale(${config.scale}) rotate(${config.rotate}deg);
11892
- }
11893
- `;
12021
+ return generateStateCSS(
12022
+ themeId,
12023
+ variantId,
12024
+ "hover",
12025
+ `transform: scale(${config.scale}) rotate(${config.rotate}deg);`
12026
+ );
11894
12027
  }
11895
12028
  };
11896
12029
 
@@ -11901,6 +12034,7 @@ var auroraGlowEffect = {
11901
12034
  description: "Blurred gradient glow that appears on hover",
11902
12035
  category: "overlay",
11903
12036
  pseudoElement: "before",
12037
+ applicableTo: ["button"],
11904
12038
  customizableOption: {
11905
12039
  name: "intensity",
11906
12040
  type: "percentage",
@@ -11913,8 +12047,8 @@ var auroraGlowEffect = {
11913
12047
  let toColor;
11914
12048
  if (buttonConfig.background.type === "gradient") {
11915
12049
  const stops = buttonConfig.background.stops;
11916
- fromColor = tokens.getColor(stops[0]);
11917
- toColor = tokens.getColor(stops[stops.length - 1]);
12050
+ fromColor = tokens.getColor(stops[0] ?? "primary-500");
12051
+ toColor = tokens.getColor(stops[stops.length - 1] ?? "primary-300");
11918
12052
  } else if (buttonConfig.background.type === "solid") {
11919
12053
  fromColor = tokens.getColor(buttonConfig.background.colorToken);
11920
12054
  const lighterToken = tokens.adjustShade(buttonConfig.background.colorToken, -200);
@@ -11957,6 +12091,7 @@ var cosmicStardustEffect = {
11957
12091
  description: "Rotating rainbow gradient glow (always visible, intensifies on hover)",
11958
12092
  category: "overlay",
11959
12093
  pseudoElement: "before",
12094
+ applicableTo: ["button"],
11960
12095
  customizableOption: {
11961
12096
  name: "speed",
11962
12097
  type: "select",
@@ -12019,6 +12154,7 @@ var gradientBorderFillEffect = {
12019
12154
  description: "Gradient border that fills on hover using ::before pseudo-element",
12020
12155
  category: "overlay",
12021
12156
  pseudoElement: "before",
12157
+ applicableTo: ["button"],
12022
12158
  customizableOption: {
12023
12159
  name: "borderWidth",
12024
12160
  type: "number",
@@ -12075,6 +12211,7 @@ var animatedGradientBorderFillEffect = {
12075
12211
  description: "Rotating gradient border that fills on hover",
12076
12212
  category: "overlay",
12077
12213
  pseudoElement: "before",
12214
+ applicableTo: ["button"],
12078
12215
  customizableOption: {
12079
12216
  name: "speed",
12080
12217
  type: "select",
@@ -12150,6 +12287,7 @@ var borderBottomGrowEffect = {
12150
12287
  description: "Animated underline that grows on hover",
12151
12288
  category: "overlay",
12152
12289
  pseudoElement: "after",
12290
+ applicableTo: ["button", "nav-link"],
12153
12291
  customizableOption: {
12154
12292
  name: "height",
12155
12293
  type: "number",
@@ -12197,6 +12335,7 @@ var gradientTextEffect = {
12197
12335
  description: "Gradient text using background-clip",
12198
12336
  category: "text",
12199
12337
  pseudoElement: "none",
12338
+ applicableTo: ["button"],
12200
12339
  customizableOption: {
12201
12340
  name: "solidOnHover",
12202
12341
  type: "select",
@@ -12248,6 +12387,7 @@ var textColorChangeEffect = {
12248
12387
  description: "Changes text color on hover",
12249
12388
  category: "text",
12250
12389
  pseudoElement: "none",
12390
+ applicableTo: ["button", "nav-link"],
12251
12391
  customizableOption: {
12252
12392
  name: "hoverColorToken",
12253
12393
  type: "select",
@@ -12275,6 +12415,7 @@ var borderColorChangeEffect = {
12275
12415
  description: "Changes border color on hover",
12276
12416
  category: "border",
12277
12417
  pseudoElement: "none",
12418
+ applicableTo: ["button", "nav-link"],
12278
12419
  customizableOption: {
12279
12420
  name: "hoverColorToken",
12280
12421
  type: "select",
@@ -12300,6 +12441,7 @@ var sketchyBorderEffect = {
12300
12441
  description: "Hand-drawn style with imperfect edges",
12301
12442
  category: "border",
12302
12443
  pseudoElement: "none",
12444
+ applicableTo: ["button"],
12303
12445
  customizableOption: {
12304
12446
  name: "intensity",
12305
12447
  type: "select",
@@ -12347,6 +12489,7 @@ var pulseAnimationEffect = {
12347
12489
  description: "Continuous pulse animation (opacity fade)",
12348
12490
  category: "animation",
12349
12491
  pseudoElement: "none",
12492
+ applicableTo: ["button", "nav-link"],
12350
12493
  customizableOption: {
12351
12494
  name: "speed",
12352
12495
  type: "select",
@@ -12375,6 +12518,138 @@ var pulseAnimationEffect = {
12375
12518
  }
12376
12519
  };
12377
12520
 
12521
+ // ../blocks/src/theme/interactive/effects/nav-underline.ts
12522
+ var navUnderlineEffect = {
12523
+ id: "nav-underline",
12524
+ name: "Nav Underline",
12525
+ description: "Underline that appears or grows on hover",
12526
+ category: "overlay",
12527
+ pseudoElement: "after",
12528
+ applicableTo: ["nav-link"],
12529
+ customizableOption: {
12530
+ name: "style",
12531
+ type: "select",
12532
+ label: "Underline Style",
12533
+ default: "static",
12534
+ options: ["static", "grow"]
12535
+ },
12536
+ generateCSS: (ctx) => {
12537
+ const { themeId, variantId, options, tokens } = ctx;
12538
+ const style = options.style || "static";
12539
+ const colorToken = options.colorToken || "primary";
12540
+ const color = tokens.getColor(colorToken);
12541
+ const height = options.height || 2;
12542
+ if (style === "grow") {
12543
+ return `
12544
+ /* Setup nav link for pseudo-element */
12545
+ :where([data-theme-scope="${themeId}"]) .${variantId} {
12546
+ position: relative;
12547
+ }
12548
+
12549
+ /* Underline using ::after - starts at 0 width */
12550
+ :where([data-theme-scope="${themeId}"]) .${variantId}::after {
12551
+ content: '';
12552
+ position: absolute;
12553
+ bottom: 0;
12554
+ left: 50%;
12555
+ width: 0;
12556
+ height: ${height}px;
12557
+ background: ${color};
12558
+ transform: translateX(-50%);
12559
+ transition: width 200ms ease-in-out;
12560
+ }
12561
+
12562
+ /* Grow underline on hover */
12563
+ :where([data-theme-scope="${themeId}"]) .${variantId}:hover::after {
12564
+ width: 100%;
12565
+ }
12566
+ `;
12567
+ }
12568
+ return `
12569
+ /* Setup nav link for pseudo-element */
12570
+ :where([data-theme-scope="${themeId}"]) .${variantId} {
12571
+ position: relative;
12572
+ }
12573
+
12574
+ /* Hidden underline using ::after */
12575
+ :where([data-theme-scope="${themeId}"]) .${variantId}::after {
12576
+ content: '';
12577
+ position: absolute;
12578
+ bottom: 0;
12579
+ left: 0;
12580
+ width: 100%;
12581
+ height: ${height}px;
12582
+ background: ${color};
12583
+ opacity: 0;
12584
+ transition: opacity 150ms ease-in-out;
12585
+ }
12586
+
12587
+ /* Show underline on hover */
12588
+ :where([data-theme-scope="${themeId}"]) .${variantId}:hover::after {
12589
+ opacity: 1;
12590
+ }
12591
+ `;
12592
+ }
12593
+ };
12594
+
12595
+ // ../blocks/src/theme/interactive/effects/nav-frosted.ts
12596
+ var navFrostedBaseEffect = {
12597
+ id: "nav-frosted-base",
12598
+ name: "Nav Frosted Base",
12599
+ description: "Base frosted glass styling for nav links",
12600
+ category: "background",
12601
+ pseudoElement: "none",
12602
+ applicableTo: ["nav-link"],
12603
+ generateCSS: (ctx) => {
12604
+ const { themeId, variantId } = ctx;
12605
+ return `
12606
+ /* Frosted glass base state */
12607
+ :where([data-theme-scope="${themeId}"]) .${variantId} {
12608
+ background: rgba(255, 255, 255, 0.05);
12609
+ backdrop-filter: blur(4px);
12610
+ -webkit-backdrop-filter: blur(4px);
12611
+ transition: background 150ms ease, backdrop-filter 150ms ease;
12612
+ }
12613
+ `;
12614
+ }
12615
+ };
12616
+ var navFrostedHoverEffect = {
12617
+ id: "nav-frosted-hover",
12618
+ name: "Nav Frosted Hover",
12619
+ description: "Enhanced frosted glass on hover",
12620
+ category: "background",
12621
+ pseudoElement: "none",
12622
+ applicableTo: ["nav-link"],
12623
+ generateCSS: (ctx) => {
12624
+ const { themeId, variantId } = ctx;
12625
+ return `
12626
+ /* Frosted glass hover state */
12627
+ :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
12628
+ background: rgba(255, 255, 255, 0.1);
12629
+ backdrop-filter: blur(8px);
12630
+ -webkit-backdrop-filter: blur(8px);
12631
+ }
12632
+ `;
12633
+ }
12634
+ };
12635
+ var navFrostedActiveEffect = {
12636
+ id: "nav-frosted-active",
12637
+ name: "Nav Frosted Active",
12638
+ description: "Frosted glass active/pressed state",
12639
+ category: "background",
12640
+ pseudoElement: "none",
12641
+ applicableTo: ["nav-link"],
12642
+ generateCSS: (ctx) => {
12643
+ const { themeId, variantId } = ctx;
12644
+ return `
12645
+ /* Frosted glass active state */
12646
+ :where([data-theme-scope="${themeId}"]) .${variantId}:active {
12647
+ background: rgba(255, 255, 255, 0.15);
12648
+ }
12649
+ `;
12650
+ }
12651
+ };
12652
+
12378
12653
  // ../blocks/src/theme/buttons/effects/registry.ts
12379
12654
  var EFFECT_PRESETS = [
12380
12655
  // Background effects
@@ -12411,88 +12686,65 @@ var EFFECT_PRESETS = [
12411
12686
  borderColorChangeEffect,
12412
12687
  sketchyBorderEffect,
12413
12688
  // Animation effects
12414
- pulseAnimationEffect
12689
+ pulseAnimationEffect,
12690
+ // Nav-link specific effects
12691
+ navUnderlineEffect,
12692
+ navFrostedBaseEffect,
12693
+ navFrostedHoverEffect,
12694
+ navFrostedActiveEffect
12415
12695
  ];
12416
12696
  function getEffectPreset(id) {
12417
12697
  return EFFECT_PRESETS.find((preset) => preset.id === id);
12418
12698
  }
12419
12699
 
12420
- // ../blocks/src/theme/tokens/resolver.ts
12421
- var TokenResolver = class {
12422
- constructor(theme) {
12423
- this.theme = theme;
12424
- }
12425
- /**
12426
- * Resolve a color token to a CSS variable reference wrapped in rgb()
12427
- *
12428
- * @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
12429
- * @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
12430
- *
12431
- * Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
12432
- * so we must wrap them with rgb() to create a valid color value.
12433
- *
12434
- * @example
12435
- * resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
12436
- * resolver.getColor('white') // => 'rgb(var(--tb-white))'
12437
- */
12438
- getColor(token) {
12439
- if (!this.tokenExists(token)) {
12440
- console.warn(
12441
- `[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`
12700
+ // ../blocks/src/theme/interactive/generateEffectsCSS.ts
12701
+ function generateEffectsCSS(options) {
12702
+ const { themeId, variantId, effects, elementConfig, tokens, theme } = options;
12703
+ if (!effects) return "";
12704
+ const cssChunks = [];
12705
+ const stateGroups = [
12706
+ ["base", effects.base],
12707
+ ["hover", effects.hover],
12708
+ ["active", effects.active],
12709
+ ["focus", effects.focus]
12710
+ ];
12711
+ for (const [stateGroup, effectApps] of stateGroups) {
12712
+ if (!effectApps || effectApps.length === 0) continue;
12713
+ for (const effectApp of effectApps) {
12714
+ const css2 = generateSingleEffectCSS(
12715
+ themeId,
12716
+ variantId,
12717
+ stateGroup,
12718
+ effectApp,
12719
+ elementConfig,
12720
+ tokens,
12721
+ theme
12442
12722
  );
12723
+ if (css2) {
12724
+ cssChunks.push(css2);
12725
+ }
12443
12726
  }
12444
- return `rgb(var(--tb-${token}))`;
12445
12727
  }
12446
- /**
12447
- * Adjust a color token's shade by a given amount
12448
- *
12449
- * @param token - Base token (e.g., 'primary-500')
12450
- * @param amount - Shade adjustment (+100 = darker, -100 = lighter)
12451
- * @returns Adjusted token name (e.g., 'primary-600')
12452
- *
12453
- * @example
12454
- * resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
12455
- * resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
12456
- */
12457
- adjustShade(token, amount) {
12458
- const parts = token.split("-");
12459
- if (parts.length < 2) {
12460
- const newShade2 = Math.max(100, Math.min(900, 500 + amount));
12461
- return `${token}-${newShade2}`;
12462
- }
12463
- const base = parts.slice(0, -1).join("-");
12464
- const shade = parseInt(parts[parts.length - 1], 10);
12465
- if (isNaN(shade)) {
12466
- console.warn(
12467
- `[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`
12468
- );
12469
- return token;
12470
- }
12471
- const newShade = Math.max(100, Math.min(900, shade + amount));
12472
- return `${base}-${newShade}`;
12728
+ return cssChunks.join("\n\n");
12729
+ }
12730
+ function generateSingleEffectCSS(themeId, variantId, stateGroup, effectApp, elementConfig, tokens, theme) {
12731
+ const effectPreset = getEffectPreset(effectApp.effectId);
12732
+ if (!effectPreset) {
12733
+ console.warn(`[generateEffectsCSS] Unknown effect: ${effectApp.effectId}`);
12734
+ return "";
12473
12735
  }
12474
- /**
12475
- * Check if a token exists in the theme palette
12476
- *
12477
- * @param token - Token name to check
12478
- * @returns True if token exists in theme
12479
- */
12480
- tokenExists(token) {
12481
- const commonTokens = ["white", "black", "transparent"];
12482
- if (commonTokens.includes(token)) {
12483
- return true;
12484
- }
12485
- const parts = token.split("-");
12486
- if (parts.length >= 2) {
12487
- const lastPart = parts[parts.length - 1];
12488
- const shade = parseInt(lastPart, 10);
12489
- if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
12490
- return true;
12491
- }
12492
- }
12493
- return true;
12494
- }
12495
- };
12736
+ const ctx = {
12737
+ themeId,
12738
+ variantId,
12739
+ stateGroup,
12740
+ options: effectApp.options || {},
12741
+ buttonConfig: elementConfig,
12742
+ // Effects access .background, .textColorToken, .border
12743
+ tokens,
12744
+ theme
12745
+ };
12746
+ return effectPreset.generateCSS(ctx);
12747
+ }
12496
12748
 
12497
12749
  // ../blocks/src/theme/buttons/constants.ts
12498
12750
  var GRADIENT_DIRECTION_MAP = {
@@ -12551,6 +12803,11 @@ var FONT_SIZE_MAP = {
12551
12803
  "text-xl": "1.25rem",
12552
12804
  "text-2xl": "1.5rem"
12553
12805
  };
12806
+ var BUTTON_PADDING_PRESETS = {
12807
+ compact: "0.375rem 0.75rem",
12808
+ default: "0.5rem 1rem",
12809
+ spacious: "0.75rem 1.5rem"
12810
+ };
12554
12811
 
12555
12812
  // ../blocks/src/theme/buttons/utils/contrast.ts
12556
12813
  function getContrastingTextColorToken(backgroundToken, theme) {
@@ -12560,7 +12817,7 @@ function getContrastingTextColorToken(backgroundToken, theme) {
12560
12817
  }
12561
12818
  if (backgroundToken.startsWith("neutral-")) {
12562
12819
  const match = backgroundToken.match(/neutral-(\d+)/);
12563
- if (match) {
12820
+ if (match?.[1]) {
12564
12821
  const shade = parseInt(match[1], 10);
12565
12822
  return shade >= 500 ? "neutral-50" : "neutral-950";
12566
12823
  }
@@ -12569,16 +12826,6 @@ function getContrastingTextColorToken(backgroundToken, theme) {
12569
12826
  }
12570
12827
 
12571
12828
  // ../blocks/src/theme/buttons/generateDefaultButtonSystem.ts
12572
- var CORNERS_TO_RADIUS_MAP = {
12573
- square: "rounded-none",
12574
- // 0px - sharp corners
12575
- soft: "rounded-md",
12576
- // 6px - subtle rounding
12577
- rounded: "rounded-md",
12578
- // 6px - standard rounded (same as soft for buttons)
12579
- pill: "rounded-full"
12580
- // 9999px - full pill shape
12581
- };
12582
12829
  var BORDER_WIDTH_TO_CLASS_MAP = {
12583
12830
  none: "border-0",
12584
12831
  // 0px
@@ -12590,7 +12837,6 @@ var BORDER_WIDTH_TO_CLASS_MAP = {
12590
12837
  // 4px - clearly thick
12591
12838
  };
12592
12839
  function generateDefaultButtonSystem(theme) {
12593
- const borderRadius = CORNERS_TO_RADIUS_MAP[theme.corners] || "rounded-lg";
12594
12840
  const borderWidthClass = BORDER_WIDTH_TO_CLASS_MAP[theme.border.width] || "border-2";
12595
12841
  const variants = [];
12596
12842
  const primaryVariant = {
@@ -12600,10 +12846,11 @@ function generateDefaultButtonSystem(theme) {
12600
12846
  priority: 1,
12601
12847
  background: {
12602
12848
  type: "solid",
12603
- colorToken: "primary-500"
12849
+ colorToken: "primary"
12604
12850
  },
12605
- textColorToken: getContrastingTextColorToken("primary-500", theme),
12606
- borderRadius,
12851
+ textColorToken: getContrastingTextColorToken("primary", theme),
12852
+ borderRadius: "",
12853
+ // Empty string = inherit from global.cornerStyle
12607
12854
  // Inherits shadow from theme
12608
12855
  effects: {
12609
12856
  hover: [
@@ -12625,7 +12872,8 @@ function generateDefaultButtonSystem(theme) {
12625
12872
  colorToken: "neutral-700"
12626
12873
  },
12627
12874
  textColorToken: getContrastingTextColorToken("neutral-700", theme),
12628
- borderRadius,
12875
+ borderRadius: "",
12876
+ // Empty string = inherit from global.cornerStyle
12629
12877
  // Inherits shadow from theme
12630
12878
  effects: {
12631
12879
  hover: [
@@ -12645,12 +12893,13 @@ function generateDefaultButtonSystem(theme) {
12645
12893
  background: {
12646
12894
  type: "transparent"
12647
12895
  },
12648
- textColorToken: "primary-600",
12649
- borderRadius,
12896
+ textColorToken: "primary",
12897
+ borderRadius: "",
12898
+ // Empty string = inherit from global.cornerStyle
12650
12899
  shadow: { elevation: "none", softness: null, position: "bottom" },
12651
12900
  // Outline buttons typically don't have shadows
12652
12901
  border: {
12653
- colorToken: "primary-500",
12902
+ colorToken: "primary",
12654
12903
  widthClass: borderWidthClass
12655
12904
  },
12656
12905
  effects: {
@@ -12671,8 +12920,9 @@ function generateDefaultButtonSystem(theme) {
12671
12920
  background: {
12672
12921
  type: "transparent"
12673
12922
  },
12674
- textColorToken: "primary-600",
12675
- borderRadius,
12923
+ textColorToken: "primary",
12924
+ borderRadius: "",
12925
+ // Empty string = inherit from global.cornerStyle
12676
12926
  shadow: { elevation: "none", softness: null, position: "bottom" },
12677
12927
  // Ghost buttons have no shadow
12678
12928
  // No border for ghost buttons
@@ -12738,6 +12988,10 @@ function generateButtonCss(options) {
12738
12988
  if (!variant.enabled) continue;
12739
12989
  const baseCSS = generateVariantBaseCSS(variant, buttonSystem.global, themeId, tokens, theme);
12740
12990
  cssChunks.push(baseCSS);
12991
+ const hoverBgCSS = generateHoverBackgroundCSS(variant, buttonSystem.global, themeId, tokens);
12992
+ if (hoverBgCSS) {
12993
+ cssChunks.push(hoverBgCSS);
12994
+ }
12741
12995
  const effectCSS = generateVariantEffectCSS(variant, themeId, tokens, theme);
12742
12996
  if (effectCSS) {
12743
12997
  cssChunks.push(effectCSS);
@@ -12756,7 +13010,7 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12756
13010
  const rules = [];
12757
13011
  const borderWidth = variant.border ? BORDER_WIDTH_MAP[variant.border.widthClass] || "1px" : "0px";
12758
13012
  const hasBorder = borderWidth !== "0px";
12759
- const padding = variant.padding || STRUCTURAL_BASE_STYLES.padding;
13013
+ const padding = variant.padding || (global.paddingPreset ? BUTTON_PADDING_PRESETS[global.paddingPreset] : null) || STRUCTURAL_BASE_STYLES.padding;
12760
13014
  const paddingParts = padding.split(" ");
12761
13015
  const verticalPadding = paddingParts[0] || "0.5rem";
12762
13016
  const horizontalPadding = paddingParts[1] || paddingParts[0] || "1rem";
@@ -12769,6 +13023,15 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12769
13023
  rules.push(`padding: ${padding};`);
12770
13024
  }
12771
13025
  rules.push(`font-weight: ${global.fontWeight};`);
13026
+ if (global.typography === "heading") {
13027
+ rules.push(`font-family: var(--font-heading);`);
13028
+ rules.push(`letter-spacing: var(--ls-heading);`);
13029
+ } else {
13030
+ rules.push(`font-family: var(--font-body);`);
13031
+ }
13032
+ if (global.italic) {
13033
+ rules.push(`font-style: italic;`);
13034
+ }
12772
13035
  if (variant.fontSize) {
12773
13036
  const fontSize = FONT_SIZE_MAP[variant.fontSize] || "1rem";
12774
13037
  rules.push(`font-size: ${fontSize};`);
@@ -12782,16 +13045,16 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12782
13045
  rules.push(`align-items: ${STRUCTURAL_BASE_STYLES.alignItems};`);
12783
13046
  rules.push(`justify-content: ${STRUCTURAL_BASE_STYLES.justifyContent};`);
12784
13047
  let borderRadius;
12785
- if (variant.borderRadius) {
12786
- borderRadius = BORDER_RADIUS_MAP[variant.borderRadius] || BORDER_RADIUS_MAP["rounded"];
13048
+ if (variant.borderRadius && variant.borderRadius !== "") {
13049
+ borderRadius = BORDER_RADIUS_MAP[variant.borderRadius] ?? BORDER_RADIUS_MAP["rounded"] ?? "0.375rem";
12787
13050
  } else {
12788
13051
  const cornerStyleMap = {
12789
13052
  "square": "rounded-none",
12790
13053
  "rounded": "rounded-md",
12791
13054
  "pill": "rounded-full"
12792
13055
  };
12793
- const fallbackRadius = cornerStyleMap[global.cornerStyle] || "rounded-md";
12794
- borderRadius = BORDER_RADIUS_MAP[fallbackRadius] || BORDER_RADIUS_MAP["rounded"];
13056
+ const fallbackRadius = cornerStyleMap[global.cornerStyle] ?? "rounded-md";
13057
+ borderRadius = BORDER_RADIUS_MAP[fallbackRadius] ?? BORDER_RADIUS_MAP["rounded"] ?? "0.375rem";
12795
13058
  }
12796
13059
  rules.push(`border-radius: ${borderRadius};`);
12797
13060
  const shadowConfig = variant.shadow || shadowSizeToShadow(global.shadow, theme) || theme.shadow;
@@ -12827,31 +13090,33 @@ ${selector} {
12827
13090
  }
12828
13091
  function generateVariantEffectCSS(variant, themeId, tokens, theme) {
12829
13092
  if (!variant.effects) return "";
12830
- const cssChunks = [];
12831
- for (const [stateGroup, effectApps] of Object.entries(variant.effects)) {
12832
- if (!effectApps || effectApps.length === 0) continue;
12833
- for (const effectApp of effectApps) {
12834
- const effectPreset = getEffectPreset(effectApp.effectId);
12835
- if (!effectPreset) {
12836
- console.warn(`[generateVariantEffectCSS] Unknown effect: ${effectApp.effectId}`);
12837
- continue;
12838
- }
12839
- const ctx = {
12840
- themeId,
12841
- variantId: variant.id,
12842
- stateGroup,
12843
- options: effectApp.options || {},
12844
- buttonConfig: variant,
12845
- tokens,
12846
- theme
12847
- };
12848
- const css2 = effectPreset.generateCSS(ctx);
12849
- if (css2) {
12850
- cssChunks.push(css2);
12851
- }
12852
- }
13093
+ const elementConfig = {
13094
+ background: variant.background,
13095
+ textColorToken: variant.textColorToken,
13096
+ border: variant.border ? { colorToken: variant.border.colorToken } : void 0
13097
+ };
13098
+ return generateEffectsCSS({
13099
+ themeId,
13100
+ variantId: variant.id,
13101
+ effects: variant.effects,
13102
+ elementConfig,
13103
+ tokens,
13104
+ theme
13105
+ });
13106
+ }
13107
+ function generateHoverBackgroundCSS(variant, global, themeId, tokens) {
13108
+ let hoverToken = variant.hoverBackgroundToken;
13109
+ if (!hoverToken && global.hoverColor === "token" && global.hoverColorToken) {
13110
+ hoverToken = global.hoverColorToken;
12853
13111
  }
12854
- return cssChunks.join("\n\n");
13112
+ if (!hoverToken) return null;
13113
+ const selector = `:where([data-theme-scope="${themeId}"]) .${variant.id}:hover`;
13114
+ const hoverBgColor = tokens.getColor(hoverToken);
13115
+ return `
13116
+ ${selector} {
13117
+ background-color: ${hoverBgColor};
13118
+ }
13119
+ `.trim();
12855
13120
  }
12856
13121
  function generateDisabledCSS(variant, themeId) {
12857
13122
  const selector = `:where([data-theme-scope="${themeId}"]) .${variant.id}:disabled`;
@@ -13744,6 +14009,345 @@ function generateDefaultInputSystem(theme) {
13744
14009
  };
13745
14010
  }
13746
14011
 
14012
+ // ../blocks/src/theme/navigation/types.ts
14013
+ import { z as z19 } from "zod";
14014
+
14015
+ // ../blocks/src/theme/interactive/baseSchema.ts
14016
+ import { z as z18 } from "zod";
14017
+ var interactiveTypographySchema = z18.object({
14018
+ /** Font family source */
14019
+ typography: z18.enum(["body", "heading"]).default("body"),
14020
+ /** Font weight */
14021
+ fontWeight: z18.enum(["regular", "medium", "semibold", "bold"]).default("medium"),
14022
+ /** Text transform */
14023
+ textTransform: z18.enum(["none", "uppercase", "capitalize"]).default("none"),
14024
+ /** Italic style */
14025
+ italic: z18.boolean().default(false)
14026
+ });
14027
+ var effectApplicationSchema = z18.object({
14028
+ /** Effect preset ID */
14029
+ effectId: z18.string(),
14030
+ /** User-provided customization options */
14031
+ options: z18.record(z18.string(), z18.any()).optional()
14032
+ });
14033
+ var effectCompositionSchema = z18.object({
14034
+ /** Effects always applied */
14035
+ base: z18.array(effectApplicationSchema).optional(),
14036
+ /** Effects applied on hover */
14037
+ hover: z18.array(effectApplicationSchema).optional(),
14038
+ /** Effects applied on active/pressed */
14039
+ active: z18.array(effectApplicationSchema).optional(),
14040
+ /** Effects applied on focus */
14041
+ focus: z18.array(effectApplicationSchema).optional()
14042
+ });
14043
+
14044
+ // ../blocks/src/theme/navigation/types.ts
14045
+ var navLinkPaddingSchema = z19.enum(["none", "compact", "default"]);
14046
+ var navLinkBorderRadiusSchema = z19.enum(["none", "sm", "md", "full"]);
14047
+ var navLinkStyleSchema = z19.object({
14048
+ /** Display name for the style */
14049
+ name: z19.string().min(1).max(30).optional(),
14050
+ /** Typography settings (inherited from interactive base) */
14051
+ ...interactiveTypographySchema.shape,
14052
+ /** Text color token */
14053
+ colorToken: z19.string().default("text"),
14054
+ /** Hover/active color token (for color-change effects) */
14055
+ hoverColorToken: z19.string().nullable().optional(),
14056
+ /** Padding preset */
14057
+ padding: navLinkPaddingSchema.default("compact"),
14058
+ /** Border radius (for capsule/pill styles) */
14059
+ borderRadius: navLinkBorderRadiusSchema.default("none"),
14060
+ /** Composable effects */
14061
+ effects: effectCompositionSchema.optional()
14062
+ });
14063
+ var NAV_LINK_PADDING_MAP = {
14064
+ none: "0",
14065
+ compact: "0.5rem 1rem",
14066
+ default: "0.75rem 1.25rem"
14067
+ };
14068
+ var NAV_LINK_BORDER_RADIUS_MAP = {
14069
+ none: "0",
14070
+ sm: "0.25rem",
14071
+ md: "0.5rem",
14072
+ full: "9999px"
14073
+ };
14074
+
14075
+ // ../blocks/src/theme/interactive/generateBaseCSS.ts
14076
+ var FONT_WEIGHT_MAP2 = {
14077
+ regular: 400,
14078
+ medium: 500,
14079
+ semibold: 600,
14080
+ bold: 700
14081
+ };
14082
+ function generateBaseInteractiveCSS(options) {
14083
+ const {
14084
+ typography,
14085
+ padding,
14086
+ borderRadius,
14087
+ includeTransition = true,
14088
+ includeFlexCenter = false,
14089
+ borderWidth
14090
+ } = options;
14091
+ const rules = [];
14092
+ if (typography.fontWeight !== void 0) {
14093
+ const weight = typeof typography.fontWeight === "number" ? typography.fontWeight : FONT_WEIGHT_MAP2[typography.fontWeight];
14094
+ rules.push(`font-weight: ${weight};`);
14095
+ }
14096
+ if (typography.typography === "heading") {
14097
+ rules.push(`font-family: var(--font-heading);`);
14098
+ rules.push(`letter-spacing: var(--ls-heading);`);
14099
+ } else if (typography.typography === "body") {
14100
+ rules.push(`font-family: var(--font-body);`);
14101
+ }
14102
+ if (typography.italic) {
14103
+ rules.push(`font-style: italic;`);
14104
+ }
14105
+ if (typography.textTransform && typography.textTransform !== "none") {
14106
+ rules.push(`text-transform: ${typography.textTransform};`);
14107
+ }
14108
+ if (padding) {
14109
+ if (borderWidth && borderWidth !== "0px") {
14110
+ const paddingParts = padding.split(" ");
14111
+ const verticalPadding = paddingParts[0] || "0.5rem";
14112
+ const horizontalPadding = paddingParts[1] || paddingParts[0] || "1rem";
14113
+ rules.push(`padding-top: calc(${verticalPadding} - ${borderWidth} - 1px);`);
14114
+ rules.push(`padding-bottom: calc(${verticalPadding} - ${borderWidth} - 1px);`);
14115
+ rules.push(`padding-left: calc(${horizontalPadding} - ${borderWidth});`);
14116
+ rules.push(`padding-right: calc(${horizontalPadding} - ${borderWidth});`);
14117
+ } else {
14118
+ rules.push(`padding: ${padding};`);
14119
+ }
14120
+ }
14121
+ if (borderRadius) {
14122
+ rules.push(`border-radius: ${borderRadius};`);
14123
+ }
14124
+ if (includeTransition) {
14125
+ rules.push(`transition: all 200ms ease;`);
14126
+ }
14127
+ if (includeFlexCenter) {
14128
+ rules.push(`display: inline-flex;`);
14129
+ rules.push(`align-items: center;`);
14130
+ rules.push(`justify-content: center;`);
14131
+ rules.push(`cursor: pointer;`);
14132
+ }
14133
+ return rules;
14134
+ }
14135
+
14136
+ // ../blocks/src/theme/navigation/generateNavLinkCSS.ts
14137
+ function generateNavLinkCSS(options) {
14138
+ const { themeId, tokens, theme, style, className } = options;
14139
+ const selector = `:where([data-theme-scope="${themeId}"]) .${className}`;
14140
+ const hoverSelector = `${selector}:hover`;
14141
+ const activeSelector = `${selector}:active, ${selector}[data-active="true"]`;
14142
+ const cssChunks = [];
14143
+ const hoverRules = [];
14144
+ const activeRules = [];
14145
+ const baseRules = generateBaseInteractiveCSS({
14146
+ typography: {
14147
+ typography: style.typography,
14148
+ fontWeight: style.fontWeight,
14149
+ textTransform: style.textTransform,
14150
+ italic: style.italic
14151
+ },
14152
+ padding: style.padding ? NAV_LINK_PADDING_MAP[style.padding] : void 0,
14153
+ borderRadius: style.borderRadius ? NAV_LINK_BORDER_RADIUS_MAP[style.borderRadius] : void 0,
14154
+ includeTransition: true,
14155
+ includeFlexCenter: false
14156
+ });
14157
+ if (style.colorToken) {
14158
+ baseRules.push(`color: ${tokens.getColor(style.colorToken)};`);
14159
+ }
14160
+ if (style.hoverColorToken && !style.effects?.hover?.length) {
14161
+ hoverRules.push(`color: ${tokens.getColor(style.hoverColorToken)};`);
14162
+ }
14163
+ const elementConfig = {
14164
+ background: { type: "transparent" },
14165
+ textColorToken: style.colorToken || "text"
14166
+ };
14167
+ if (style.effects) {
14168
+ const effectsCss = generateEffectsCSS({
14169
+ themeId,
14170
+ variantId: className,
14171
+ effects: style.effects,
14172
+ elementConfig,
14173
+ tokens,
14174
+ theme
14175
+ });
14176
+ if (effectsCss) {
14177
+ cssChunks.push(effectsCss);
14178
+ }
14179
+ }
14180
+ if (baseRules.length > 0) {
14181
+ cssChunks.unshift(`
14182
+ ${selector} {
14183
+ ${baseRules.join("\n ")}
14184
+ }
14185
+ `);
14186
+ }
14187
+ if (hoverRules.length > 0) {
14188
+ cssChunks.push(`
14189
+ ${hoverSelector} {
14190
+ ${hoverRules.join("\n ")}
14191
+ }
14192
+ `);
14193
+ }
14194
+ if (activeRules.length > 0) {
14195
+ cssChunks.push(`
14196
+ ${activeSelector} {
14197
+ ${activeRules.join("\n ")}
14198
+ }
14199
+ `);
14200
+ }
14201
+ return cssChunks.join("");
14202
+ }
14203
+
14204
+ // ../blocks/src/theme/navigation/presets.ts
14205
+ var NAV_LINK_PRESETS = {
14206
+ /**
14207
+ * Minimal - subtle color change on hover
14208
+ */
14209
+ minimal: {
14210
+ name: "Minimal",
14211
+ typography: "body",
14212
+ fontWeight: "medium",
14213
+ textTransform: "none",
14214
+ italic: false,
14215
+ colorToken: "text",
14216
+ hoverColorToken: "primary",
14217
+ padding: "none",
14218
+ borderRadius: "none",
14219
+ effects: {
14220
+ hover: [
14221
+ { effectId: "text-color-change", options: { colorToken: "primary" } }
14222
+ ]
14223
+ }
14224
+ },
14225
+ /**
14226
+ * Underline - static underline appears on hover
14227
+ */
14228
+ underline: {
14229
+ name: "Underline",
14230
+ typography: "body",
14231
+ fontWeight: "medium",
14232
+ textTransform: "none",
14233
+ italic: false,
14234
+ colorToken: "text",
14235
+ padding: "none",
14236
+ borderRadius: "none",
14237
+ effects: {
14238
+ hover: [
14239
+ { effectId: "nav-underline", options: { style: "static" } }
14240
+ ]
14241
+ }
14242
+ },
14243
+ /**
14244
+ * Underline Grow - animated underline grows from center on hover
14245
+ */
14246
+ "underline-grow": {
14247
+ name: "Underline Grow",
14248
+ typography: "body",
14249
+ fontWeight: "medium",
14250
+ textTransform: "none",
14251
+ italic: false,
14252
+ colorToken: "text",
14253
+ padding: "none",
14254
+ borderRadius: "none",
14255
+ effects: {
14256
+ hover: [
14257
+ { effectId: "nav-underline", options: { style: "grow" } }
14258
+ ]
14259
+ }
14260
+ },
14261
+ /**
14262
+ * Capsule - pill-shaped background on hover
14263
+ */
14264
+ capsule: {
14265
+ name: "Capsule",
14266
+ typography: "body",
14267
+ fontWeight: "medium",
14268
+ textTransform: "none",
14269
+ italic: false,
14270
+ colorToken: "text",
14271
+ padding: "compact",
14272
+ borderRadius: "full",
14273
+ effects: {
14274
+ hover: [
14275
+ { effectId: "background-on-hover-alpha", options: { colorToken: "primary", opacity: 0.1 } }
14276
+ ],
14277
+ active: [
14278
+ { effectId: "background-on-hover-alpha", options: { colorToken: "primary", opacity: 0.15 } }
14279
+ ]
14280
+ }
14281
+ },
14282
+ /**
14283
+ * Solid - solid background color on hover
14284
+ */
14285
+ solid: {
14286
+ name: "Solid",
14287
+ typography: "body",
14288
+ fontWeight: "medium",
14289
+ textTransform: "none",
14290
+ italic: false,
14291
+ colorToken: "text",
14292
+ padding: "compact",
14293
+ borderRadius: "none",
14294
+ effects: {
14295
+ hover: [
14296
+ { effectId: "background-on-hover", options: { colorToken: "primary" } }
14297
+ ],
14298
+ active: [
14299
+ { effectId: "background-on-hover", options: { colorToken: "primary" } }
14300
+ ]
14301
+ }
14302
+ },
14303
+ /**
14304
+ * Scale - gentle scale-up on hover
14305
+ */
14306
+ scale: {
14307
+ name: "Scale",
14308
+ typography: "body",
14309
+ fontWeight: "medium",
14310
+ textTransform: "none",
14311
+ italic: false,
14312
+ colorToken: "text",
14313
+ padding: "none",
14314
+ borderRadius: "none",
14315
+ effects: {
14316
+ hover: [
14317
+ { effectId: "scale-up", options: { scale: 1.05 } }
14318
+ ]
14319
+ }
14320
+ },
14321
+ /**
14322
+ * Frosted - semi-transparent glass-like background
14323
+ */
14324
+ frosted: {
14325
+ name: "Frosted",
14326
+ typography: "body",
14327
+ fontWeight: "medium",
14328
+ textTransform: "none",
14329
+ italic: false,
14330
+ colorToken: "text",
14331
+ padding: "compact",
14332
+ borderRadius: "md",
14333
+ effects: {
14334
+ base: [
14335
+ { effectId: "nav-frosted-base" }
14336
+ ],
14337
+ hover: [
14338
+ { effectId: "nav-frosted-hover" }
14339
+ ],
14340
+ active: [
14341
+ { effectId: "nav-frosted-active" }
14342
+ ]
14343
+ }
14344
+ }
14345
+ };
14346
+ function getNavLinkPreset(presetId) {
14347
+ return NAV_LINK_PRESETS[presetId];
14348
+ }
14349
+ var NAV_LINK_PRESET_IDS = Object.keys(NAV_LINK_PRESETS);
14350
+
13747
14351
  // ../blocks/src/theme/header/generateHeaderCss.ts
13748
14352
  function generateHeaderCss(options) {
13749
14353
  const { themeId, theme } = options;
@@ -13751,10 +14355,8 @@ function generateHeaderCss(options) {
13751
14355
  const header = theme.header;
13752
14356
  const cssChunks = [];
13753
14357
  cssChunks.push(generateHeaderRootStyles(header, themeId, tokens, theme));
13754
- if (header.logo) {
13755
- cssChunks.push(generateLogoStyles(header.logo, themeId, tokens));
13756
- }
13757
- cssChunks.push(generateNavLinkStyles(header, themeId, tokens));
14358
+ cssChunks.push(generateLogoStyles(header.logo, themeId, tokens, header.textColor));
14359
+ cssChunks.push(generateNavLinkStyles(header, themeId, tokens, theme));
13758
14360
  if (header.variant === "floating" && header.container) {
13759
14361
  cssChunks.push(generateFloatingContainerStyles(header.container, themeId, tokens));
13760
14362
  }
@@ -13810,32 +14412,36 @@ ${selector} {
13810
14412
  }
13811
14413
  `;
13812
14414
  }
13813
- function generateLogoStyles(logo, themeId, tokens) {
13814
- if (!logo) return "";
14415
+ function generateLogoStyles(logo, themeId, tokens, textColor) {
13815
14416
  const selector = `:where([data-theme-scope="${themeId}"]) .header-logo-text`;
13816
14417
  const rules = [];
13817
14418
  const primaryColor = tokens.getColor("primary");
13818
14419
  const accentColor = tokens.getColor("accent");
13819
- if (logo.fontFamily === "mono") {
13820
- rules.push(`font-family: ui-monospace, monospace;`);
13821
- } else if (logo.fontFamily === "serif") {
13822
- rules.push(`font-family: ui-serif, serif;`);
13823
- }
13824
- if (logo.letterSpacing && logo.letterSpacing !== "normal") {
13825
- const spacingMap = {
13826
- "normal": "0em",
13827
- "wide": "0.05em",
13828
- "wider": "0.1em",
13829
- "widest": "0.15em"
13830
- };
13831
- rules.push(`letter-spacing: ${spacingMap[logo.letterSpacing]};`);
13832
- }
13833
- if (logo.gradient) {
13834
- rules.push(`background: linear-gradient(to right, ${primaryColor}, ${accentColor});`);
13835
- rules.push(`-webkit-background-clip: text;`);
13836
- rules.push(`-webkit-text-fill-color: transparent;`);
13837
- rules.push(`background-clip: text;`);
13838
- rules.push(`color: transparent;`);
14420
+ if (textColor && !logo?.gradient) {
14421
+ rules.push(`color: ${tokens.getColor(textColor)};`);
14422
+ }
14423
+ if (logo) {
14424
+ if (logo.fontFamily === "mono") {
14425
+ rules.push(`font-family: ui-monospace, monospace;`);
14426
+ } else if (logo.fontFamily === "serif") {
14427
+ rules.push(`font-family: ui-serif, serif;`);
14428
+ }
14429
+ if (logo.letterSpacing && logo.letterSpacing !== "normal") {
14430
+ const spacingMap = {
14431
+ "normal": "0em",
14432
+ "wide": "0.05em",
14433
+ "wider": "0.1em",
14434
+ "widest": "0.15em"
14435
+ };
14436
+ rules.push(`letter-spacing: ${spacingMap[logo.letterSpacing]};`);
14437
+ }
14438
+ if (logo.gradient) {
14439
+ rules.push(`background: linear-gradient(to right, ${primaryColor}, ${accentColor});`);
14440
+ rules.push(`-webkit-background-clip: text;`);
14441
+ rules.push(`-webkit-text-fill-color: transparent;`);
14442
+ rules.push(`background-clip: text;`);
14443
+ rules.push(`color: transparent;`);
14444
+ }
13839
14445
  }
13840
14446
  if (rules.length === 0) return "";
13841
14447
  return `
@@ -13844,129 +14450,82 @@ ${selector} {
13844
14450
  }
13845
14451
  `;
13846
14452
  }
13847
- function generateNavLinkStyles(header, themeId, tokens) {
13848
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
13849
- const hoverSelector = `${selector}:hover`;
13850
- const activeSelector = `${selector}[data-active="true"]`;
13851
- const cssChunks = [];
13852
- const baseRules = [];
13853
- const hoverRules = [];
13854
- const activeRules = [];
13855
- const navContainerType = header.navContainer?.type ?? "none";
13856
- const usesGlassNavContainer = navContainerType === "glass";
13857
- const navColorToken = header.navColor?.trim();
13858
- const primaryColor = tokens.getColor("primary");
13859
- const navWeight = header.navWeight ?? "medium";
13860
- const navWeightMap = {
13861
- regular: 400,
13862
- medium: 500,
13863
- semibold: 600,
13864
- bold: 700
13865
- };
13866
- if (navColorToken) {
13867
- baseRules.push(`color: ${tokens.getColor(navColorToken)};`);
13868
- }
13869
- if (navWeight) {
13870
- baseRules.push(`font-weight: ${navWeightMap[navWeight]};`);
13871
- }
13872
- if (navWeight === "bold" && header.navStyle === "solid") {
13873
- baseRules.push(`text-transform: uppercase;`);
13874
- }
13875
- switch (header.navStyle) {
13876
- case "minimal":
13877
- baseRules.push(`transition: color 200ms ease;`);
13878
- hoverRules.push(`color: ${primaryColor};`);
13879
- activeRules.push(`color: ${primaryColor};`);
13880
- break;
13881
- case "underline":
13882
- baseRules.push(`transition: all 200ms ease;`);
13883
- hoverRules.push(`text-decoration: underline;`);
13884
- hoverRules.push(`text-underline-offset: 4px;`);
13885
- activeRules.push(`text-decoration: underline;`);
13886
- activeRules.push(`text-underline-offset: 4px;`);
13887
- break;
13888
- case "underline-grow":
13889
- baseRules.push(`position: relative;`);
13890
- baseRules.push(`transition: color 200ms ease;`);
13891
- hoverRules.push(`color: ${primaryColor};`);
13892
- activeRules.push(`color: ${primaryColor};`);
13893
- cssChunks.push(generateUnderlineGrowCSS(themeId));
13894
- break;
13895
- case "capsule":
13896
- baseRules.push(`border-radius: 9999px;`);
13897
- baseRules.push(`transition: background-color 200ms ease;`);
13898
- hoverRules.push(`background-color: color-mix(in srgb, ${primaryColor} 10%, transparent);`);
13899
- activeRules.push(`background-color: color-mix(in srgb, ${primaryColor} 15%, transparent);`);
13900
- break;
13901
- case "solid":
13902
- baseRules.push(`border-radius: 0;`);
13903
- baseRules.push(`transition: background-color 200ms ease, color 200ms ease;`);
13904
- hoverRules.push(`background-color: ${primaryColor};`);
13905
- if (navColorToken) {
13906
- hoverRules.push(`color: ${tokens.getColor(navColorToken)};`);
13907
- }
13908
- activeRules.push(`background-color: ${primaryColor};`);
13909
- if (navColorToken) {
13910
- activeRules.push(`color: ${tokens.getColor(navColorToken)};`);
13911
- }
13912
- break;
13913
- case "scale":
13914
- baseRules.push(`transition: all 200ms ease;`);
13915
- hoverRules.push(`transform: scale(1.05);`);
13916
- break;
13917
- case "frosted":
13918
- baseRules.push(`border-radius: 0.5rem;`);
13919
- baseRules.push(`background-color: rgb(255 255 255 / 0.1);`);
13920
- baseRules.push(`backdrop-filter: blur(8px);`);
13921
- baseRules.push(`transition: background-color 200ms ease;`);
13922
- hoverRules.push(`background-color: rgb(255 255 255 / 0.2);`);
13923
- activeRules.push(`background-color: rgb(255 255 255 / 0.25);`);
13924
- break;
14453
+ function generateNavLinkStyles(header, themeId, tokens, theme) {
14454
+ if (header.navLinkStyle) {
14455
+ return generateNavLinkCSS({
14456
+ themeId,
14457
+ tokens,
14458
+ theme,
14459
+ style: header.navLinkStyle,
14460
+ className: "header-nav-link"
14461
+ });
13925
14462
  }
13926
- if (usesGlassNavContainer && header.navStyle === "minimal") {
13927
- baseRules.push(`border-radius: 9999px;`);
13928
- hoverRules.push(`background-color: rgba(255, 255, 255, 0.1);`);
13929
- activeRules.push(`background-color: rgba(255, 255, 255, 0.18);`);
13930
- hoverRules.push(`color: #fff;`);
13931
- activeRules.push(`color: #fff;`);
14463
+ const navStyle = header.navStyle ?? "minimal";
14464
+ const basePreset = getNavLinkPreset(navStyle);
14465
+ if (!basePreset) {
14466
+ console.warn(`[generateHeaderCss] Nav link preset not found: ${navStyle}`);
14467
+ return "";
14468
+ }
14469
+ const mergedStyle = {
14470
+ ...basePreset,
14471
+ // Override with legacy settings
14472
+ fontWeight: header.navWeight ?? basePreset.fontWeight,
14473
+ colorToken: (header.navColor ?? header.textColor)?.trim() || basePreset.colorToken
14474
+ };
14475
+ if (header.navWeight === "bold" && navStyle === "solid") {
14476
+ mergedStyle.textTransform = "uppercase";
13932
14477
  }
13933
14478
  if (header.navEffects) {
14479
+ mergedStyle.effects = {
14480
+ ...mergedStyle.effects,
14481
+ hover: [...mergedStyle.effects?.hover || []],
14482
+ active: [...mergedStyle.effects?.active || []]
14483
+ };
13934
14484
  if (header.navEffects.glow) {
13935
- hoverRules.push(`filter: drop-shadow(0 0 5px currentColor);`);
14485
+ mergedStyle.effects.hover.push({
14486
+ effectId: "outer-glow",
14487
+ options: { spread: 5 }
14488
+ });
13936
14489
  }
13937
14490
  if (header.navEffects.neumorphic) {
13938
- console.log("[generateHeaderCss] Generating neumorphic styles for theme:", themeId);
13939
- baseRules.push(`transition: all 200ms ease;`);
13940
- baseRules.push(`border-radius: 0.5rem;`);
13941
- hoverRules.push(`box-shadow: inset 4px 4px 8px #c7c7c7, inset -4px -4px 8px #ffffff;`);
13942
- hoverRules.push(`color: ${primaryColor};`);
14491
+ mergedStyle.effects.hover.push({
14492
+ effectId: "neumorphic-shadow",
14493
+ options: { mode: "pressed" }
14494
+ });
14495
+ mergedStyle.effects.hover.push({
14496
+ effectId: "text-color-change",
14497
+ options: { hoverColorToken: "primary" }
14498
+ });
13943
14499
  }
13944
14500
  if (header.navEffects.underlineGradient) {
13945
- cssChunks.push(generateGradientUnderlineCSS(themeId, tokens));
14501
+ mergedStyle.effects.hover.push({
14502
+ effectId: "border-bottom-grow",
14503
+ options: { origin: "center" }
14504
+ });
13946
14505
  }
13947
14506
  }
13948
- if (baseRules.length > 0) {
13949
- cssChunks.push(`
13950
- ${selector} {
13951
- ${baseRules.join("\n ")}
13952
- }
13953
- `);
13954
- }
13955
- if (hoverRules.length > 0) {
13956
- cssChunks.push(`
13957
- ${hoverSelector} {
13958
- ${hoverRules.join("\n ")}
13959
- }
13960
- `);
13961
- }
13962
- if (activeRules.length > 0) {
13963
- cssChunks.push(`
13964
- ${activeSelector} {
13965
- ${activeRules.join("\n ")}
13966
- }
13967
- `);
14507
+ const navContainerType = header.navContainer?.type ?? "none";
14508
+ if (navContainerType === "glass" && navStyle === "minimal") {
14509
+ mergedStyle.borderRadius = "full";
14510
+ mergedStyle.effects = {
14511
+ ...mergedStyle.effects,
14512
+ hover: [
14513
+ { effectId: "background-on-hover-alpha", options: { colorToken: "neutral-50", opacity: 10 } },
14514
+ { effectId: "text-color-change", options: { hoverColorToken: "neutral-50" } }
14515
+ ],
14516
+ active: [
14517
+ { effectId: "background-on-hover-alpha", options: { colorToken: "neutral-50", opacity: 18 } },
14518
+ { effectId: "text-color-change", options: { hoverColorToken: "neutral-50" } }
14519
+ ]
14520
+ };
13968
14521
  }
13969
- return cssChunks.join("");
14522
+ return generateNavLinkCSS({
14523
+ themeId,
14524
+ tokens,
14525
+ theme,
14526
+ style: mergedStyle,
14527
+ className: "header-nav-link"
14528
+ });
13970
14529
  }
13971
14530
  function generateNavContainerStyles(header, themeId, tokens) {
13972
14531
  const navContainer = header.navContainer;
@@ -14003,52 +14562,6 @@ function generateNavContainerStyles(header, themeId, tokens) {
14003
14562
  }
14004
14563
  return "";
14005
14564
  }
14006
- function generateGradientUnderlineCSS(themeId, tokens) {
14007
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
14008
- const primaryColor = tokens.getColor("primary");
14009
- const accentColor = tokens.getColor("accent");
14010
- return `
14011
- ${selector}::after {
14012
- content: '';
14013
- position: absolute;
14014
- bottom: 0;
14015
- left: 0;
14016
- width: 100%;
14017
- height: 2px;
14018
- background: linear-gradient(to right, ${primaryColor}, ${accentColor});
14019
- transform: scaleX(0);
14020
- transform-origin: center;
14021
- transition: transform 0.3s ease;
14022
- }
14023
-
14024
- ${selector}:hover::after,
14025
- ${selector}[data-active="true"]::after {
14026
- transform: scaleX(1);
14027
- }
14028
- `.trim();
14029
- }
14030
- function generateUnderlineGrowCSS(themeId) {
14031
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
14032
- return `
14033
- ${selector}::after {
14034
- content: '';
14035
- position: absolute;
14036
- bottom: 0;
14037
- left: 0;
14038
- width: 100%;
14039
- height: 2px;
14040
- background: currentColor;
14041
- transform: scaleX(0);
14042
- transform-origin: center;
14043
- transition: transform 0.3s ease;
14044
- }
14045
-
14046
- ${selector}:hover::after,
14047
- ${selector}[data-active="true"]::after {
14048
- transform: scaleX(1);
14049
- }
14050
- `.trim();
14051
- }
14052
14565
  function generateFloatingContainerStyles(container, themeId, tokens) {
14053
14566
  if (!container) return "";
14054
14567
  const selector = `:where([data-theme-scope="${themeId}"]) .header-floating-container`;
@@ -14105,12 +14618,52 @@ function applyAlpha(color, opacity) {
14105
14618
  const clamped = clampOpacity(opacity, 1);
14106
14619
  if (color.startsWith("rgb(") && color.endsWith(")")) {
14107
14620
  const inner = color.slice(4, -1).trim();
14108
- const channels = inner.includes("/") ? inner.split("/")[0].trim() : inner;
14621
+ const channels = inner.includes("/") ? (inner.split("/")[0] ?? inner).trim() : inner;
14109
14622
  return `rgb(${channels} / ${clamped})`;
14110
14623
  }
14111
14624
  return color;
14112
14625
  }
14113
14626
 
14627
+ // ../blocks/src/theme/footer/generateFooterCss.ts
14628
+ function generateFooterCss(options) {
14629
+ const { themeId, theme } = options;
14630
+ const tokens = new TokenResolver(theme);
14631
+ const footer = theme.footer;
14632
+ const cssChunks = [];
14633
+ cssChunks.push(generateFooterNavLinkStyles(footer, themeId, tokens, theme));
14634
+ return cssChunks.filter(Boolean).join("\n\n");
14635
+ }
14636
+ function generateFooterNavLinkStyles(footer, themeId, tokens, theme) {
14637
+ if (footer.navLinkStyle) {
14638
+ return generateNavLinkCSS({
14639
+ themeId,
14640
+ tokens,
14641
+ theme,
14642
+ style: footer.navLinkStyle,
14643
+ className: "footer-nav-link"
14644
+ });
14645
+ }
14646
+ const navStyle = footer.navStyle ?? "minimal";
14647
+ const basePreset = getNavLinkPreset(navStyle);
14648
+ if (!basePreset) {
14649
+ console.warn(`[generateFooterCss] Nav link preset not found: ${navStyle}`);
14650
+ return "";
14651
+ }
14652
+ const mergedStyle = {
14653
+ ...basePreset,
14654
+ // Override with legacy settings
14655
+ fontWeight: footer.navWeight ?? basePreset.fontWeight,
14656
+ colorToken: footer.navColor?.trim() || basePreset.colorToken
14657
+ };
14658
+ return generateNavLinkCSS({
14659
+ themeId,
14660
+ tokens,
14661
+ theme,
14662
+ style: mergedStyle,
14663
+ className: "footer-nav-link"
14664
+ });
14665
+ }
14666
+
14114
14667
  // ../blocks/src/theme/layout/generateLayoutCss.ts
14115
14668
  function generateLayoutCss(options) {
14116
14669
  const { themeId } = options;
@@ -14264,6 +14817,105 @@ ${root} .step-connector {
14264
14817
  return cssBlocks.join("\n\n");
14265
14818
  }
14266
14819
 
14820
+ // ../blocks/src/theme/typography/generateTypographyCss.ts
14821
+ function generateTypographyCss({ themeId }) {
14822
+ const root = `:where([data-theme-scope="${themeId}"])`;
14823
+ const cssBlocks = [];
14824
+ cssBlocks.push(`/* Base heading styles */
14825
+ ${root} :where(h1, h2, h3, h4, h5, h6) {
14826
+ font-family: var(--font-heading);
14827
+ font-style: var(--fi-heading, normal);
14828
+ text-transform: var(--tt-heading, none);
14829
+ font-variant: var(--fv-heading, normal);
14830
+ margin: 0;
14831
+ }`);
14832
+ cssBlocks.push(`/* Heading level styles with fluid typography */
14833
+ ${root} :where(h1) {
14834
+ font-size: var(--fs-h1-fluid, var(--fs-h1));
14835
+ font-weight: var(--fw-h1, var(--font-weight-heading));
14836
+ font-style: var(--fi-h1, var(--fi-heading, normal));
14837
+ letter-spacing: var(--ls-h1, var(--ls-heading));
14838
+ line-height: var(--lh-h1, var(--lh-heading));
14839
+ }
14840
+
14841
+ ${root} :where(h2) {
14842
+ font-size: var(--fs-h2-fluid, var(--fs-h2));
14843
+ font-weight: var(--fw-h2, var(--font-weight-heading));
14844
+ font-style: var(--fi-h2, var(--fi-heading, normal));
14845
+ letter-spacing: var(--ls-h2, var(--ls-heading));
14846
+ line-height: var(--lh-h2, var(--lh-heading));
14847
+ }
14848
+
14849
+ ${root} :where(h3) {
14850
+ font-size: var(--fs-h3-fluid, var(--fs-h3));
14851
+ font-weight: var(--fw-h3, var(--font-weight-heading));
14852
+ font-style: var(--fi-h3, var(--fi-heading, normal));
14853
+ letter-spacing: var(--ls-h3, var(--ls-heading));
14854
+ line-height: var(--lh-h3, var(--lh-heading));
14855
+ }
14856
+
14857
+ ${root} :where(h4) {
14858
+ font-size: var(--fs-h4-fluid, var(--fs-h4, var(--fs-h3)));
14859
+ font-weight: var(--fw-h4, var(--font-weight-heading));
14860
+ font-style: var(--fi-h4, var(--fi-heading, normal));
14861
+ letter-spacing: var(--ls-h4, var(--ls-heading));
14862
+ line-height: var(--lh-h4, var(--lh-heading));
14863
+ }
14864
+
14865
+ ${root} :where(h5) {
14866
+ font-size: var(--fs-h5-fluid, var(--fs-h5, var(--fs-body)));
14867
+ font-weight: var(--fw-h5, var(--font-weight-heading));
14868
+ font-style: var(--fi-h5, var(--fi-heading, normal));
14869
+ letter-spacing: var(--ls-h5, var(--ls-heading));
14870
+ line-height: var(--lh-h5, var(--lh-heading));
14871
+ }
14872
+
14873
+ ${root} :where(h6) {
14874
+ font-size: var(--fs-h6-fluid, var(--fs-h6, var(--fs-body)));
14875
+ font-weight: var(--fw-h6, var(--font-weight-heading));
14876
+ font-style: var(--fi-h6, var(--fi-heading, normal));
14877
+ letter-spacing: var(--ls-h6, var(--ls-heading));
14878
+ line-height: var(--lh-h6, var(--lh-heading));
14879
+ }`);
14880
+ cssBlocks.push(`/* Display variant for hero/splash headings */
14881
+ ${root} :where(.heading-display) {
14882
+ font-size: var(--fs-display-fluid, var(--fs-display));
14883
+ }
14884
+
14885
+ ${root} :where(h1.heading-display) {
14886
+ font-size: var(--fs-h1-display-fluid, var(--fs-h1-display, calc(var(--fs-h1) * 1.5)));
14887
+ }
14888
+
14889
+ ${root} :where(h2.heading-display) {
14890
+ font-size: var(--fs-h2-display-fluid, var(--fs-h2-display, calc(var(--fs-h2) * 1.35)));
14891
+ }`);
14892
+ cssBlocks.push(`/* Compact variant for cards/sidebars */
14893
+ ${root} :where(.heading-compact) {
14894
+ font-size: var(--fs-compact);
14895
+ }
14896
+
14897
+ ${root} :where(h1.heading-compact) {
14898
+ font-size: calc(var(--fs-h1) * 0.75);
14899
+ }
14900
+
14901
+ ${root} :where(h2.heading-compact) {
14902
+ font-size: calc(var(--fs-h2) * 0.8);
14903
+ }
14904
+
14905
+ ${root} :where(h3.heading-compact) {
14906
+ font-size: calc(var(--fs-h3) * 0.85);
14907
+ }`);
14908
+ cssBlocks.push(`/* Body text base styles */
14909
+ ${root} :where(p, li, td, th, label, span) {
14910
+ font-family: var(--font-body);
14911
+ font-weight: var(--font-weight-body);
14912
+ font-size: var(--fs-body);
14913
+ letter-spacing: var(--ls-body);
14914
+ line-height: var(--lh-body);
14915
+ }`);
14916
+ return cssBlocks.join("\n\n");
14917
+ }
14918
+
14267
14919
  // ../blocks/src/theme/runtime/buildThemeRuntime.ts
14268
14920
  function buildThemeRuntime(theme, options) {
14269
14921
  const hydrated = hydrateTheme(theme);
@@ -14278,6 +14930,10 @@ function buildThemeRuntime(theme, options) {
14278
14930
  themeId,
14279
14931
  theme
14280
14932
  });
14933
+ const footerCss = generateFooterCss({
14934
+ themeId,
14935
+ theme
14936
+ });
14281
14937
  const cardCss = generateCardCss({
14282
14938
  themeId,
14283
14939
  theme,
@@ -14307,8 +14963,12 @@ function buildThemeRuntime(theme, options) {
14307
14963
  themeId,
14308
14964
  theme
14309
14965
  });
14966
+ const typographyCss = generateTypographyCss({
14967
+ themeId,
14968
+ theme
14969
+ });
14310
14970
  const tokens = buildThemeTokens(hydrated, theme);
14311
- return { hydrated, cssVars, tokens, paletteTokens: hydrated.palette, buttonCss, headerCss, cardCss, accordionCss, inputCss, layoutCss, statusCss, progressCss };
14971
+ return { hydrated, cssVars, tokens, paletteTokens: hydrated.palette, buttonCss, headerCss, footerCss, cardCss, accordionCss, inputCss, layoutCss, statusCss, progressCss, typographyCss };
14312
14972
  }
14313
14973
  function hydrateTheme(theme) {
14314
14974
  return {
@@ -14338,17 +14998,39 @@ function buildThemeCssVars(theme) {
14338
14998
  ...typography.headings.h1.letterSpacing ? { ["--ls-h1"]: mapLetterSpacing(typography.headings.h1.letterSpacing) } : {},
14339
14999
  ...typography.headings.h2.letterSpacing ? { ["--ls-h2"]: mapLetterSpacing(typography.headings.h2.letterSpacing) } : {},
14340
15000
  ...typography.headings.h3.letterSpacing ? { ["--ls-h3"]: mapLetterSpacing(typography.headings.h3.letterSpacing) } : {},
15001
+ ...typography.headings.h4?.letterSpacing ? { ["--ls-h4"]: mapLetterSpacing(typography.headings.h4.letterSpacing) } : {},
15002
+ ...typography.headings.h5?.letterSpacing ? { ["--ls-h5"]: mapLetterSpacing(typography.headings.h5.letterSpacing) } : {},
15003
+ ...typography.headings.h6?.letterSpacing ? { ["--ls-h6"]: mapLetterSpacing(typography.headings.h6.letterSpacing) } : {},
14341
15004
  ["--lh-heading"]: mapLineHeight(typography.headings.default.lineHeight),
14342
15005
  ["--lh-body"]: mapLineHeight(typography.body.lineHeight),
14343
15006
  ...typography.headings.h1.lineHeight ? { ["--lh-h1"]: mapLineHeight(typography.headings.h1.lineHeight) } : {},
14344
15007
  ...typography.headings.h2.lineHeight ? { ["--lh-h2"]: mapLineHeight(typography.headings.h2.lineHeight) } : {},
14345
15008
  ...typography.headings.h3.lineHeight ? { ["--lh-h3"]: mapLineHeight(typography.headings.h3.lineHeight) } : {},
15009
+ ...typography.headings.h4?.lineHeight ? { ["--lh-h4"]: mapLineHeight(typography.headings.h4.lineHeight) } : {},
15010
+ ...typography.headings.h5?.lineHeight ? { ["--lh-h5"]: mapLineHeight(typography.headings.h5.lineHeight) } : {},
15011
+ ...typography.headings.h6?.lineHeight ? { ["--lh-h6"]: mapLineHeight(typography.headings.h6.lineHeight) } : {},
14346
15012
  ["--tt-heading"]: mapTextTransform(typography.headings.default.case),
14347
15013
  ["--fv-heading"]: mapFontVariant(typography.headings.default.case),
15014
+ ["--fi-heading"]: mapFontStyle(typography.headings.default.italic),
15015
+ // Per-level italic overrides
15016
+ ...typography.headings.h1.italic != null ? { ["--fi-h1"]: mapFontStyle(typography.headings.h1.italic) } : {},
15017
+ ...typography.headings.h2.italic != null ? { ["--fi-h2"]: mapFontStyle(typography.headings.h2.italic) } : {},
15018
+ ...typography.headings.h3.italic != null ? { ["--fi-h3"]: mapFontStyle(typography.headings.h3.italic) } : {},
15019
+ ...typography.headings.h4?.italic != null ? { ["--fi-h4"]: mapFontStyle(typography.headings.h4.italic) } : {},
15020
+ ...typography.headings.h5?.italic != null ? { ["--fi-h5"]: mapFontStyle(typography.headings.h5.italic) } : {},
15021
+ ...typography.headings.h6?.italic != null ? { ["--fi-h6"]: mapFontStyle(typography.headings.h6.italic) } : {},
15022
+ // Static font sizes (fallback)
14348
15023
  ["--fs-body"]: sized.body,
14349
15024
  ["--fs-h3"]: sized.h3,
14350
15025
  ["--fs-h2"]: sized.h2,
14351
15026
  ["--fs-h1"]: sized.h1,
15027
+ // Fluid font sizes (responsive with clamp)
15028
+ ["--fs-h1-fluid"]: sized.h1Fluid,
15029
+ ["--fs-h2-fluid"]: sized.h2Fluid,
15030
+ ["--fs-h3-fluid"]: sized.h3Fluid,
15031
+ // Display variant (for hero/splash headings)
15032
+ ["--fs-h1-display"]: sized.h1Display,
15033
+ ["--fs-h1-display-fluid"]: sized.h1DisplayFluid,
14352
15034
  ["--shadow-elev"]: mapShadow(shadow.elevation, shadow.softness, shadow.position || "bottom"),
14353
15035
  ["--space-mult"]: mapSpaceMult(space),
14354
15036
  ["--motion-duration"]: mapMotionDuration(motion.level),
@@ -14454,33 +15136,81 @@ function mapMotionEasing(e) {
14454
15136
  gentle: "cubic-bezier(.15,.85,.15,1)"
14455
15137
  }[e];
14456
15138
  }
15139
+ function normalizeScaleName(scale) {
15140
+ const aliasMap = {
15141
+ minorThird: "compact",
15142
+ majorThird: "balanced",
15143
+ perfectFourth: "spacious",
15144
+ compact: "compact",
15145
+ balanced: "balanced",
15146
+ spacious: "spacious"
15147
+ };
15148
+ return aliasMap[scale] ?? "balanced";
15149
+ }
14457
15150
  function computeTypeScale(scale, bodySize) {
14458
15151
  const base = { md: 16, lg: 17, xl: 18 }[bodySize];
14459
- const ratio = { minorThird: 1.2, majorThird: 1.25, perfectFourth: 1.333 }[scale];
15152
+ const normalizedScale = normalizeScaleName(scale);
15153
+ const ratio = { compact: 1.2, balanced: 1.25, spacious: 1.333 }[normalizedScale];
14460
15154
  const h3 = Math.round(base * ratio);
14461
15155
  const h2 = Math.round(h3 * ratio);
14462
15156
  const h1 = Math.round(h2 * ratio);
15157
+ const fluidMin = 0.7;
15158
+ const fluidMax = 1.3;
14463
15159
  return {
15160
+ // Static sizes (fallback)
14464
15161
  ["--fs-body"]: `${base}px`,
14465
15162
  ["--fs-h3"]: `${h3}px`,
14466
15163
  ["--fs-h2"]: `${h2}px`,
14467
- ["--fs-h1"]: `${h1}px`
15164
+ ["--fs-h1"]: `${h1}px`,
15165
+ // Fluid typography min/max values
15166
+ ["--fs-h1-min"]: `${Math.round(h1 * fluidMin)}px`,
15167
+ ["--fs-h1-max"]: `${Math.round(h1 * fluidMax)}px`,
15168
+ ["--fs-h2-min"]: `${Math.round(h2 * fluidMin)}px`,
15169
+ ["--fs-h2-max"]: `${Math.round(h2 * fluidMax)}px`,
15170
+ ["--fs-h3-min"]: `${Math.round(h3 * fluidMin)}px`,
15171
+ ["--fs-h3-max"]: `${Math.round(h3 * fluidMax)}px`,
15172
+ // Fluid clamp values (320px to 1200px viewport range = 880px)
15173
+ ["--fs-h1-fluid"]: `clamp(${Math.round(h1 * fluidMin)}px, calc(${Math.round(h1 * fluidMin)}px + ${Math.round(h1 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h1 * fluidMax)}px)`,
15174
+ ["--fs-h2-fluid"]: `clamp(${Math.round(h2 * fluidMin)}px, calc(${Math.round(h2 * fluidMin)}px + ${Math.round(h2 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h2 * fluidMax)}px)`,
15175
+ ["--fs-h3-fluid"]: `clamp(${Math.round(h3 * fluidMin)}px, calc(${Math.round(h3 * fluidMin)}px + ${Math.round(h3 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h3 * fluidMax)}px)`,
15176
+ // Display variant sizes (1.5x for hero headings)
15177
+ ["--fs-h1-display"]: `${Math.round(h1 * 1.5)}px`,
15178
+ ["--fs-h1-display-fluid"]: `clamp(${Math.round(h1 * fluidMin * 1.5)}px, calc(${Math.round(h1 * fluidMin * 1.5)}px + ${Math.round(h1 * (fluidMax - fluidMin) * 1.5)} * ((100vw - 320px) / 880)), ${Math.round(h1 * fluidMax * 1.5)}px)`
14468
15179
  };
14469
15180
  }
15181
+ var SIZE_SCALE_MAP = {
15182
+ xs: 0.85,
15183
+ sm: 0.925,
15184
+ md: 1,
15185
+ lg: 1.1,
15186
+ xl: 1.2,
15187
+ "2xl": 1.35
15188
+ };
14470
15189
  function applyHeadingSizeOverrides(base, typography) {
14471
15190
  const body = base["--fs-body"];
14472
15191
  const h1Base = parseFloat(base["--fs-h1"].replace("px", ""));
14473
15192
  const h2Base = parseFloat(base["--fs-h2"].replace("px", ""));
14474
15193
  const h3Base = parseFloat(base["--fs-h3"].replace("px", ""));
14475
- const scaleMap = { md: 1, lg: 1.1, xl: 1.2 };
14476
- const h1Scale = typography.headings.h1.size ? scaleMap[typography.headings.h1.size] : 1;
14477
- const h2Scale = typography.headings.h2.size ? scaleMap[typography.headings.h2.size] : 1;
14478
- const h3Scale = typography.headings.h3.size ? scaleMap[typography.headings.h3.size] : 1;
15194
+ const h1Scale = typography.headings.h1.size ? SIZE_SCALE_MAP[typography.headings.h1.size] : 1;
15195
+ const h2Scale = typography.headings.h2.size ? SIZE_SCALE_MAP[typography.headings.h2.size] : 1;
15196
+ const h3Scale = typography.headings.h3.size ? SIZE_SCALE_MAP[typography.headings.h3.size] : 1;
15197
+ const h1 = Math.round(h1Base * h1Scale);
15198
+ const h2 = Math.round(h2Base * h2Scale);
15199
+ const h3 = Math.round(h3Base * h3Scale);
15200
+ const fluidMin = 0.7;
15201
+ const fluidMax = 1.3;
14479
15202
  return {
14480
15203
  body,
14481
- h1: `${Math.round(h1Base * h1Scale)}px`,
14482
- h2: `${Math.round(h2Base * h2Scale)}px`,
14483
- h3: `${Math.round(h3Base * h3Scale)}px`
15204
+ h1: `${h1}px`,
15205
+ h2: `${h2}px`,
15206
+ h3: `${h3}px`,
15207
+ // Fluid versions with size overrides applied
15208
+ h1Fluid: `clamp(${Math.round(h1 * fluidMin)}px, calc(${Math.round(h1 * fluidMin)}px + ${Math.round(h1 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h1 * fluidMax)}px)`,
15209
+ h2Fluid: `clamp(${Math.round(h2 * fluidMin)}px, calc(${Math.round(h2 * fluidMin)}px + ${Math.round(h2 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h2 * fluidMax)}px)`,
15210
+ h3Fluid: `clamp(${Math.round(h3 * fluidMin)}px, calc(${Math.round(h3 * fluidMin)}px + ${Math.round(h3 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h3 * fluidMax)}px)`,
15211
+ // Display variant (1.5x scale for hero headings)
15212
+ h1Display: `${Math.round(h1 * 1.5)}px`,
15213
+ h1DisplayFluid: `clamp(${Math.round(h1 * 1.5 * fluidMin)}px, calc(${Math.round(h1 * 1.5 * fluidMin)}px + ${Math.round(h1 * 1.5 * (fluidMax - fluidMin))} * ((100vw - 320px) / 880)), ${Math.round(h1 * 1.5 * fluidMax)}px)`
14484
15214
  };
14485
15215
  }
14486
15216
  function mapLetterSpacing(s) {
@@ -14497,6 +15227,9 @@ function mapFontVariant(c) {
14497
15227
  if (c === "smallCaps") return "small-caps";
14498
15228
  return "normal";
14499
15229
  }
15230
+ function mapFontStyle(italic) {
15231
+ return italic ? "italic" : "normal";
15232
+ }
14500
15233
 
14501
15234
  // ../blocks/src/system/manifest/hydrateLinks.ts
14502
15235
  function hydrateManifestLinks(manifest, content, routes) {
@@ -14643,6 +15376,7 @@ function PageRenderer({
14643
15376
  blockOverrides,
14644
15377
  sdkConfig
14645
15378
  }) {
15379
+ setContextSupabaseUrl(dataContext?.supabaseUrl);
14646
15380
  if (!page || page.blocks.length === 0) {
14647
15381
  return /* @__PURE__ */ jsx21("div", { className: "p-6 text-sm", style: textColorStyle("mutedText"), children: "No blocks found." });
14648
15382
  }
@@ -14879,6 +15613,7 @@ function Page({
14879
15613
  usePlaceholders = false,
14880
15614
  blockOverrides,
14881
15615
  sdkConfig,
15616
+ supabaseUrl,
14882
15617
  dataContext
14883
15618
  }) {
14884
15619
  const baseTokens = providedTokens ?? buildThemeRuntime(theme).tokens;
@@ -14895,7 +15630,8 @@ function Page({
14895
15630
  resolvedData,
14896
15631
  routes: routeMap,
14897
15632
  occurrenceContext: dataContext?.occurrenceContext ?? null,
14898
- contentEntry: dataContext?.contentEntry ?? null
15633
+ contentEntry: dataContext?.contentEntry ?? null,
15634
+ supabaseUrl
14899
15635
  },
14900
15636
  routeMap,
14901
15637
  wrapBlock,
@@ -16361,6 +17097,15 @@ var ENDPOINT_DEFINITIONS = {
16361
17097
  auth: "public",
16362
17098
  responseKind: "json"
16363
17099
  },
17100
+ // Resolve event occurrence by URL segment (date or UUID)
17101
+ resolveEventOccurrence: {
17102
+ path: "/public/sites/{siteId}/events/occurrences/resolve",
17103
+ method: "GET",
17104
+ revalidate: 60,
17105
+ tags: ["public-events-{siteId}", "event-occurrence"],
17106
+ auth: "public",
17107
+ responseKind: "json"
17108
+ },
16364
17109
  // Public event registration
16365
17110
  registerForEvent: {
16366
17111
  path: "/public/sites/{siteId}/events/register",
@@ -16535,6 +17280,20 @@ if (typeof window === "undefined") {
16535
17280
  revalidateTag = null;
16536
17281
  }
16537
17282
  }
17283
+ var sdkVersion;
17284
+ function generateRequestId() {
17285
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
17286
+ return crypto.randomUUID();
17287
+ }
17288
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
17289
+ const r2 = Math.random() * 16 | 0;
17290
+ const v = c === "x" ? r2 : r2 & 3 | 8;
17291
+ return v.toString(16);
17292
+ });
17293
+ }
17294
+ function setSdkVersion(version2) {
17295
+ sdkVersion = version2;
17296
+ }
16538
17297
  var ApiRequestError = class extends Error {
16539
17298
  constructor(message, options) {
16540
17299
  super(message);
@@ -16546,6 +17305,7 @@ var ApiRequestError = class extends Error {
16546
17305
  this.requestId = options.requestId;
16547
17306
  this.body = options.body;
16548
17307
  this.cause = options.cause;
17308
+ this.errorCode = options.errorCode;
16549
17309
  }
16550
17310
  };
16551
17311
  function buildEndpointURL(baseURL, endpoint) {
@@ -16582,6 +17342,29 @@ async function parseErrorBody(response) {
16582
17342
  return null;
16583
17343
  }
16584
17344
  }
17345
+ function buildSuccessEnvelope(data, requestId) {
17346
+ return {
17347
+ success: true,
17348
+ data,
17349
+ meta: {
17350
+ requestId: requestId ?? generateRequestId(),
17351
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
17352
+ apiVersion: "2025-01-01"
17353
+ }
17354
+ };
17355
+ }
17356
+ function buildErrorEnvelope(code, message, status, requestId) {
17357
+ return {
17358
+ success: false,
17359
+ error: {
17360
+ code,
17361
+ message,
17362
+ requestId: requestId ?? generateRequestId(),
17363
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
17364
+ status
17365
+ }
17366
+ };
17367
+ }
16585
17368
  async function parseSuccessResponse(endpoint, response, config) {
16586
17369
  const responseKind = config.responseKind ?? "json";
16587
17370
  const auth = config.auth ?? "user";
@@ -16589,14 +17372,15 @@ async function parseSuccessResponse(endpoint, response, config) {
16589
17372
  switch (responseKind) {
16590
17373
  case "json": {
16591
17374
  if (response.status === 204 || response.status === 205 || response.status === 304) {
16592
- return void 0;
17375
+ return buildSuccessEnvelope(void 0, requestId);
16593
17376
  }
16594
17377
  const raw = await response.text();
16595
17378
  if (!raw.trim()) {
16596
- return void 0;
17379
+ return buildSuccessEnvelope(void 0, requestId);
16597
17380
  }
17381
+ let parsed;
16598
17382
  try {
16599
- return JSON.parse(raw);
17383
+ parsed = JSON.parse(raw);
16600
17384
  } catch (cause) {
16601
17385
  throw new ApiRequestError(
16602
17386
  `Failed to parse JSON response for endpoint ${String(endpoint)}`,
@@ -16611,32 +17395,33 @@ async function parseSuccessResponse(endpoint, response, config) {
16611
17395
  }
16612
17396
  );
16613
17397
  }
17398
+ if (parsed && typeof parsed === "object" && "success" in parsed && typeof parsed.success === "boolean") {
17399
+ return parsed;
17400
+ }
17401
+ return buildSuccessEnvelope(parsed, requestId);
16614
17402
  }
16615
17403
  case "text": {
16616
- return await response.text();
17404
+ const text2 = await response.text();
17405
+ return buildSuccessEnvelope(text2, requestId);
16617
17406
  }
16618
17407
  case "stream": {
16619
17408
  const body = response.body;
16620
17409
  if (!body) {
16621
- throw new ApiRequestError(
17410
+ return buildErrorEnvelope(
17411
+ "server:internal_error",
16622
17412
  `Expected a streamed body for endpoint ${String(endpoint)}`,
16623
- {
16624
- endpoint,
16625
- status: response.status,
16626
- method: config.method,
16627
- auth,
16628
- requestId
16629
- }
17413
+ response.status,
17414
+ requestId
16630
17415
  );
16631
17416
  }
16632
17417
  const stream = body;
16633
- return stream;
17418
+ return buildSuccessEnvelope(stream, requestId);
16634
17419
  }
16635
17420
  case "void": {
16636
- return void 0;
17421
+ return buildSuccessEnvelope(void 0, requestId);
16637
17422
  }
16638
17423
  default: {
16639
- return void 0;
17424
+ return buildSuccessEnvelope(void 0, requestId);
16640
17425
  }
16641
17426
  }
16642
17427
  }
@@ -16702,11 +17487,15 @@ function createRawCMSClient(headers = {}, baseUrl) {
16702
17487
  const requestInit = {
16703
17488
  method,
16704
17489
  ...options,
17490
+ // Include credentials for same-origin requests (sends cookies for auth)
17491
+ credentials: "same-origin",
16705
17492
  // Don't include body for GET/HEAD requests
16706
17493
  body: isGetOrHead ? void 0 : isFormData ? body : body ? JSON.stringify(body) : void 0,
16707
17494
  headers: {
16708
17495
  ...options.headers,
16709
17496
  ...headers,
17497
+ // Include SDK version if set
17498
+ ...sdkVersion && { "x-sdk-version": sdkVersion },
16710
17499
  // Don't set Content-Type for GET/HEAD requests without body
16711
17500
  ...isGetOrHead ? {} : isFormData ? {} : { "Content-Type": "application/json" }
16712
17501
  },
@@ -16760,94 +17549,30 @@ function createBearerAPIClient(token, baseUrl) {
16760
17549
  return createCMSClient(authHeaders, baseUrl);
16761
17550
  }
16762
17551
 
16763
- // ../api/src/aiPlayground.ts
16764
- import { z as z18 } from "zod";
16765
- var Rfc6902PatchOp = z18.discriminatedUnion("op", [
16766
- // Standard RFC-6902 operations
16767
- z18.object({
16768
- op: z18.literal("add"),
16769
- path: z18.string(),
16770
- value: z18.unknown()
16771
- }),
16772
- z18.object({
16773
- op: z18.literal("remove"),
16774
- path: z18.string()
16775
- }),
16776
- z18.object({
16777
- op: z18.literal("replace"),
16778
- path: z18.string(),
16779
- value: z18.unknown()
16780
- }),
16781
- z18.object({
16782
- op: z18.literal("move"),
16783
- from: z18.string(),
16784
- path: z18.string()
16785
- }),
16786
- z18.object({
16787
- op: z18.literal("copy"),
16788
- from: z18.string(),
16789
- path: z18.string()
16790
- }),
16791
- // Block-level operations (Phase 2)
16792
- z18.object({
16793
- op: z18.literal("add_block"),
16794
- blockKind: z18.string(),
16795
- afterBlockId: z18.string().nullable(),
16796
- content: z18.record(z18.string(), z18.unknown()),
16797
- rationale: z18.string()
16798
- }),
16799
- z18.object({
16800
- op: z18.literal("delete_block"),
16801
- blockId: z18.string(),
16802
- rationale: z18.string()
16803
- }),
16804
- z18.object({
16805
- op: z18.literal("reorder_block"),
16806
- blockId: z18.string(),
16807
- afterBlockId: z18.string().nullable(),
16808
- rationale: z18.string()
16809
- })
16810
- ]);
16811
- var PatchEnvelope = z18.object({
16812
- blockId: z18.string(),
16813
- blockKind: z18.string().optional(),
16814
- blockPurpose: z18.string().optional().nullable(),
16815
- ops: z18.array(Rfc6902PatchOp),
16816
- rationale: z18.string(),
16817
- currentContent: z18.record(z18.string(), z18.unknown()).optional()
16818
- });
16819
- var ContentUpdateResponse = z18.object({
16820
- patches: z18.array(PatchEnvelope),
16821
- assistantMessage: z18.string()
16822
- });
16823
- var PlaygroundProposeRequest = z18.object({
16824
- request: z18.string().min(1).max(2e3)
16825
- });
16826
- var PlaygroundProposeResponse = z18.object({
16827
- patches: z18.array(PatchEnvelope),
16828
- assistantMessage: z18.string(),
16829
- validation: z18.object({
16830
- valid: z18.boolean(),
16831
- issues: z18.array(z18.string()),
16832
- filtered: z18.number()
16833
- })
16834
- });
16835
- var MultiPagePatchEnvelope = z18.object({
16836
- pageId: z18.string(),
16837
- blockId: z18.string().optional(),
16838
- // Not present for page-level ops
16839
- blockKind: z18.string().optional(),
16840
- blockPurpose: z18.string().optional().nullable(),
16841
- ops: z18.array(Rfc6902PatchOp),
16842
- rationale: z18.string(),
16843
- currentContent: z18.record(z18.string(), z18.unknown()).optional()
16844
- });
16845
- var MultiPageUpdateResponse = z18.object({
16846
- patches: z18.array(MultiPagePatchEnvelope),
16847
- assistantMessage: z18.string(),
16848
- pagesModified: z18.number(),
16849
- toolCallsUsed: z18.number()
16850
- });
17552
+ // ../api/src/common/envelope.ts
17553
+ function isApiError(result) {
17554
+ return result.success === false;
17555
+ }
17556
+ function isApiSuccess(result) {
17557
+ return result.success === true;
17558
+ }
17559
+ var ApiEnvelopeError = class extends Error {
17560
+ constructor(error) {
17561
+ super(error.message);
17562
+ this.name = "ApiEnvelopeError";
17563
+ this.code = error.code;
17564
+ this.requestId = error.requestId;
17565
+ this.timestamp = error.timestamp;
17566
+ this.status = error.status;
17567
+ this.fieldErrors = error.fieldErrors;
17568
+ }
17569
+ };
17570
+ function unwrapResponse(result) {
17571
+ if (isApiSuccess(result)) {
17572
+ return result.data;
17573
+ }
17574
+ throw new ApiEnvelopeError(result.error);
17575
+ }
16851
17576
 
16852
17577
  // src/client/cache.ts
16853
17578
  var SimpleCache = class {
@@ -16885,7 +17610,100 @@ var SimpleCache = class {
16885
17610
  }
16886
17611
  };
16887
17612
 
17613
+ // src/version.ts
17614
+ var SDK_VERSION = "0.4.3";
17615
+
17616
+ // src/client/error.ts
17617
+ var RiverbankApiError = class _RiverbankApiError extends Error {
17618
+ constructor(apiError) {
17619
+ super(apiError.message);
17620
+ this.name = "RiverbankApiError";
17621
+ this.code = apiError.code;
17622
+ this.requestId = apiError.requestId;
17623
+ this.status = apiError.status;
17624
+ this.fieldErrors = apiError.fieldErrors;
17625
+ this.timestamp = apiError.timestamp;
17626
+ Object.setPrototypeOf(this, _RiverbankApiError.prototype);
17627
+ }
17628
+ /**
17629
+ * Check if this error matches a specific error code
17630
+ *
17631
+ * @example
17632
+ * ```ts
17633
+ * if (error.is('auth:unauthenticated')) {
17634
+ * // Redirect to login
17635
+ * }
17636
+ * ```
17637
+ */
17638
+ is(code) {
17639
+ return this.code === code;
17640
+ }
17641
+ /**
17642
+ * Check if this is an authentication or authorization error
17643
+ *
17644
+ * Matches: auth:unauthenticated, auth:token_expired, auth:token_invalid,
17645
+ * auth:forbidden, auth:mfa_required, auth:insufficient_permissions
17646
+ */
17647
+ isAuthError() {
17648
+ return this.code.startsWith("auth:");
17649
+ }
17650
+ /**
17651
+ * Check if this is a validation error
17652
+ *
17653
+ * Matches: validation:invalid_input, validation:missing_field, validation:invalid_format
17654
+ */
17655
+ isValidationError() {
17656
+ return this.code.startsWith("validation:");
17657
+ }
17658
+ /**
17659
+ * Check if this is a resource error (not found, conflict, etc.)
17660
+ *
17661
+ * Matches: resource:not_found, resource:already_exists, resource:conflict, resource:gone
17662
+ */
17663
+ isResourceError() {
17664
+ return this.code.startsWith("resource:");
17665
+ }
17666
+ /**
17667
+ * Check if this is a rate limiting error
17668
+ */
17669
+ isRateLimitError() {
17670
+ return this.code.startsWith("rate_limit:");
17671
+ }
17672
+ /**
17673
+ * Check if this is a billing/payment error
17674
+ */
17675
+ isBillingError() {
17676
+ return this.code.startsWith("billing:");
17677
+ }
17678
+ /**
17679
+ * Check if this is a server error
17680
+ */
17681
+ isServerError() {
17682
+ return this.code.startsWith("server:");
17683
+ }
17684
+ };
17685
+
16888
17686
  // src/client/index.ts
17687
+ setSdkVersion(SDK_VERSION);
17688
+ function convertToTypedError(error) {
17689
+ if (error instanceof ApiEnvelopeError) {
17690
+ throw new RiverbankApiError({
17691
+ code: error.code,
17692
+ message: error.message,
17693
+ requestId: error.requestId,
17694
+ timestamp: error.timestamp,
17695
+ status: error.status,
17696
+ fieldErrors: error.fieldErrors
17697
+ });
17698
+ }
17699
+ if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
17700
+ const body = error.body;
17701
+ if (isApiError(body)) {
17702
+ throw new RiverbankApiError(body.error);
17703
+ }
17704
+ }
17705
+ throw error;
17706
+ }
16889
17707
  function createRiverbankClient(config) {
16890
17708
  if (!config.baseUrl) {
16891
17709
  throw new Error(
@@ -16912,7 +17730,13 @@ function createRiverbankClient(config) {
16912
17730
  return cached;
16913
17731
  }
16914
17732
  }
16915
- const data = await fetcher();
17733
+ let data;
17734
+ try {
17735
+ const response = await fetcher();
17736
+ data = unwrapResponse(response);
17737
+ } catch (error) {
17738
+ convertToTypedError(error);
17739
+ }
16916
17740
  if (cacheEnabled) {
16917
17741
  cache.set(cacheKey, data);
16918
17742
  }
@@ -16947,33 +17771,27 @@ function createRiverbankClient(config) {
16947
17771
  const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
16948
17772
  const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
16949
17773
  return cachedFetch(cacheKey, async () => {
16950
- const apiParams = {
16951
- siteId,
16952
- type: contentType
16953
- };
16954
- if (typeof limit === "number") {
16955
- apiParams.limit = String(limit);
16956
- }
16957
- if (typeof offset === "number") {
16958
- apiParams.offset = String(offset);
16959
- }
16960
- if (includeMeta) {
16961
- apiParams.meta = "true";
16962
- }
17774
+ let orderParam;
16963
17775
  if (order === "newest") {
16964
- apiParams.order = "published_at.desc";
17776
+ orderParam = "published_at.desc";
16965
17777
  } else if (order === "oldest") {
16966
- apiParams.order = "published_at.asc";
17778
+ orderParam = "published_at.asc";
16967
17779
  } else if (order === "title") {
16968
- apiParams.order = "title.asc";
16969
- }
16970
- if (preview) {
16971
- apiParams.stage = "preview";
16972
- }
16973
- if (mode === "manual" && entryIds?.length) {
16974
- apiParams.mode = "manual";
16975
- apiParams.entryIds = JSON.stringify(entryIds);
17780
+ orderParam = "title.asc";
16976
17781
  }
17782
+ const apiParams = {
17783
+ siteId,
17784
+ type: contentType,
17785
+ ...typeof limit === "number" && { limit: String(limit) },
17786
+ ...typeof offset === "number" && { offset: String(offset) },
17787
+ ...includeMeta && { meta: "true" },
17788
+ ...orderParam && { order: orderParam },
17789
+ ...preview && { stage: "preview" },
17790
+ ...mode === "manual" && entryIds?.length && {
17791
+ mode: "manual",
17792
+ entryIds: JSON.stringify(entryIds)
17793
+ }
17794
+ };
16977
17795
  return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
16978
17796
  });
16979
17797
  },
@@ -17001,8 +17819,10 @@ function createRiverbankClient(config) {
17001
17819
  }
17002
17820
  const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
17003
17821
  return cachedFetch(cacheKey, async () => {
17004
- const apiParams = { siteId };
17005
- if (ids) apiParams.ids = ids;
17822
+ const apiParams = {
17823
+ siteId,
17824
+ ...ids && { ids }
17825
+ };
17006
17826
  return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
17007
17827
  });
17008
17828
  },
@@ -17013,14 +17833,42 @@ function createRiverbankClient(config) {
17013
17833
  }
17014
17834
  const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
17015
17835
  return cachedFetch(cacheKey, async () => {
17016
- const apiParams = { siteId };
17017
- if (typeof limit === "number") apiParams.limit = String(limit);
17018
- if (from) apiParams.from = from;
17019
- if (to) apiParams.to = to;
17020
- if (stage) apiParams.stage = stage;
17836
+ const apiParams = {
17837
+ siteId,
17838
+ ...typeof limit === "number" && { limit: String(limit) },
17839
+ ...from && { from },
17840
+ ...to && { to },
17841
+ ...stage && { stage }
17842
+ };
17021
17843
  return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
17022
17844
  });
17023
17845
  },
17846
+ async resolveEventOccurrence(params) {
17847
+ const { siteId, entryId, segment } = params;
17848
+ if (!siteId || !entryId || !segment) {
17849
+ throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
17850
+ }
17851
+ const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
17852
+ return cachedFetch(cacheKey, async () => {
17853
+ return await apiClient({
17854
+ endpoint: "resolveEventOccurrence",
17855
+ params: { siteId, entryId, segment }
17856
+ });
17857
+ });
17858
+ },
17859
+ async checkRedirect(params) {
17860
+ const { siteId, path } = params;
17861
+ if (!siteId || !path) {
17862
+ throw new Error("checkRedirect() requires siteId and path");
17863
+ }
17864
+ const cacheKey = `redirect:${siteId}:${path}`;
17865
+ return cachedFetch(cacheKey, async () => {
17866
+ return await apiClient({
17867
+ endpoint: "checkRedirect",
17868
+ params: { site: siteId, path }
17869
+ });
17870
+ });
17871
+ },
17024
17872
  clearCache() {
17025
17873
  cache.clear();
17026
17874
  }