@riverbankcms/sdk 0.4.3 → 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 (176) 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 +1357 -519
  15. package/dist/client/client.js.map +1 -1
  16. package/dist/client/client.mjs +1357 -519
  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 +20 -14
  25. package/dist/client/rendering/client.js.map +1 -1
  26. package/dist/client/rendering/client.mjs +20 -14
  27. package/dist/client/rendering/client.mjs.map +1 -1
  28. package/dist/client/usePage-BTPnCuWC.d.mts +6511 -0
  29. package/dist/client/usePage-BafOS9UT.d.mts +6512 -0
  30. package/dist/client/usePage-Bnx-kA6x.d.mts +6670 -0
  31. package/dist/client/usePage-DoPI6b8V.d.ts +6511 -0
  32. package/dist/client/usePage-QNWArrVO.d.ts +6670 -0
  33. package/dist/client/usePage-fBgPB6Oq.d.ts +6512 -0
  34. package/dist/server/{Layout-CXI_VkhN.d.ts → Layout-B-q2Py4v.d.ts} +4 -4
  35. package/dist/server/{Layout-p6f3TLw9.d.mts → Layout-Cc5HUXAH.d.mts} +4 -4
  36. package/dist/server/{chunk-6JBKKV3G.js → chunk-2KCF2DNK.js} +30 -10
  37. package/dist/server/chunk-2KCF2DNK.js.map +1 -0
  38. package/dist/server/{chunk-N3PX76AP.mjs → chunk-4HIRA33Z.mjs} +247 -135
  39. package/dist/server/chunk-4HIRA33Z.mjs.map +1 -0
  40. package/dist/server/chunk-5STV4MWD.js +189 -0
  41. package/dist/server/chunk-5STV4MWD.js.map +1 -0
  42. package/dist/server/{chunk-R5B6IOFQ.js → chunk-6OSNCH4F.js} +247 -135
  43. package/dist/server/chunk-6OSNCH4F.js.map +1 -0
  44. package/dist/server/{chunk-VHDDXCK6.js → chunk-7UPVCT3K.js} +1206 -496
  45. package/dist/server/chunk-7UPVCT3K.js.map +1 -0
  46. package/dist/server/{chunk-7DS4Q3GA.mjs → chunk-AEFWG657.mjs} +3 -3
  47. package/dist/server/chunk-AEFWG657.mjs.map +1 -0
  48. package/dist/server/{chunk-USQF2XTU.mjs → chunk-BYBJA6SP.mjs} +26 -11
  49. package/dist/server/chunk-BYBJA6SP.mjs.map +1 -0
  50. package/dist/server/{chunk-ES6QDZUX.mjs → chunk-C6FIJC7T.mjs} +2 -2
  51. package/dist/server/{chunk-TO7FD6TQ.js → chunk-I2D7KOEA.js} +4 -4
  52. package/dist/server/{chunk-TO7FD6TQ.js.map → chunk-I2D7KOEA.js.map} +1 -1
  53. package/dist/server/chunk-KFLZGNPO.mjs +189 -0
  54. package/dist/server/chunk-KFLZGNPO.mjs.map +1 -0
  55. package/dist/server/chunk-L5EA4FXU.mjs +134 -0
  56. package/dist/server/chunk-L5EA4FXU.mjs.map +1 -0
  57. package/dist/server/{chunk-U2NI3TS3.mjs → chunk-LNOUXALA.mjs} +1135 -425
  58. package/dist/server/chunk-LNOUXALA.mjs.map +1 -0
  59. package/dist/server/{chunk-24F6FTCI.mjs → chunk-OSF34JTQ.mjs} +4 -4
  60. package/dist/server/{chunk-G35R7N7B.js → chunk-P3NNN73G.js} +3 -3
  61. package/dist/server/{chunk-G35R7N7B.js.map → chunk-P3NNN73G.js.map} +1 -1
  62. package/dist/server/{chunk-I6K5REFT.mjs → chunk-P4K63SBZ.mjs} +24 -4
  63. package/dist/server/chunk-P4K63SBZ.mjs.map +1 -0
  64. package/dist/server/{chunk-HOY77YBF.js → chunk-RVDS7VSP.js} +5 -5
  65. package/dist/server/chunk-RVDS7VSP.js.map +1 -0
  66. package/dist/server/{chunk-2SSEBAHC.js → chunk-TT5JWA4X.js} +9 -9
  67. package/dist/server/{chunk-2SSEBAHC.js.map → chunk-TT5JWA4X.js.map} +1 -1
  68. package/dist/server/chunk-VSFQRHYZ.js +134 -0
  69. package/dist/server/chunk-VSFQRHYZ.js.map +1 -0
  70. package/dist/server/{chunk-EGTDJ4PL.js → chunk-YYO3RIFO.js} +26 -11
  71. package/dist/server/chunk-YYO3RIFO.js.map +1 -0
  72. package/dist/server/{chunk-OP2GHK27.mjs → chunk-Z5ZA6Q4D.mjs} +2 -2
  73. package/dist/server/{components-Dhiemsjd.d.ts → components-CU46ZkAv.d.mts} +20 -75
  74. package/dist/server/{components-C75e4poV.d.mts → components-DvozDwRN.d.ts} +20 -75
  75. package/dist/server/components.d.mts +11 -8
  76. package/dist/server/components.d.ts +11 -8
  77. package/dist/server/components.js +5 -4
  78. package/dist/server/components.js.map +1 -1
  79. package/dist/server/components.mjs +4 -3
  80. package/dist/server/config-validation.d.mts +3 -3
  81. package/dist/server/config-validation.d.ts +3 -3
  82. package/dist/server/config-validation.js +9 -5
  83. package/dist/server/config-validation.js.map +1 -1
  84. package/dist/server/config-validation.mjs +8 -4
  85. package/dist/server/config.d.mts +243 -5
  86. package/dist/server/config.d.ts +243 -5
  87. package/dist/server/config.js +72 -5
  88. package/dist/server/config.js.map +1 -1
  89. package/dist/server/config.mjs +72 -5
  90. package/dist/server/config.mjs.map +1 -1
  91. package/dist/server/core-DsNWrl3o.d.mts +44 -0
  92. package/dist/server/core-DsNWrl3o.d.ts +44 -0
  93. package/dist/server/data.d.mts +4 -3
  94. package/dist/server/data.d.ts +4 -3
  95. package/dist/server/data.js +3 -3
  96. package/dist/server/data.mjs +2 -2
  97. package/dist/server/{index-C6o9LPvq.d.mts → index-CJfMXZQr.d.ts} +2 -1
  98. package/dist/server/{index-CAwBj3-A.d.ts → index-Q7RLMAQ6.d.mts} +2 -1
  99. package/dist/server/index.d.mts +63 -6
  100. package/dist/server/index.d.ts +63 -6
  101. package/dist/server/index.js +91 -2
  102. package/dist/server/index.js.map +1 -1
  103. package/dist/server/index.mjs +90 -1
  104. package/dist/server/index.mjs.map +1 -1
  105. package/dist/server/link-DjxLyC82.d.mts +23 -0
  106. package/dist/server/link-DjxLyC82.d.ts +23 -0
  107. package/dist/server/{loadContent-CdXfuCuE.d.mts → loadContent-DgpSKWqY.d.mts} +4 -4
  108. package/dist/server/{loadContent-CsvQRoxb.d.ts → loadContent-GPvUI1bN.d.ts} +4 -4
  109. package/dist/server/{loadPage-p3AWwwrd.d.mts → loadPage-DGnIK7s4.d.mts} +5 -46
  110. package/dist/server/loadPage-DNQTTRHL.mjs +11 -0
  111. package/dist/server/{loadPage-BA0HiT-6.d.ts → loadPage-DW9WB-u9.d.ts} +5 -46
  112. package/dist/server/loadPage-IDGVDFBB.js +11 -0
  113. package/dist/server/{loadPage-DLC7DJZP.js.map → loadPage-IDGVDFBB.js.map} +1 -1
  114. package/dist/server/metadata.d.mts +6 -4
  115. package/dist/server/metadata.d.ts +6 -4
  116. package/dist/server/navigation.d.mts +199 -29
  117. package/dist/server/navigation.d.ts +199 -29
  118. package/dist/server/navigation.js +27 -43
  119. package/dist/server/navigation.js.map +1 -1
  120. package/dist/server/navigation.mjs +20 -36
  121. package/dist/server/navigation.mjs.map +1 -1
  122. package/dist/server/rendering/server.d.mts +8 -6
  123. package/dist/server/rendering/server.d.ts +8 -6
  124. package/dist/server/rendering/server.js +7 -6
  125. package/dist/server/rendering/server.js.map +1 -1
  126. package/dist/server/rendering/server.mjs +6 -5
  127. package/dist/server/rendering.d.mts +14 -10
  128. package/dist/server/rendering.d.ts +14 -10
  129. package/dist/server/rendering.js +9 -8
  130. package/dist/server/rendering.js.map +1 -1
  131. package/dist/server/rendering.mjs +8 -7
  132. package/dist/server/richTextSchema-DURiozvD.d.mts +62 -0
  133. package/dist/server/richTextSchema-DURiozvD.d.ts +62 -0
  134. package/dist/server/routing.d.mts +178 -11
  135. package/dist/server/routing.d.ts +178 -11
  136. package/dist/server/routing.js +95 -2
  137. package/dist/server/routing.js.map +1 -1
  138. package/dist/server/routing.mjs +94 -1
  139. package/dist/server/routing.mjs.map +1 -1
  140. package/dist/server/{schema-Bpy9N5ZI.d.mts → schema-Z6-afHJG.d.mts} +1 -1
  141. package/dist/server/{schema-Bpy9N5ZI.d.ts → schema-Z6-afHJG.d.ts} +1 -1
  142. package/dist/server/server.d.mts +9 -7
  143. package/dist/server/server.d.ts +9 -7
  144. package/dist/server/server.js +6 -6
  145. package/dist/server/server.mjs +5 -5
  146. package/dist/server/theme-bridge.js +8 -8
  147. package/dist/server/theme-bridge.mjs +2 -2
  148. package/dist/server/{types-Dj8B3QRb.d.ts → types-0f4PIlgx.d.mts} +55 -2
  149. package/dist/server/{types-txWsSxN7.d.mts → types-BjgZt8xJ.d.mts} +63 -2
  150. package/dist/server/{types-BWQ-TohG.d.ts → types-C28kMfa1.d.ts} +254 -82
  151. package/dist/server/{types-CL916r6x.d.ts → types-DLBhEPSt.d.ts} +63 -2
  152. package/dist/server/{types-BLf-hE50.d.mts → types-DuzJZKJI.d.mts} +254 -82
  153. package/dist/server/{types-CdhKJrB0.d.mts → types-kOQyCFXO.d.ts} +55 -2
  154. package/dist/server/{validation-DzvDwwRo.d.mts → validation-BGuRo8P1.d.mts} +18 -5
  155. package/dist/server/{validation-CoU8uAiu.d.ts → validation-DU2YE7u5.d.ts} +18 -5
  156. package/package.json +3 -1
  157. package/dist/server/chunk-6JBKKV3G.js.map +0 -1
  158. package/dist/server/chunk-7DS4Q3GA.mjs.map +0 -1
  159. package/dist/server/chunk-EGTDJ4PL.js.map +0 -1
  160. package/dist/server/chunk-HOY77YBF.js.map +0 -1
  161. package/dist/server/chunk-I6K5REFT.mjs.map +0 -1
  162. package/dist/server/chunk-LCYGQDAB.mjs +0 -835
  163. package/dist/server/chunk-LCYGQDAB.mjs.map +0 -1
  164. package/dist/server/chunk-N3PX76AP.mjs.map +0 -1
  165. package/dist/server/chunk-R5B6IOFQ.js.map +0 -1
  166. package/dist/server/chunk-TNYU5EIO.js +0 -835
  167. package/dist/server/chunk-TNYU5EIO.js.map +0 -1
  168. package/dist/server/chunk-U2NI3TS3.mjs.map +0 -1
  169. package/dist/server/chunk-USQF2XTU.mjs.map +0 -1
  170. package/dist/server/chunk-VHDDXCK6.js.map +0 -1
  171. package/dist/server/loadPage-DLC7DJZP.js +0 -11
  172. package/dist/server/loadPage-GEGN4UAL.mjs +0 -11
  173. /package/dist/server/{chunk-ES6QDZUX.mjs.map → chunk-C6FIJC7T.mjs.map} +0 -0
  174. /package/dist/server/{chunk-24F6FTCI.mjs.map → chunk-OSF34JTQ.mjs.map} +0 -0
  175. /package/dist/server/{chunk-OP2GHK27.mjs.map → chunk-Z5ZA6Q4D.mjs.map} +0 -0
  176. /package/dist/server/{loadPage-GEGN4UAL.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
  };
@@ -7470,7 +7489,7 @@ var COLUMN_CLASSES = {
7470
7489
  "4": "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
7471
7490
  };
7472
7491
  function formatDateForUrl(isoTimestamp) {
7473
- return isoTimestamp.split("T")[0];
7492
+ return isoTimestamp.split("T")[0] ?? "";
7474
7493
  }
7475
7494
  function buildEventUrl(basePath, slug, startsAt) {
7476
7495
  const date = formatDateForUrl(startsAt);
@@ -7655,7 +7674,7 @@ function getContainerClass(layout, columns) {
7655
7674
  return `grid gap-6 ${COLUMN_CLASSES2[columns] || COLUMN_CLASSES2["3"]}`;
7656
7675
  }
7657
7676
  function formatDateForUrl2(isoTimestamp) {
7658
- return isoTimestamp.split("T")[0];
7677
+ return isoTimestamp.split("T")[0] ?? "";
7659
7678
  }
7660
7679
  function buildEventUrl2(basePath, slug, startsAt) {
7661
7680
  const date = formatDateForUrl2(startsAt);
@@ -7874,7 +7893,7 @@ function extractSegmentsFromPart(part) {
7874
7893
  if (!part) return [];
7875
7894
  const segments = [];
7876
7895
  const baseMatch = part.match(/^([^\[]+)/);
7877
- if (baseMatch) {
7896
+ if (baseMatch && baseMatch[1]) {
7878
7897
  segments.push(baseMatch[1]);
7879
7898
  }
7880
7899
  const bracketMatches = Array.from(part.matchAll(BRACKET_ACCESS_REGEX));
@@ -8840,8 +8859,8 @@ function getLabWhitePoint() {
8840
8859
  var lab2rgb = (...args) => {
8841
8860
  args = unpack_default(args, "lab");
8842
8861
  const [L, a, b] = args;
8843
- const [x, y, z19] = lab2xyz(L, a, b);
8844
- 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);
8845
8864
  return [r2, g, b_, args.length > 3 ? args[3] : 1];
8846
8865
  };
8847
8866
  var lab2xyz = (L, a, b) => {
@@ -8856,15 +8875,15 @@ var lab2xyz = (L, a, b) => {
8856
8875
  const zr = fz3 > kE ? fz3 : (116 * fz - 16) / kK;
8857
8876
  const x = xr * Xn;
8858
8877
  const y = yr * Yn;
8859
- const z19 = zr * Zn;
8860
- return [x, y, z19];
8878
+ const z20 = zr * Zn;
8879
+ return [x, y, z20];
8861
8880
  };
8862
8881
  var compand = (linear) => {
8863
8882
  const sign = Math.sign(linear);
8864
8883
  linear = Math.abs(linear);
8865
8884
  return (linear <= 31308e-7 ? linear * 12.92 : 1.055 * Math.pow(linear, 1 / 2.4) - 0.055) * sign;
8866
8885
  };
8867
- var xyz2rgb = (x, y, z19) => {
8886
+ var xyz2rgb = (x, y, z20) => {
8868
8887
  const { MtxAdaptMa, MtxAdaptMaI, MtxXYZ2RGB, RefWhiteRGB, Xn, Yn, Zn } = lab_constants_default;
8869
8888
  const As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;
8870
8889
  const Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;
@@ -8872,9 +8891,9 @@ var xyz2rgb = (x, y, z19) => {
8872
8891
  const Ad = RefWhiteRGB.X * MtxAdaptMa.m00 + RefWhiteRGB.Y * MtxAdaptMa.m10 + RefWhiteRGB.Z * MtxAdaptMa.m20;
8873
8892
  const Bd = RefWhiteRGB.X * MtxAdaptMa.m01 + RefWhiteRGB.Y * MtxAdaptMa.m11 + RefWhiteRGB.Z * MtxAdaptMa.m21;
8874
8893
  const Cd = RefWhiteRGB.X * MtxAdaptMa.m02 + RefWhiteRGB.Y * MtxAdaptMa.m12 + RefWhiteRGB.Z * MtxAdaptMa.m22;
8875
- const X1 = (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z19 * MtxAdaptMa.m20) * (Ad / As);
8876
- const Y1 = (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z19 * MtxAdaptMa.m21) * (Bd / Bs);
8877
- 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);
8878
8897
  const X2 = X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;
8879
8898
  const Y2 = X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;
8880
8899
  const Z2 = X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;
@@ -8894,15 +8913,15 @@ var lab2rgb_default = lab2rgb;
8894
8913
  // ../../node_modules/.pnpm/chroma-js@3.1.2/node_modules/chroma-js/src/io/lab/rgb2lab.js
8895
8914
  var rgb2lab = (...args) => {
8896
8915
  const [r2, g, b, ...rest] = unpack_default(args, "rgb");
8897
- const [x, y, z19] = rgb2xyz(r2, g, b);
8898
- 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);
8899
8918
  return [L, a, b_, ...rest.length > 0 && rest[0] < 1 ? [rest[0]] : []];
8900
8919
  };
8901
- function xyz2lab(x, y, z19) {
8920
+ function xyz2lab(x, y, z20) {
8902
8921
  const { Xn, Yn, Zn, kE, kK } = lab_constants_default;
8903
8922
  const xr = x / Xn;
8904
8923
  const yr = y / Yn;
8905
- const zr = z19 / Zn;
8924
+ const zr = z20 / Zn;
8906
8925
  const fx = xr > kE ? Math.pow(xr, 1 / 3) : (kK * xr + 16) / 116;
8907
8926
  const fy = yr > kE ? Math.pow(yr, 1 / 3) : (kK * yr + 16) / 116;
8908
8927
  const fz = zr > kE ? Math.pow(zr, 1 / 3) : (kK * zr + 16) / 116;
@@ -8921,20 +8940,20 @@ var rgb2xyz = (r2, g, b) => {
8921
8940
  const { MtxRGB2XYZ, MtxAdaptMa, MtxAdaptMaI, Xn, Yn, Zn, As, Bs, Cs } = lab_constants_default;
8922
8941
  let x = r2 * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;
8923
8942
  let y = r2 * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;
8924
- let z19 = r2 * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;
8943
+ let z20 = r2 * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;
8925
8944
  const Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;
8926
8945
  const Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;
8927
8946
  const Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;
8928
- let X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z19 * MtxAdaptMa.m20;
8929
- let Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z19 * MtxAdaptMa.m21;
8930
- 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;
8931
8950
  X *= Ad / As;
8932
8951
  Y *= Bd / Bs;
8933
8952
  Z *= Cd / Cs;
8934
8953
  x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;
8935
8954
  y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;
8936
- z19 = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;
8937
- return [x, y, z19];
8955
+ z20 = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;
8956
+ return [x, y, z20];
8938
8957
  };
8939
8958
  var rgb2lab_default = rgb2lab;
8940
8959
 
@@ -11263,7 +11282,7 @@ function generateShades(hexColor, name) {
11263
11282
  900: 0.1,
11264
11283
  950: 0.05
11265
11284
  };
11266
- const lightness = lightnessMap[shadeNumber];
11285
+ const lightness = lightnessMap[shadeNumber] ?? 0.5;
11267
11286
  const shadeColor = base.luminance(lightness).hex();
11268
11287
  return {
11269
11288
  name: shadeName,
@@ -11334,7 +11353,7 @@ function expandPalette(palette) {
11334
11353
  if (base.text) expanded.body = base.text;
11335
11354
  const isDark = base.background ? isBackgroundDark(base.background) : false;
11336
11355
  const isDarkMode = palette.meta?.mode === "dark" || isDark;
11337
- expanded.mutedText = isDarkMode ? expanded["neutral-600"] : expanded["neutral-500"];
11356
+ expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
11338
11357
  return expanded;
11339
11358
  }
11340
11359
 
@@ -11402,6 +11421,83 @@ function generateShadowCssVars(theme) {
11402
11421
  };
11403
11422
  }
11404
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
+
11405
11501
  // ../blocks/src/theme/buttons/effects/presets/background.ts
11406
11502
  var darkenBackgroundEffect = {
11407
11503
  id: "darken-background",
@@ -11409,6 +11505,7 @@ var darkenBackgroundEffect = {
11409
11505
  description: "Darkens the background color on hover",
11410
11506
  category: "background",
11411
11507
  pseudoElement: "none",
11508
+ applicableTo: ["button"],
11412
11509
  customizableOption: {
11413
11510
  name: "amount",
11414
11511
  type: "number",
@@ -11440,6 +11537,7 @@ var lightenBackgroundEffect = {
11440
11537
  description: "Lightens the background color on hover",
11441
11538
  category: "background",
11442
11539
  pseudoElement: "none",
11540
+ applicableTo: ["button"],
11443
11541
  customizableOption: {
11444
11542
  name: "amount",
11445
11543
  type: "number",
@@ -11471,6 +11569,7 @@ var fadeBackgroundEffect = {
11471
11569
  description: "Reduces background opacity on hover",
11472
11570
  category: "background",
11473
11571
  pseudoElement: "none",
11572
+ applicableTo: ["button", "nav-link"],
11474
11573
  customizableOption: {
11475
11574
  name: "opacity",
11476
11575
  type: "percentage",
@@ -11493,6 +11592,7 @@ var opacityReduceEffect = {
11493
11592
  description: "Reduces button opacity on hover by a percentage",
11494
11593
  category: "background",
11495
11594
  pseudoElement: "none",
11595
+ applicableTo: ["button", "nav-link"],
11496
11596
  customizableOption: {
11497
11597
  name: "reduction",
11498
11598
  type: "percentage",
@@ -11516,6 +11616,7 @@ var animatedGradientEffect = {
11516
11616
  description: "Animates gradient position on hover",
11517
11617
  category: "background",
11518
11618
  pseudoElement: "none",
11619
+ applicableTo: ["button"],
11519
11620
  customizableOption: {
11520
11621
  name: "direction",
11521
11622
  type: "select",
@@ -11555,6 +11656,7 @@ var backgroundOnHoverEffect = {
11555
11656
  description: "Adds background color on hover",
11556
11657
  category: "background",
11557
11658
  pseudoElement: "none",
11659
+ applicableTo: ["button", "nav-link"],
11558
11660
  customizableOption: {
11559
11661
  name: "colorToken",
11560
11662
  type: "select",
@@ -11580,6 +11682,7 @@ var backgroundOnHoverAlphaEffect = {
11580
11682
  description: "Adds semi-transparent background on hover",
11581
11683
  category: "background",
11582
11684
  pseudoElement: "none",
11685
+ applicableTo: ["button", "nav-link"],
11583
11686
  customizableOption: {
11584
11687
  name: "opacity",
11585
11688
  type: "percentage",
@@ -11601,6 +11704,33 @@ var backgroundOnHoverAlphaEffect = {
11601
11704
  }
11602
11705
  };
11603
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
+
11604
11734
  // ../blocks/src/theme/buttons/effects/presets/shadow.ts
11605
11735
  var borderGlowEffect = {
11606
11736
  id: "border-glow",
@@ -11608,6 +11738,7 @@ var borderGlowEffect = {
11608
11738
  description: "Adds a glowing shadow on hover",
11609
11739
  category: "shadow",
11610
11740
  pseudoElement: "none",
11741
+ applicableTo: ["button", "nav-link"],
11611
11742
  customizableOption: {
11612
11743
  name: "glowColorToken",
11613
11744
  type: "colorToken",
@@ -11618,11 +11749,7 @@ var borderGlowEffect = {
11618
11749
  const { themeId, variantId, options, tokens } = ctx;
11619
11750
  const glowToken = options.glowColorToken || "primary-400";
11620
11751
  const glowColor = tokens.getColor(glowToken);
11621
- return `
11622
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11623
- box-shadow: 0 0 15px ${glowColor};
11624
- }
11625
- `;
11752
+ return generateStateCSS(themeId, variantId, "hover", `box-shadow: 0 0 15px ${glowColor};`);
11626
11753
  }
11627
11754
  };
11628
11755
  var dropShadowEffect = {
@@ -11631,6 +11758,7 @@ var dropShadowEffect = {
11631
11758
  description: "Adds an elevated shadow on hover",
11632
11759
  category: "shadow",
11633
11760
  pseudoElement: "none",
11761
+ applicableTo: ["button"],
11634
11762
  customizableOption: {
11635
11763
  name: "intensity",
11636
11764
  type: "select",
@@ -11646,11 +11774,7 @@ var dropShadowEffect = {
11646
11774
  medium: "0 10px 15px rgba(0, 0, 0, 0.15)",
11647
11775
  strong: "0 20px 25px rgba(0, 0, 0, 0.2)"
11648
11776
  };
11649
- return `
11650
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11651
- box-shadow: ${shadows[intensity]};
11652
- }
11653
- `;
11777
+ return generateStateCSS(themeId, variantId, "hover", `box-shadow: ${shadows[intensity]};`);
11654
11778
  }
11655
11779
  };
11656
11780
  var innerShadowEffect = {
@@ -11659,6 +11783,7 @@ var innerShadowEffect = {
11659
11783
  description: "Adds an inset shadow for a pressed appearance",
11660
11784
  category: "shadow",
11661
11785
  pseudoElement: "none",
11786
+ applicableTo: ["button"],
11662
11787
  generateCSS: (ctx) => {
11663
11788
  const { themeId, variantId } = ctx;
11664
11789
  return `
@@ -11674,6 +11799,7 @@ var neumorphicShadowEffect = {
11674
11799
  description: "Dual shadows for soft UI design (raised or pressed)",
11675
11800
  category: "shadow",
11676
11801
  pseudoElement: "none",
11802
+ applicableTo: ["button", "nav-link"],
11677
11803
  customizableOption: {
11678
11804
  name: "mode",
11679
11805
  type: "select",
@@ -11716,6 +11842,7 @@ var outerGlowEffect = {
11716
11842
  description: "Creates an outer glow around the button",
11717
11843
  category: "shadow",
11718
11844
  pseudoElement: "none",
11845
+ applicableTo: ["button", "nav-link"],
11719
11846
  customizableOption: {
11720
11847
  name: "spread",
11721
11848
  type: "number",
@@ -11750,6 +11877,7 @@ var retro3DShadowEffect = {
11750
11877
  description: "3D pressed shadow with vertical movement (retro style)",
11751
11878
  category: "shadow",
11752
11879
  pseudoElement: "none",
11880
+ applicableTo: ["button"],
11753
11881
  generateCSS: (ctx) => {
11754
11882
  const { themeId, variantId } = ctx;
11755
11883
  return `
@@ -11782,6 +11910,7 @@ var scaleUpEffect = {
11782
11910
  description: "Slightly enlarges the button on hover",
11783
11911
  category: "transform",
11784
11912
  pseudoElement: "none",
11913
+ applicableTo: ["button", "nav-link"],
11785
11914
  customizableOption: {
11786
11915
  name: "scale",
11787
11916
  type: "select",
@@ -11792,11 +11921,7 @@ var scaleUpEffect = {
11792
11921
  generateCSS: (ctx) => {
11793
11922
  const { themeId, variantId, options } = ctx;
11794
11923
  const scale = options.scale || "1.05";
11795
- return `
11796
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11797
- transform: scale(${scale});
11798
- }
11799
- `;
11924
+ return generateStateCSS(themeId, variantId, "hover", `transform: scale(${scale});`);
11800
11925
  }
11801
11926
  };
11802
11927
  var scaleDownEffect = {
@@ -11805,6 +11930,7 @@ var scaleDownEffect = {
11805
11930
  description: "Slightly shrinks the button when clicked",
11806
11931
  category: "transform",
11807
11932
  pseudoElement: "none",
11933
+ applicableTo: ["button", "nav-link"],
11808
11934
  customizableOption: {
11809
11935
  name: "scale",
11810
11936
  type: "select",
@@ -11828,6 +11954,7 @@ var liftEffect = {
11828
11954
  description: "Lifts the button upward on hover",
11829
11955
  category: "transform",
11830
11956
  pseudoElement: "none",
11957
+ applicableTo: ["button", "nav-link"],
11831
11958
  customizableOption: {
11832
11959
  name: "amount",
11833
11960
  type: "select",
@@ -11838,11 +11965,7 @@ var liftEffect = {
11838
11965
  generateCSS: (ctx) => {
11839
11966
  const { themeId, variantId, options } = ctx;
11840
11967
  const amount = options.amount || "0.5";
11841
- return `
11842
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11843
- transform: translateY(-${amount}rem);
11844
- }
11845
- `;
11968
+ return generateStateCSS(themeId, variantId, "hover", `transform: translateY(-${amount}rem);`);
11846
11969
  }
11847
11970
  };
11848
11971
  var pressDownEffect = {
@@ -11851,6 +11974,7 @@ var pressDownEffect = {
11851
11974
  description: "Translates button to shadow position and removes shadow on hover",
11852
11975
  category: "transform",
11853
11976
  pseudoElement: "none",
11977
+ applicableTo: ["button"],
11854
11978
  generateCSS: (ctx) => {
11855
11979
  const { themeId, variantId, theme } = ctx;
11856
11980
  const shadowConfig = theme.shadow;
@@ -11863,12 +11987,12 @@ var pressDownEffect = {
11863
11987
  const offset = elevationOffsets[shadowConfig.elevation];
11864
11988
  const xOffset = (shadowConfig.position || "bottom") === "bottom-right" ? offset : 0;
11865
11989
  const yOffset = offset;
11866
- return `
11867
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11868
- transform: translate(${xOffset}px, ${yOffset}px);
11869
- box-shadow: none;
11870
- }
11871
- `;
11990
+ return generateStateCSS(
11991
+ themeId,
11992
+ variantId,
11993
+ "hover",
11994
+ `transform: translate(${xOffset}px, ${yOffset}px); box-shadow: none;`
11995
+ );
11872
11996
  }
11873
11997
  };
11874
11998
  var popEffect = {
@@ -11877,6 +12001,7 @@ var popEffect = {
11877
12001
  description: "Scales up and tilts the button on hover for a playful pop effect",
11878
12002
  category: "transform",
11879
12003
  pseudoElement: "none",
12004
+ applicableTo: ["button"],
11880
12005
  customizableOption: {
11881
12006
  name: "intensity",
11882
12007
  type: "select",
@@ -11893,11 +12018,12 @@ var popEffect = {
11893
12018
  dramatic: { scale: 1.08, rotate: 3 }
11894
12019
  };
11895
12020
  const config = intensityConfig[intensity] || intensityConfig.medium;
11896
- return `
11897
- :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
11898
- transform: scale(${config.scale}) rotate(${config.rotate}deg);
11899
- }
11900
- `;
12021
+ return generateStateCSS(
12022
+ themeId,
12023
+ variantId,
12024
+ "hover",
12025
+ `transform: scale(${config.scale}) rotate(${config.rotate}deg);`
12026
+ );
11901
12027
  }
11902
12028
  };
11903
12029
 
@@ -11908,6 +12034,7 @@ var auroraGlowEffect = {
11908
12034
  description: "Blurred gradient glow that appears on hover",
11909
12035
  category: "overlay",
11910
12036
  pseudoElement: "before",
12037
+ applicableTo: ["button"],
11911
12038
  customizableOption: {
11912
12039
  name: "intensity",
11913
12040
  type: "percentage",
@@ -11920,8 +12047,8 @@ var auroraGlowEffect = {
11920
12047
  let toColor;
11921
12048
  if (buttonConfig.background.type === "gradient") {
11922
12049
  const stops = buttonConfig.background.stops;
11923
- fromColor = tokens.getColor(stops[0]);
11924
- 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");
11925
12052
  } else if (buttonConfig.background.type === "solid") {
11926
12053
  fromColor = tokens.getColor(buttonConfig.background.colorToken);
11927
12054
  const lighterToken = tokens.adjustShade(buttonConfig.background.colorToken, -200);
@@ -11964,6 +12091,7 @@ var cosmicStardustEffect = {
11964
12091
  description: "Rotating rainbow gradient glow (always visible, intensifies on hover)",
11965
12092
  category: "overlay",
11966
12093
  pseudoElement: "before",
12094
+ applicableTo: ["button"],
11967
12095
  customizableOption: {
11968
12096
  name: "speed",
11969
12097
  type: "select",
@@ -12026,6 +12154,7 @@ var gradientBorderFillEffect = {
12026
12154
  description: "Gradient border that fills on hover using ::before pseudo-element",
12027
12155
  category: "overlay",
12028
12156
  pseudoElement: "before",
12157
+ applicableTo: ["button"],
12029
12158
  customizableOption: {
12030
12159
  name: "borderWidth",
12031
12160
  type: "number",
@@ -12082,6 +12211,7 @@ var animatedGradientBorderFillEffect = {
12082
12211
  description: "Rotating gradient border that fills on hover",
12083
12212
  category: "overlay",
12084
12213
  pseudoElement: "before",
12214
+ applicableTo: ["button"],
12085
12215
  customizableOption: {
12086
12216
  name: "speed",
12087
12217
  type: "select",
@@ -12157,6 +12287,7 @@ var borderBottomGrowEffect = {
12157
12287
  description: "Animated underline that grows on hover",
12158
12288
  category: "overlay",
12159
12289
  pseudoElement: "after",
12290
+ applicableTo: ["button", "nav-link"],
12160
12291
  customizableOption: {
12161
12292
  name: "height",
12162
12293
  type: "number",
@@ -12204,6 +12335,7 @@ var gradientTextEffect = {
12204
12335
  description: "Gradient text using background-clip",
12205
12336
  category: "text",
12206
12337
  pseudoElement: "none",
12338
+ applicableTo: ["button"],
12207
12339
  customizableOption: {
12208
12340
  name: "solidOnHover",
12209
12341
  type: "select",
@@ -12255,6 +12387,7 @@ var textColorChangeEffect = {
12255
12387
  description: "Changes text color on hover",
12256
12388
  category: "text",
12257
12389
  pseudoElement: "none",
12390
+ applicableTo: ["button", "nav-link"],
12258
12391
  customizableOption: {
12259
12392
  name: "hoverColorToken",
12260
12393
  type: "select",
@@ -12282,6 +12415,7 @@ var borderColorChangeEffect = {
12282
12415
  description: "Changes border color on hover",
12283
12416
  category: "border",
12284
12417
  pseudoElement: "none",
12418
+ applicableTo: ["button", "nav-link"],
12285
12419
  customizableOption: {
12286
12420
  name: "hoverColorToken",
12287
12421
  type: "select",
@@ -12307,6 +12441,7 @@ var sketchyBorderEffect = {
12307
12441
  description: "Hand-drawn style with imperfect edges",
12308
12442
  category: "border",
12309
12443
  pseudoElement: "none",
12444
+ applicableTo: ["button"],
12310
12445
  customizableOption: {
12311
12446
  name: "intensity",
12312
12447
  type: "select",
@@ -12354,6 +12489,7 @@ var pulseAnimationEffect = {
12354
12489
  description: "Continuous pulse animation (opacity fade)",
12355
12490
  category: "animation",
12356
12491
  pseudoElement: "none",
12492
+ applicableTo: ["button", "nav-link"],
12357
12493
  customizableOption: {
12358
12494
  name: "speed",
12359
12495
  type: "select",
@@ -12382,6 +12518,138 @@ var pulseAnimationEffect = {
12382
12518
  }
12383
12519
  };
12384
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
+
12385
12653
  // ../blocks/src/theme/buttons/effects/registry.ts
12386
12654
  var EFFECT_PRESETS = [
12387
12655
  // Background effects
@@ -12418,88 +12686,65 @@ var EFFECT_PRESETS = [
12418
12686
  borderColorChangeEffect,
12419
12687
  sketchyBorderEffect,
12420
12688
  // Animation effects
12421
- pulseAnimationEffect
12689
+ pulseAnimationEffect,
12690
+ // Nav-link specific effects
12691
+ navUnderlineEffect,
12692
+ navFrostedBaseEffect,
12693
+ navFrostedHoverEffect,
12694
+ navFrostedActiveEffect
12422
12695
  ];
12423
12696
  function getEffectPreset(id) {
12424
12697
  return EFFECT_PRESETS.find((preset) => preset.id === id);
12425
12698
  }
12426
12699
 
12427
- // ../blocks/src/theme/tokens/resolver.ts
12428
- var TokenResolver = class {
12429
- constructor(theme) {
12430
- this.theme = theme;
12431
- }
12432
- /**
12433
- * Resolve a color token to a CSS variable reference wrapped in rgb()
12434
- *
12435
- * @param token - Token name (e.g., 'primary-500', 'white', 'text-900')
12436
- * @returns CSS rgb() function with var reference (e.g., 'rgb(var(--tb-primary-500))')
12437
- *
12438
- * Note: CSS variables are stored as RGB channels (e.g., "37 99 235") following Tailwind convention,
12439
- * so we must wrap them with rgb() to create a valid color value.
12440
- *
12441
- * @example
12442
- * resolver.getColor('primary-500') // => 'rgb(var(--tb-primary-500))'
12443
- * resolver.getColor('white') // => 'rgb(var(--tb-white))'
12444
- */
12445
- getColor(token) {
12446
- if (!this.tokenExists(token)) {
12447
- console.warn(
12448
- `[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
12449
12722
  );
12723
+ if (css2) {
12724
+ cssChunks.push(css2);
12725
+ }
12450
12726
  }
12451
- return `rgb(var(--tb-${token}))`;
12452
12727
  }
12453
- /**
12454
- * Adjust a color token's shade by a given amount
12455
- *
12456
- * @param token - Base token (e.g., 'primary-500')
12457
- * @param amount - Shade adjustment (+100 = darker, -100 = lighter)
12458
- * @returns Adjusted token name (e.g., 'primary-600')
12459
- *
12460
- * @example
12461
- * resolver.adjustShade('primary-500', 100) // => 'primary-600' (darker)
12462
- * resolver.adjustShade('primary-500', -100) // => 'primary-400' (lighter)
12463
- */
12464
- adjustShade(token, amount) {
12465
- const parts = token.split("-");
12466
- if (parts.length < 2) {
12467
- const newShade2 = Math.max(100, Math.min(900, 500 + amount));
12468
- return `${token}-${newShade2}`;
12469
- }
12470
- const base = parts.slice(0, -1).join("-");
12471
- const shade = parseInt(parts[parts.length - 1], 10);
12472
- if (isNaN(shade)) {
12473
- console.warn(
12474
- `[TokenResolver] Cannot adjust shade for non-shade token "${token}". Returning original token.`
12475
- );
12476
- return token;
12477
- }
12478
- const newShade = Math.max(100, Math.min(900, shade + amount));
12479
- 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 "";
12480
12735
  }
12481
- /**
12482
- * Check if a token exists in the theme palette
12483
- *
12484
- * @param token - Token name to check
12485
- * @returns True if token exists in theme
12486
- */
12487
- tokenExists(token) {
12488
- const commonTokens = ["white", "black", "transparent"];
12489
- if (commonTokens.includes(token)) {
12490
- return true;
12491
- }
12492
- const parts = token.split("-");
12493
- if (parts.length >= 2) {
12494
- const lastPart = parts[parts.length - 1];
12495
- const shade = parseInt(lastPart, 10);
12496
- if (!isNaN(shade) && shade >= 100 && shade <= 900 && shade % 100 === 0) {
12497
- return true;
12498
- }
12499
- }
12500
- return true;
12501
- }
12502
- };
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
+ }
12503
12748
 
12504
12749
  // ../blocks/src/theme/buttons/constants.ts
12505
12750
  var GRADIENT_DIRECTION_MAP = {
@@ -12558,6 +12803,11 @@ var FONT_SIZE_MAP = {
12558
12803
  "text-xl": "1.25rem",
12559
12804
  "text-2xl": "1.5rem"
12560
12805
  };
12806
+ var BUTTON_PADDING_PRESETS = {
12807
+ compact: "0.375rem 0.75rem",
12808
+ default: "0.5rem 1rem",
12809
+ spacious: "0.75rem 1.5rem"
12810
+ };
12561
12811
 
12562
12812
  // ../blocks/src/theme/buttons/utils/contrast.ts
12563
12813
  function getContrastingTextColorToken(backgroundToken, theme) {
@@ -12567,7 +12817,7 @@ function getContrastingTextColorToken(backgroundToken, theme) {
12567
12817
  }
12568
12818
  if (backgroundToken.startsWith("neutral-")) {
12569
12819
  const match = backgroundToken.match(/neutral-(\d+)/);
12570
- if (match) {
12820
+ if (match?.[1]) {
12571
12821
  const shade = parseInt(match[1], 10);
12572
12822
  return shade >= 500 ? "neutral-50" : "neutral-950";
12573
12823
  }
@@ -12576,16 +12826,6 @@ function getContrastingTextColorToken(backgroundToken, theme) {
12576
12826
  }
12577
12827
 
12578
12828
  // ../blocks/src/theme/buttons/generateDefaultButtonSystem.ts
12579
- var CORNERS_TO_RADIUS_MAP = {
12580
- square: "rounded-none",
12581
- // 0px - sharp corners
12582
- soft: "rounded-md",
12583
- // 6px - subtle rounding
12584
- rounded: "rounded-md",
12585
- // 6px - standard rounded (same as soft for buttons)
12586
- pill: "rounded-full"
12587
- // 9999px - full pill shape
12588
- };
12589
12829
  var BORDER_WIDTH_TO_CLASS_MAP = {
12590
12830
  none: "border-0",
12591
12831
  // 0px
@@ -12597,7 +12837,6 @@ var BORDER_WIDTH_TO_CLASS_MAP = {
12597
12837
  // 4px - clearly thick
12598
12838
  };
12599
12839
  function generateDefaultButtonSystem(theme) {
12600
- const borderRadius = CORNERS_TO_RADIUS_MAP[theme.corners] || "rounded-lg";
12601
12840
  const borderWidthClass = BORDER_WIDTH_TO_CLASS_MAP[theme.border.width] || "border-2";
12602
12841
  const variants = [];
12603
12842
  const primaryVariant = {
@@ -12607,10 +12846,11 @@ function generateDefaultButtonSystem(theme) {
12607
12846
  priority: 1,
12608
12847
  background: {
12609
12848
  type: "solid",
12610
- colorToken: "primary-500"
12849
+ colorToken: "primary"
12611
12850
  },
12612
- textColorToken: getContrastingTextColorToken("primary-500", theme),
12613
- borderRadius,
12851
+ textColorToken: getContrastingTextColorToken("primary", theme),
12852
+ borderRadius: "",
12853
+ // Empty string = inherit from global.cornerStyle
12614
12854
  // Inherits shadow from theme
12615
12855
  effects: {
12616
12856
  hover: [
@@ -12632,7 +12872,8 @@ function generateDefaultButtonSystem(theme) {
12632
12872
  colorToken: "neutral-700"
12633
12873
  },
12634
12874
  textColorToken: getContrastingTextColorToken("neutral-700", theme),
12635
- borderRadius,
12875
+ borderRadius: "",
12876
+ // Empty string = inherit from global.cornerStyle
12636
12877
  // Inherits shadow from theme
12637
12878
  effects: {
12638
12879
  hover: [
@@ -12652,12 +12893,13 @@ function generateDefaultButtonSystem(theme) {
12652
12893
  background: {
12653
12894
  type: "transparent"
12654
12895
  },
12655
- textColorToken: "primary-600",
12656
- borderRadius,
12896
+ textColorToken: "primary",
12897
+ borderRadius: "",
12898
+ // Empty string = inherit from global.cornerStyle
12657
12899
  shadow: { elevation: "none", softness: null, position: "bottom" },
12658
12900
  // Outline buttons typically don't have shadows
12659
12901
  border: {
12660
- colorToken: "primary-500",
12902
+ colorToken: "primary",
12661
12903
  widthClass: borderWidthClass
12662
12904
  },
12663
12905
  effects: {
@@ -12678,8 +12920,9 @@ function generateDefaultButtonSystem(theme) {
12678
12920
  background: {
12679
12921
  type: "transparent"
12680
12922
  },
12681
- textColorToken: "primary-600",
12682
- borderRadius,
12923
+ textColorToken: "primary",
12924
+ borderRadius: "",
12925
+ // Empty string = inherit from global.cornerStyle
12683
12926
  shadow: { elevation: "none", softness: null, position: "bottom" },
12684
12927
  // Ghost buttons have no shadow
12685
12928
  // No border for ghost buttons
@@ -12745,6 +12988,10 @@ function generateButtonCss(options) {
12745
12988
  if (!variant.enabled) continue;
12746
12989
  const baseCSS = generateVariantBaseCSS(variant, buttonSystem.global, themeId, tokens, theme);
12747
12990
  cssChunks.push(baseCSS);
12991
+ const hoverBgCSS = generateHoverBackgroundCSS(variant, buttonSystem.global, themeId, tokens);
12992
+ if (hoverBgCSS) {
12993
+ cssChunks.push(hoverBgCSS);
12994
+ }
12748
12995
  const effectCSS = generateVariantEffectCSS(variant, themeId, tokens, theme);
12749
12996
  if (effectCSS) {
12750
12997
  cssChunks.push(effectCSS);
@@ -12763,7 +13010,7 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12763
13010
  const rules = [];
12764
13011
  const borderWidth = variant.border ? BORDER_WIDTH_MAP[variant.border.widthClass] || "1px" : "0px";
12765
13012
  const hasBorder = borderWidth !== "0px";
12766
- 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;
12767
13014
  const paddingParts = padding.split(" ");
12768
13015
  const verticalPadding = paddingParts[0] || "0.5rem";
12769
13016
  const horizontalPadding = paddingParts[1] || paddingParts[0] || "1rem";
@@ -12776,6 +13023,15 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12776
13023
  rules.push(`padding: ${padding};`);
12777
13024
  }
12778
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
+ }
12779
13035
  if (variant.fontSize) {
12780
13036
  const fontSize = FONT_SIZE_MAP[variant.fontSize] || "1rem";
12781
13037
  rules.push(`font-size: ${fontSize};`);
@@ -12789,16 +13045,16 @@ function generateVariantBaseCSS(variant, global, themeId, tokens, theme) {
12789
13045
  rules.push(`align-items: ${STRUCTURAL_BASE_STYLES.alignItems};`);
12790
13046
  rules.push(`justify-content: ${STRUCTURAL_BASE_STYLES.justifyContent};`);
12791
13047
  let borderRadius;
12792
- if (variant.borderRadius) {
12793
- 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";
12794
13050
  } else {
12795
13051
  const cornerStyleMap = {
12796
13052
  "square": "rounded-none",
12797
13053
  "rounded": "rounded-md",
12798
13054
  "pill": "rounded-full"
12799
13055
  };
12800
- const fallbackRadius = cornerStyleMap[global.cornerStyle] || "rounded-md";
12801
- 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";
12802
13058
  }
12803
13059
  rules.push(`border-radius: ${borderRadius};`);
12804
13060
  const shadowConfig = variant.shadow || shadowSizeToShadow(global.shadow, theme) || theme.shadow;
@@ -12834,31 +13090,33 @@ ${selector} {
12834
13090
  }
12835
13091
  function generateVariantEffectCSS(variant, themeId, tokens, theme) {
12836
13092
  if (!variant.effects) return "";
12837
- const cssChunks = [];
12838
- for (const [stateGroup, effectApps] of Object.entries(variant.effects)) {
12839
- if (!effectApps || effectApps.length === 0) continue;
12840
- for (const effectApp of effectApps) {
12841
- const effectPreset = getEffectPreset(effectApp.effectId);
12842
- if (!effectPreset) {
12843
- console.warn(`[generateVariantEffectCSS] Unknown effect: ${effectApp.effectId}`);
12844
- continue;
12845
- }
12846
- const ctx = {
12847
- themeId,
12848
- variantId: variant.id,
12849
- stateGroup,
12850
- options: effectApp.options || {},
12851
- buttonConfig: variant,
12852
- tokens,
12853
- theme
12854
- };
12855
- const css2 = effectPreset.generateCSS(ctx);
12856
- if (css2) {
12857
- cssChunks.push(css2);
12858
- }
12859
- }
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;
12860
13111
  }
12861
- 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();
12862
13120
  }
12863
13121
  function generateDisabledCSS(variant, themeId) {
12864
13122
  const selector = `:where([data-theme-scope="${themeId}"]) .${variant.id}:disabled`;
@@ -13751,6 +14009,345 @@ function generateDefaultInputSystem(theme) {
13751
14009
  };
13752
14010
  }
13753
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
+
13754
14351
  // ../blocks/src/theme/header/generateHeaderCss.ts
13755
14352
  function generateHeaderCss(options) {
13756
14353
  const { themeId, theme } = options;
@@ -13758,10 +14355,8 @@ function generateHeaderCss(options) {
13758
14355
  const header = theme.header;
13759
14356
  const cssChunks = [];
13760
14357
  cssChunks.push(generateHeaderRootStyles(header, themeId, tokens, theme));
13761
- if (header.logo) {
13762
- cssChunks.push(generateLogoStyles(header.logo, themeId, tokens));
13763
- }
13764
- cssChunks.push(generateNavLinkStyles(header, themeId, tokens));
14358
+ cssChunks.push(generateLogoStyles(header.logo, themeId, tokens, header.textColor));
14359
+ cssChunks.push(generateNavLinkStyles(header, themeId, tokens, theme));
13765
14360
  if (header.variant === "floating" && header.container) {
13766
14361
  cssChunks.push(generateFloatingContainerStyles(header.container, themeId, tokens));
13767
14362
  }
@@ -13817,32 +14412,36 @@ ${selector} {
13817
14412
  }
13818
14413
  `;
13819
14414
  }
13820
- function generateLogoStyles(logo, themeId, tokens) {
13821
- if (!logo) return "";
14415
+ function generateLogoStyles(logo, themeId, tokens, textColor) {
13822
14416
  const selector = `:where([data-theme-scope="${themeId}"]) .header-logo-text`;
13823
14417
  const rules = [];
13824
14418
  const primaryColor = tokens.getColor("primary");
13825
14419
  const accentColor = tokens.getColor("accent");
13826
- if (logo.fontFamily === "mono") {
13827
- rules.push(`font-family: ui-monospace, monospace;`);
13828
- } else if (logo.fontFamily === "serif") {
13829
- rules.push(`font-family: ui-serif, serif;`);
13830
- }
13831
- if (logo.letterSpacing && logo.letterSpacing !== "normal") {
13832
- const spacingMap = {
13833
- "normal": "0em",
13834
- "wide": "0.05em",
13835
- "wider": "0.1em",
13836
- "widest": "0.15em"
13837
- };
13838
- rules.push(`letter-spacing: ${spacingMap[logo.letterSpacing]};`);
13839
- }
13840
- if (logo.gradient) {
13841
- rules.push(`background: linear-gradient(to right, ${primaryColor}, ${accentColor});`);
13842
- rules.push(`-webkit-background-clip: text;`);
13843
- rules.push(`-webkit-text-fill-color: transparent;`);
13844
- rules.push(`background-clip: text;`);
13845
- 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
+ }
13846
14445
  }
13847
14446
  if (rules.length === 0) return "";
13848
14447
  return `
@@ -13851,129 +14450,82 @@ ${selector} {
13851
14450
  }
13852
14451
  `;
13853
14452
  }
13854
- function generateNavLinkStyles(header, themeId, tokens) {
13855
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
13856
- const hoverSelector = `${selector}:hover`;
13857
- const activeSelector = `${selector}[data-active="true"]`;
13858
- const cssChunks = [];
13859
- const baseRules = [];
13860
- const hoverRules = [];
13861
- const activeRules = [];
13862
- const navContainerType = header.navContainer?.type ?? "none";
13863
- const usesGlassNavContainer = navContainerType === "glass";
13864
- const navColorToken = header.navColor?.trim();
13865
- const primaryColor = tokens.getColor("primary");
13866
- const navWeight = header.navWeight ?? "medium";
13867
- const navWeightMap = {
13868
- regular: 400,
13869
- medium: 500,
13870
- semibold: 600,
13871
- bold: 700
13872
- };
13873
- if (navColorToken) {
13874
- baseRules.push(`color: ${tokens.getColor(navColorToken)};`);
13875
- }
13876
- if (navWeight) {
13877
- baseRules.push(`font-weight: ${navWeightMap[navWeight]};`);
13878
- }
13879
- if (navWeight === "bold" && header.navStyle === "solid") {
13880
- baseRules.push(`text-transform: uppercase;`);
13881
- }
13882
- switch (header.navStyle) {
13883
- case "minimal":
13884
- baseRules.push(`transition: color 200ms ease;`);
13885
- hoverRules.push(`color: ${primaryColor};`);
13886
- activeRules.push(`color: ${primaryColor};`);
13887
- break;
13888
- case "underline":
13889
- baseRules.push(`transition: all 200ms ease;`);
13890
- hoverRules.push(`text-decoration: underline;`);
13891
- hoverRules.push(`text-underline-offset: 4px;`);
13892
- activeRules.push(`text-decoration: underline;`);
13893
- activeRules.push(`text-underline-offset: 4px;`);
13894
- break;
13895
- case "underline-grow":
13896
- baseRules.push(`position: relative;`);
13897
- baseRules.push(`transition: color 200ms ease;`);
13898
- hoverRules.push(`color: ${primaryColor};`);
13899
- activeRules.push(`color: ${primaryColor};`);
13900
- cssChunks.push(generateUnderlineGrowCSS(themeId));
13901
- break;
13902
- case "capsule":
13903
- baseRules.push(`border-radius: 9999px;`);
13904
- baseRules.push(`transition: background-color 200ms ease;`);
13905
- hoverRules.push(`background-color: color-mix(in srgb, ${primaryColor} 10%, transparent);`);
13906
- activeRules.push(`background-color: color-mix(in srgb, ${primaryColor} 15%, transparent);`);
13907
- break;
13908
- case "solid":
13909
- baseRules.push(`border-radius: 0;`);
13910
- baseRules.push(`transition: background-color 200ms ease, color 200ms ease;`);
13911
- hoverRules.push(`background-color: ${primaryColor};`);
13912
- if (navColorToken) {
13913
- hoverRules.push(`color: ${tokens.getColor(navColorToken)};`);
13914
- }
13915
- activeRules.push(`background-color: ${primaryColor};`);
13916
- if (navColorToken) {
13917
- activeRules.push(`color: ${tokens.getColor(navColorToken)};`);
13918
- }
13919
- break;
13920
- case "scale":
13921
- baseRules.push(`transition: all 200ms ease;`);
13922
- hoverRules.push(`transform: scale(1.05);`);
13923
- break;
13924
- case "frosted":
13925
- baseRules.push(`border-radius: 0.5rem;`);
13926
- baseRules.push(`background-color: rgb(255 255 255 / 0.1);`);
13927
- baseRules.push(`backdrop-filter: blur(8px);`);
13928
- baseRules.push(`transition: background-color 200ms ease;`);
13929
- hoverRules.push(`background-color: rgb(255 255 255 / 0.2);`);
13930
- activeRules.push(`background-color: rgb(255 255 255 / 0.25);`);
13931
- 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
+ });
13932
14462
  }
13933
- if (usesGlassNavContainer && header.navStyle === "minimal") {
13934
- baseRules.push(`border-radius: 9999px;`);
13935
- hoverRules.push(`background-color: rgba(255, 255, 255, 0.1);`);
13936
- activeRules.push(`background-color: rgba(255, 255, 255, 0.18);`);
13937
- hoverRules.push(`color: #fff;`);
13938
- 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";
13939
14477
  }
13940
14478
  if (header.navEffects) {
14479
+ mergedStyle.effects = {
14480
+ ...mergedStyle.effects,
14481
+ hover: [...mergedStyle.effects?.hover || []],
14482
+ active: [...mergedStyle.effects?.active || []]
14483
+ };
13941
14484
  if (header.navEffects.glow) {
13942
- hoverRules.push(`filter: drop-shadow(0 0 5px currentColor);`);
14485
+ mergedStyle.effects.hover.push({
14486
+ effectId: "outer-glow",
14487
+ options: { spread: 5 }
14488
+ });
13943
14489
  }
13944
14490
  if (header.navEffects.neumorphic) {
13945
- console.log("[generateHeaderCss] Generating neumorphic styles for theme:", themeId);
13946
- baseRules.push(`transition: all 200ms ease;`);
13947
- baseRules.push(`border-radius: 0.5rem;`);
13948
- hoverRules.push(`box-shadow: inset 4px 4px 8px #c7c7c7, inset -4px -4px 8px #ffffff;`);
13949
- 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
+ });
13950
14499
  }
13951
14500
  if (header.navEffects.underlineGradient) {
13952
- cssChunks.push(generateGradientUnderlineCSS(themeId, tokens));
14501
+ mergedStyle.effects.hover.push({
14502
+ effectId: "border-bottom-grow",
14503
+ options: { origin: "center" }
14504
+ });
13953
14505
  }
13954
14506
  }
13955
- if (baseRules.length > 0) {
13956
- cssChunks.push(`
13957
- ${selector} {
13958
- ${baseRules.join("\n ")}
13959
- }
13960
- `);
13961
- }
13962
- if (hoverRules.length > 0) {
13963
- cssChunks.push(`
13964
- ${hoverSelector} {
13965
- ${hoverRules.join("\n ")}
13966
- }
13967
- `);
13968
- }
13969
- if (activeRules.length > 0) {
13970
- cssChunks.push(`
13971
- ${activeSelector} {
13972
- ${activeRules.join("\n ")}
13973
- }
13974
- `);
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
+ };
13975
14521
  }
13976
- return cssChunks.join("");
14522
+ return generateNavLinkCSS({
14523
+ themeId,
14524
+ tokens,
14525
+ theme,
14526
+ style: mergedStyle,
14527
+ className: "header-nav-link"
14528
+ });
13977
14529
  }
13978
14530
  function generateNavContainerStyles(header, themeId, tokens) {
13979
14531
  const navContainer = header.navContainer;
@@ -14010,52 +14562,6 @@ function generateNavContainerStyles(header, themeId, tokens) {
14010
14562
  }
14011
14563
  return "";
14012
14564
  }
14013
- function generateGradientUnderlineCSS(themeId, tokens) {
14014
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
14015
- const primaryColor = tokens.getColor("primary");
14016
- const accentColor = tokens.getColor("accent");
14017
- return `
14018
- ${selector}::after {
14019
- content: '';
14020
- position: absolute;
14021
- bottom: 0;
14022
- left: 0;
14023
- width: 100%;
14024
- height: 2px;
14025
- background: linear-gradient(to right, ${primaryColor}, ${accentColor});
14026
- transform: scaleX(0);
14027
- transform-origin: center;
14028
- transition: transform 0.3s ease;
14029
- }
14030
-
14031
- ${selector}:hover::after,
14032
- ${selector}[data-active="true"]::after {
14033
- transform: scaleX(1);
14034
- }
14035
- `.trim();
14036
- }
14037
- function generateUnderlineGrowCSS(themeId) {
14038
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-link`;
14039
- return `
14040
- ${selector}::after {
14041
- content: '';
14042
- position: absolute;
14043
- bottom: 0;
14044
- left: 0;
14045
- width: 100%;
14046
- height: 2px;
14047
- background: currentColor;
14048
- transform: scaleX(0);
14049
- transform-origin: center;
14050
- transition: transform 0.3s ease;
14051
- }
14052
-
14053
- ${selector}:hover::after,
14054
- ${selector}[data-active="true"]::after {
14055
- transform: scaleX(1);
14056
- }
14057
- `.trim();
14058
- }
14059
14565
  function generateFloatingContainerStyles(container, themeId, tokens) {
14060
14566
  if (!container) return "";
14061
14567
  const selector = `:where([data-theme-scope="${themeId}"]) .header-floating-container`;
@@ -14112,12 +14618,52 @@ function applyAlpha(color, opacity) {
14112
14618
  const clamped = clampOpacity(opacity, 1);
14113
14619
  if (color.startsWith("rgb(") && color.endsWith(")")) {
14114
14620
  const inner = color.slice(4, -1).trim();
14115
- const channels = inner.includes("/") ? inner.split("/")[0].trim() : inner;
14621
+ const channels = inner.includes("/") ? (inner.split("/")[0] ?? inner).trim() : inner;
14116
14622
  return `rgb(${channels} / ${clamped})`;
14117
14623
  }
14118
14624
  return color;
14119
14625
  }
14120
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
+
14121
14667
  // ../blocks/src/theme/layout/generateLayoutCss.ts
14122
14668
  function generateLayoutCss(options) {
14123
14669
  const { themeId } = options;
@@ -14271,6 +14817,105 @@ ${root} .step-connector {
14271
14817
  return cssBlocks.join("\n\n");
14272
14818
  }
14273
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
+
14274
14919
  // ../blocks/src/theme/runtime/buildThemeRuntime.ts
14275
14920
  function buildThemeRuntime(theme, options) {
14276
14921
  const hydrated = hydrateTheme(theme);
@@ -14285,6 +14930,10 @@ function buildThemeRuntime(theme, options) {
14285
14930
  themeId,
14286
14931
  theme
14287
14932
  });
14933
+ const footerCss = generateFooterCss({
14934
+ themeId,
14935
+ theme
14936
+ });
14288
14937
  const cardCss = generateCardCss({
14289
14938
  themeId,
14290
14939
  theme,
@@ -14314,8 +14963,12 @@ function buildThemeRuntime(theme, options) {
14314
14963
  themeId,
14315
14964
  theme
14316
14965
  });
14966
+ const typographyCss = generateTypographyCss({
14967
+ themeId,
14968
+ theme
14969
+ });
14317
14970
  const tokens = buildThemeTokens(hydrated, theme);
14318
- 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 };
14319
14972
  }
14320
14973
  function hydrateTheme(theme) {
14321
14974
  return {
@@ -14345,17 +14998,39 @@ function buildThemeCssVars(theme) {
14345
14998
  ...typography.headings.h1.letterSpacing ? { ["--ls-h1"]: mapLetterSpacing(typography.headings.h1.letterSpacing) } : {},
14346
14999
  ...typography.headings.h2.letterSpacing ? { ["--ls-h2"]: mapLetterSpacing(typography.headings.h2.letterSpacing) } : {},
14347
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) } : {},
14348
15004
  ["--lh-heading"]: mapLineHeight(typography.headings.default.lineHeight),
14349
15005
  ["--lh-body"]: mapLineHeight(typography.body.lineHeight),
14350
15006
  ...typography.headings.h1.lineHeight ? { ["--lh-h1"]: mapLineHeight(typography.headings.h1.lineHeight) } : {},
14351
15007
  ...typography.headings.h2.lineHeight ? { ["--lh-h2"]: mapLineHeight(typography.headings.h2.lineHeight) } : {},
14352
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) } : {},
14353
15012
  ["--tt-heading"]: mapTextTransform(typography.headings.default.case),
14354
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)
14355
15023
  ["--fs-body"]: sized.body,
14356
15024
  ["--fs-h3"]: sized.h3,
14357
15025
  ["--fs-h2"]: sized.h2,
14358
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,
14359
15034
  ["--shadow-elev"]: mapShadow(shadow.elevation, shadow.softness, shadow.position || "bottom"),
14360
15035
  ["--space-mult"]: mapSpaceMult(space),
14361
15036
  ["--motion-duration"]: mapMotionDuration(motion.level),
@@ -14461,33 +15136,81 @@ function mapMotionEasing(e) {
14461
15136
  gentle: "cubic-bezier(.15,.85,.15,1)"
14462
15137
  }[e];
14463
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
+ }
14464
15150
  function computeTypeScale(scale, bodySize) {
14465
15151
  const base = { md: 16, lg: 17, xl: 18 }[bodySize];
14466
- 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];
14467
15154
  const h3 = Math.round(base * ratio);
14468
15155
  const h2 = Math.round(h3 * ratio);
14469
15156
  const h1 = Math.round(h2 * ratio);
15157
+ const fluidMin = 0.7;
15158
+ const fluidMax = 1.3;
14470
15159
  return {
15160
+ // Static sizes (fallback)
14471
15161
  ["--fs-body"]: `${base}px`,
14472
15162
  ["--fs-h3"]: `${h3}px`,
14473
15163
  ["--fs-h2"]: `${h2}px`,
14474
- ["--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)`
14475
15179
  };
14476
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
+ };
14477
15189
  function applyHeadingSizeOverrides(base, typography) {
14478
15190
  const body = base["--fs-body"];
14479
15191
  const h1Base = parseFloat(base["--fs-h1"].replace("px", ""));
14480
15192
  const h2Base = parseFloat(base["--fs-h2"].replace("px", ""));
14481
15193
  const h3Base = parseFloat(base["--fs-h3"].replace("px", ""));
14482
- const scaleMap = { md: 1, lg: 1.1, xl: 1.2 };
14483
- const h1Scale = typography.headings.h1.size ? scaleMap[typography.headings.h1.size] : 1;
14484
- const h2Scale = typography.headings.h2.size ? scaleMap[typography.headings.h2.size] : 1;
14485
- 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;
14486
15202
  return {
14487
15203
  body,
14488
- h1: `${Math.round(h1Base * h1Scale)}px`,
14489
- h2: `${Math.round(h2Base * h2Scale)}px`,
14490
- 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)`
14491
15214
  };
14492
15215
  }
14493
15216
  function mapLetterSpacing(s) {
@@ -14504,6 +15227,9 @@ function mapFontVariant(c) {
14504
15227
  if (c === "smallCaps") return "small-caps";
14505
15228
  return "normal";
14506
15229
  }
15230
+ function mapFontStyle(italic) {
15231
+ return italic ? "italic" : "normal";
15232
+ }
14507
15233
 
14508
15234
  // ../blocks/src/system/manifest/hydrateLinks.ts
14509
15235
  function hydrateManifestLinks(manifest, content, routes) {
@@ -16371,6 +17097,15 @@ var ENDPOINT_DEFINITIONS = {
16371
17097
  auth: "public",
16372
17098
  responseKind: "json"
16373
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
+ },
16374
17109
  // Public event registration
16375
17110
  registerForEvent: {
16376
17111
  path: "/public/sites/{siteId}/events/register",
@@ -16545,6 +17280,20 @@ if (typeof window === "undefined") {
16545
17280
  revalidateTag = null;
16546
17281
  }
16547
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
+ }
16548
17297
  var ApiRequestError = class extends Error {
16549
17298
  constructor(message, options) {
16550
17299
  super(message);
@@ -16556,6 +17305,7 @@ var ApiRequestError = class extends Error {
16556
17305
  this.requestId = options.requestId;
16557
17306
  this.body = options.body;
16558
17307
  this.cause = options.cause;
17308
+ this.errorCode = options.errorCode;
16559
17309
  }
16560
17310
  };
16561
17311
  function buildEndpointURL(baseURL, endpoint) {
@@ -16592,6 +17342,29 @@ async function parseErrorBody(response) {
16592
17342
  return null;
16593
17343
  }
16594
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
+ }
16595
17368
  async function parseSuccessResponse(endpoint, response, config) {
16596
17369
  const responseKind = config.responseKind ?? "json";
16597
17370
  const auth = config.auth ?? "user";
@@ -16599,14 +17372,15 @@ async function parseSuccessResponse(endpoint, response, config) {
16599
17372
  switch (responseKind) {
16600
17373
  case "json": {
16601
17374
  if (response.status === 204 || response.status === 205 || response.status === 304) {
16602
- return void 0;
17375
+ return buildSuccessEnvelope(void 0, requestId);
16603
17376
  }
16604
17377
  const raw = await response.text();
16605
17378
  if (!raw.trim()) {
16606
- return void 0;
17379
+ return buildSuccessEnvelope(void 0, requestId);
16607
17380
  }
17381
+ let parsed;
16608
17382
  try {
16609
- return JSON.parse(raw);
17383
+ parsed = JSON.parse(raw);
16610
17384
  } catch (cause) {
16611
17385
  throw new ApiRequestError(
16612
17386
  `Failed to parse JSON response for endpoint ${String(endpoint)}`,
@@ -16621,32 +17395,33 @@ async function parseSuccessResponse(endpoint, response, config) {
16621
17395
  }
16622
17396
  );
16623
17397
  }
17398
+ if (parsed && typeof parsed === "object" && "success" in parsed && typeof parsed.success === "boolean") {
17399
+ return parsed;
17400
+ }
17401
+ return buildSuccessEnvelope(parsed, requestId);
16624
17402
  }
16625
17403
  case "text": {
16626
- return await response.text();
17404
+ const text2 = await response.text();
17405
+ return buildSuccessEnvelope(text2, requestId);
16627
17406
  }
16628
17407
  case "stream": {
16629
17408
  const body = response.body;
16630
17409
  if (!body) {
16631
- throw new ApiRequestError(
17410
+ return buildErrorEnvelope(
17411
+ "server:internal_error",
16632
17412
  `Expected a streamed body for endpoint ${String(endpoint)}`,
16633
- {
16634
- endpoint,
16635
- status: response.status,
16636
- method: config.method,
16637
- auth,
16638
- requestId
16639
- }
17413
+ response.status,
17414
+ requestId
16640
17415
  );
16641
17416
  }
16642
17417
  const stream = body;
16643
- return stream;
17418
+ return buildSuccessEnvelope(stream, requestId);
16644
17419
  }
16645
17420
  case "void": {
16646
- return void 0;
17421
+ return buildSuccessEnvelope(void 0, requestId);
16647
17422
  }
16648
17423
  default: {
16649
- return void 0;
17424
+ return buildSuccessEnvelope(void 0, requestId);
16650
17425
  }
16651
17426
  }
16652
17427
  }
@@ -16712,11 +17487,15 @@ function createRawCMSClient(headers = {}, baseUrl) {
16712
17487
  const requestInit = {
16713
17488
  method,
16714
17489
  ...options,
17490
+ // Include credentials for same-origin requests (sends cookies for auth)
17491
+ credentials: "same-origin",
16715
17492
  // Don't include body for GET/HEAD requests
16716
17493
  body: isGetOrHead ? void 0 : isFormData ? body : body ? JSON.stringify(body) : void 0,
16717
17494
  headers: {
16718
17495
  ...options.headers,
16719
17496
  ...headers,
17497
+ // Include SDK version if set
17498
+ ...sdkVersion && { "x-sdk-version": sdkVersion },
16720
17499
  // Don't set Content-Type for GET/HEAD requests without body
16721
17500
  ...isGetOrHead ? {} : isFormData ? {} : { "Content-Type": "application/json" }
16722
17501
  },
@@ -16770,94 +17549,30 @@ function createBearerAPIClient(token, baseUrl) {
16770
17549
  return createCMSClient(authHeaders, baseUrl);
16771
17550
  }
16772
17551
 
16773
- // ../api/src/aiPlayground.ts
16774
- import { z as z18 } from "zod";
16775
- var Rfc6902PatchOp = z18.discriminatedUnion("op", [
16776
- // Standard RFC-6902 operations
16777
- z18.object({
16778
- op: z18.literal("add"),
16779
- path: z18.string(),
16780
- value: z18.unknown()
16781
- }),
16782
- z18.object({
16783
- op: z18.literal("remove"),
16784
- path: z18.string()
16785
- }),
16786
- z18.object({
16787
- op: z18.literal("replace"),
16788
- path: z18.string(),
16789
- value: z18.unknown()
16790
- }),
16791
- z18.object({
16792
- op: z18.literal("move"),
16793
- from: z18.string(),
16794
- path: z18.string()
16795
- }),
16796
- z18.object({
16797
- op: z18.literal("copy"),
16798
- from: z18.string(),
16799
- path: z18.string()
16800
- }),
16801
- // Block-level operations (Phase 2)
16802
- z18.object({
16803
- op: z18.literal("add_block"),
16804
- blockKind: z18.string(),
16805
- afterBlockId: z18.string().nullable(),
16806
- content: z18.record(z18.string(), z18.unknown()),
16807
- rationale: z18.string()
16808
- }),
16809
- z18.object({
16810
- op: z18.literal("delete_block"),
16811
- blockId: z18.string(),
16812
- rationale: z18.string()
16813
- }),
16814
- z18.object({
16815
- op: z18.literal("reorder_block"),
16816
- blockId: z18.string(),
16817
- afterBlockId: z18.string().nullable(),
16818
- rationale: z18.string()
16819
- })
16820
- ]);
16821
- var PatchEnvelope = z18.object({
16822
- blockId: z18.string(),
16823
- blockKind: z18.string().optional(),
16824
- blockPurpose: z18.string().optional().nullable(),
16825
- ops: z18.array(Rfc6902PatchOp),
16826
- rationale: z18.string(),
16827
- currentContent: z18.record(z18.string(), z18.unknown()).optional()
16828
- });
16829
- var ContentUpdateResponse = z18.object({
16830
- patches: z18.array(PatchEnvelope),
16831
- assistantMessage: z18.string()
16832
- });
16833
- var PlaygroundProposeRequest = z18.object({
16834
- request: z18.string().min(1).max(2e3)
16835
- });
16836
- var PlaygroundProposeResponse = z18.object({
16837
- patches: z18.array(PatchEnvelope),
16838
- assistantMessage: z18.string(),
16839
- validation: z18.object({
16840
- valid: z18.boolean(),
16841
- issues: z18.array(z18.string()),
16842
- filtered: z18.number()
16843
- })
16844
- });
16845
- var MultiPagePatchEnvelope = z18.object({
16846
- pageId: z18.string(),
16847
- blockId: z18.string().optional(),
16848
- // Not present for page-level ops
16849
- blockKind: z18.string().optional(),
16850
- blockPurpose: z18.string().optional().nullable(),
16851
- ops: z18.array(Rfc6902PatchOp),
16852
- rationale: z18.string(),
16853
- currentContent: z18.record(z18.string(), z18.unknown()).optional()
16854
- });
16855
- var MultiPageUpdateResponse = z18.object({
16856
- patches: z18.array(MultiPagePatchEnvelope),
16857
- assistantMessage: z18.string(),
16858
- pagesModified: z18.number(),
16859
- toolCallsUsed: z18.number()
16860
- });
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
+ }
16861
17576
 
16862
17577
  // src/client/cache.ts
16863
17578
  var SimpleCache = class {
@@ -16895,7 +17610,100 @@ var SimpleCache = class {
16895
17610
  }
16896
17611
  };
16897
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
+
16898
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
+ }
16899
17707
  function createRiverbankClient(config) {
16900
17708
  if (!config.baseUrl) {
16901
17709
  throw new Error(
@@ -16922,7 +17730,13 @@ function createRiverbankClient(config) {
16922
17730
  return cached;
16923
17731
  }
16924
17732
  }
16925
- 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
+ }
16926
17740
  if (cacheEnabled) {
16927
17741
  cache.set(cacheKey, data);
16928
17742
  }
@@ -16957,33 +17771,27 @@ function createRiverbankClient(config) {
16957
17771
  const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
16958
17772
  const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
16959
17773
  return cachedFetch(cacheKey, async () => {
16960
- const apiParams = {
16961
- siteId,
16962
- type: contentType
16963
- };
16964
- if (typeof limit === "number") {
16965
- apiParams.limit = String(limit);
16966
- }
16967
- if (typeof offset === "number") {
16968
- apiParams.offset = String(offset);
16969
- }
16970
- if (includeMeta) {
16971
- apiParams.meta = "true";
16972
- }
17774
+ let orderParam;
16973
17775
  if (order === "newest") {
16974
- apiParams.order = "published_at.desc";
17776
+ orderParam = "published_at.desc";
16975
17777
  } else if (order === "oldest") {
16976
- apiParams.order = "published_at.asc";
17778
+ orderParam = "published_at.asc";
16977
17779
  } else if (order === "title") {
16978
- apiParams.order = "title.asc";
16979
- }
16980
- if (preview) {
16981
- apiParams.stage = "preview";
16982
- }
16983
- if (mode === "manual" && entryIds?.length) {
16984
- apiParams.mode = "manual";
16985
- apiParams.entryIds = JSON.stringify(entryIds);
17780
+ orderParam = "title.asc";
16986
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
+ };
16987
17795
  return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
16988
17796
  });
16989
17797
  },
@@ -17011,8 +17819,10 @@ function createRiverbankClient(config) {
17011
17819
  }
17012
17820
  const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
17013
17821
  return cachedFetch(cacheKey, async () => {
17014
- const apiParams = { siteId };
17015
- if (ids) apiParams.ids = ids;
17822
+ const apiParams = {
17823
+ siteId,
17824
+ ...ids && { ids }
17825
+ };
17016
17826
  return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
17017
17827
  });
17018
17828
  },
@@ -17023,14 +17833,42 @@ function createRiverbankClient(config) {
17023
17833
  }
17024
17834
  const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
17025
17835
  return cachedFetch(cacheKey, async () => {
17026
- const apiParams = { siteId };
17027
- if (typeof limit === "number") apiParams.limit = String(limit);
17028
- if (from) apiParams.from = from;
17029
- if (to) apiParams.to = to;
17030
- 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
+ };
17031
17843
  return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
17032
17844
  });
17033
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
+ },
17034
17872
  clearCache() {
17035
17873
  cache.clear();
17036
17874
  }