@riverbankcms/sdk 0.4.3 → 0.5.1

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 (178) hide show
  1. package/README.md +84 -0
  2. package/dist/cli/index.js +3463 -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 +1379 -519
  15. package/dist/client/client.js.map +1 -1
  16. package/dist/client/client.mjs +1379 -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-BXjk8BhD.d.mts +6704 -0
  30. package/dist/client/usePage-BafOS9UT.d.mts +6512 -0
  31. package/dist/client/usePage-BiOReg0_.d.ts +6704 -0
  32. package/dist/client/usePage-Bnx-kA6x.d.mts +6670 -0
  33. package/dist/client/usePage-DoPI6b8V.d.ts +6511 -0
  34. package/dist/client/usePage-QNWArrVO.d.ts +6670 -0
  35. package/dist/client/usePage-fBgPB6Oq.d.ts +6512 -0
  36. package/dist/server/{Layout-CXI_VkhN.d.ts → Layout-BClXUTsd.d.mts} +4 -4
  37. package/dist/server/{Layout-p6f3TLw9.d.mts → Layout-UXGjXv8M.d.ts} +4 -4
  38. package/dist/server/{chunk-6JBKKV3G.js → chunk-2KCF2DNK.js} +30 -10
  39. package/dist/server/chunk-2KCF2DNK.js.map +1 -0
  40. package/dist/server/chunk-5STV4MWD.js +189 -0
  41. package/dist/server/chunk-5STV4MWD.js.map +1 -0
  42. package/dist/server/{chunk-VHDDXCK6.js → chunk-7UPVCT3K.js} +1206 -496
  43. package/dist/server/chunk-7UPVCT3K.js.map +1 -0
  44. package/dist/server/{chunk-7DS4Q3GA.mjs → chunk-AEFWG657.mjs} +3 -3
  45. package/dist/server/chunk-AEFWG657.mjs.map +1 -0
  46. package/dist/server/{chunk-USQF2XTU.mjs → chunk-BYBJA6SP.mjs} +26 -11
  47. package/dist/server/chunk-BYBJA6SP.mjs.map +1 -0
  48. package/dist/server/{chunk-ES6QDZUX.mjs → chunk-C6FIJC7T.mjs} +2 -2
  49. package/dist/server/{chunk-N3PX76AP.mjs → chunk-CMABGYGI.mjs} +269 -135
  50. package/dist/server/chunk-CMABGYGI.mjs.map +1 -0
  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-R5B6IOFQ.js → chunk-KA74YRK6.js} +269 -135
  54. package/dist/server/chunk-KA74YRK6.js.map +1 -0
  55. package/dist/server/chunk-KFLZGNPO.mjs +189 -0
  56. package/dist/server/chunk-KFLZGNPO.mjs.map +1 -0
  57. package/dist/server/chunk-L5EA4FXU.mjs +134 -0
  58. package/dist/server/chunk-L5EA4FXU.mjs.map +1 -0
  59. package/dist/server/{chunk-U2NI3TS3.mjs → chunk-LNOUXALA.mjs} +1135 -425
  60. package/dist/server/chunk-LNOUXALA.mjs.map +1 -0
  61. package/dist/server/{chunk-24F6FTCI.mjs → chunk-OSF34JTQ.mjs} +4 -4
  62. package/dist/server/{chunk-G35R7N7B.js → chunk-P3NNN73G.js} +3 -3
  63. package/dist/server/{chunk-G35R7N7B.js.map → chunk-P3NNN73G.js.map} +1 -1
  64. package/dist/server/{chunk-I6K5REFT.mjs → chunk-P4K63SBZ.mjs} +24 -4
  65. package/dist/server/chunk-P4K63SBZ.mjs.map +1 -0
  66. package/dist/server/{chunk-HOY77YBF.js → chunk-RVDS7VSP.js} +5 -5
  67. package/dist/server/chunk-RVDS7VSP.js.map +1 -0
  68. package/dist/server/{chunk-2SSEBAHC.js → chunk-TT5JWA4X.js} +9 -9
  69. package/dist/server/{chunk-2SSEBAHC.js.map → chunk-TT5JWA4X.js.map} +1 -1
  70. package/dist/server/chunk-VSFQRHYZ.js +134 -0
  71. package/dist/server/chunk-VSFQRHYZ.js.map +1 -0
  72. package/dist/server/{chunk-EGTDJ4PL.js → chunk-YYO3RIFO.js} +26 -11
  73. package/dist/server/chunk-YYO3RIFO.js.map +1 -0
  74. package/dist/server/{chunk-OP2GHK27.mjs → chunk-Z5ZA6Q4D.mjs} +2 -2
  75. package/dist/server/{components-Dhiemsjd.d.ts → components-BmaJxgCV.d.mts} +20 -75
  76. package/dist/server/{components-C75e4poV.d.mts → components-DppHY5oD.d.ts} +20 -75
  77. package/dist/server/components.d.mts +11 -8
  78. package/dist/server/components.d.ts +11 -8
  79. package/dist/server/components.js +5 -4
  80. package/dist/server/components.js.map +1 -1
  81. package/dist/server/components.mjs +4 -3
  82. package/dist/server/config-validation.d.mts +3 -3
  83. package/dist/server/config-validation.d.ts +3 -3
  84. package/dist/server/config-validation.js +9 -5
  85. package/dist/server/config-validation.js.map +1 -1
  86. package/dist/server/config-validation.mjs +8 -4
  87. package/dist/server/config.d.mts +243 -5
  88. package/dist/server/config.d.ts +243 -5
  89. package/dist/server/config.js +72 -5
  90. package/dist/server/config.js.map +1 -1
  91. package/dist/server/config.mjs +72 -5
  92. package/dist/server/config.mjs.map +1 -1
  93. package/dist/server/core-DsNWrl3o.d.mts +44 -0
  94. package/dist/server/core-DsNWrl3o.d.ts +44 -0
  95. package/dist/server/data.d.mts +4 -3
  96. package/dist/server/data.d.ts +4 -3
  97. package/dist/server/data.js +3 -3
  98. package/dist/server/data.mjs +2 -2
  99. package/dist/server/{index-CAwBj3-A.d.ts → index-Bucs6UqG.d.mts} +2 -1
  100. package/dist/server/{index-C6o9LPvq.d.mts → index-Cp7tJuRt.d.ts} +2 -1
  101. package/dist/server/index.d.mts +84 -6
  102. package/dist/server/index.d.ts +84 -6
  103. package/dist/server/index.js +91 -2
  104. package/dist/server/index.js.map +1 -1
  105. package/dist/server/index.mjs +90 -1
  106. package/dist/server/index.mjs.map +1 -1
  107. package/dist/server/link-DjxLyC82.d.mts +23 -0
  108. package/dist/server/link-DjxLyC82.d.ts +23 -0
  109. package/dist/server/{loadContent-CdXfuCuE.d.mts → loadContent-BS-3wesN.d.mts} +4 -4
  110. package/dist/server/{loadContent-CsvQRoxb.d.ts → loadContent-Buvmudee.d.ts} +4 -4
  111. package/dist/server/{loadPage-p3AWwwrd.d.mts → loadPage-B8mQUUSo.d.mts} +5 -46
  112. package/dist/server/loadPage-DNQTTRHL.mjs +11 -0
  113. package/dist/server/{loadPage-BA0HiT-6.d.ts → loadPage-DP3nrHBi.d.ts} +5 -46
  114. package/dist/server/loadPage-IDGVDFBB.js +11 -0
  115. package/dist/server/{loadPage-DLC7DJZP.js.map → loadPage-IDGVDFBB.js.map} +1 -1
  116. package/dist/server/metadata.d.mts +6 -4
  117. package/dist/server/metadata.d.ts +6 -4
  118. package/dist/server/navigation.d.mts +199 -29
  119. package/dist/server/navigation.d.ts +199 -29
  120. package/dist/server/navigation.js +27 -43
  121. package/dist/server/navigation.js.map +1 -1
  122. package/dist/server/navigation.mjs +20 -36
  123. package/dist/server/navigation.mjs.map +1 -1
  124. package/dist/server/rendering/server.d.mts +8 -6
  125. package/dist/server/rendering/server.d.ts +8 -6
  126. package/dist/server/rendering/server.js +7 -6
  127. package/dist/server/rendering/server.js.map +1 -1
  128. package/dist/server/rendering/server.mjs +6 -5
  129. package/dist/server/rendering.d.mts +14 -10
  130. package/dist/server/rendering.d.ts +14 -10
  131. package/dist/server/rendering.js +9 -8
  132. package/dist/server/rendering.js.map +1 -1
  133. package/dist/server/rendering.mjs +8 -7
  134. package/dist/server/richTextSchema-DURiozvD.d.mts +62 -0
  135. package/dist/server/richTextSchema-DURiozvD.d.ts +62 -0
  136. package/dist/server/routing.d.mts +178 -11
  137. package/dist/server/routing.d.ts +178 -11
  138. package/dist/server/routing.js +95 -2
  139. package/dist/server/routing.js.map +1 -1
  140. package/dist/server/routing.mjs +94 -1
  141. package/dist/server/routing.mjs.map +1 -1
  142. package/dist/server/{schema-Bpy9N5ZI.d.mts → schema-Z6-afHJG.d.mts} +1 -1
  143. package/dist/server/{schema-Bpy9N5ZI.d.ts → schema-Z6-afHJG.d.ts} +1 -1
  144. package/dist/server/server.d.mts +9 -7
  145. package/dist/server/server.d.ts +9 -7
  146. package/dist/server/server.js +6 -6
  147. package/dist/server/server.mjs +5 -5
  148. package/dist/server/theme-bridge.js +8 -8
  149. package/dist/server/theme-bridge.mjs +2 -2
  150. package/dist/server/{types-Dj8B3QRb.d.ts → types-1cLz0vnq.d.mts} +55 -2
  151. package/dist/server/{types-txWsSxN7.d.mts → types-BjgZt8xJ.d.mts} +63 -2
  152. package/dist/server/{types-CdhKJrB0.d.mts → types-BvcJU7zk.d.ts} +55 -2
  153. package/dist/server/{types-BWQ-TohG.d.ts → types-CVykEqXN.d.ts} +289 -83
  154. package/dist/server/{types-CL916r6x.d.ts → types-DLBhEPSt.d.ts} +63 -2
  155. package/dist/server/{types-BLf-hE50.d.mts → types-Dsu9wsUh.d.mts} +289 -83
  156. package/dist/server/{validation-DzvDwwRo.d.mts → validation-BGuRo8P1.d.mts} +18 -5
  157. package/dist/server/{validation-CoU8uAiu.d.ts → validation-DU2YE7u5.d.ts} +18 -5
  158. package/package.json +5 -1
  159. package/dist/server/chunk-6JBKKV3G.js.map +0 -1
  160. package/dist/server/chunk-7DS4Q3GA.mjs.map +0 -1
  161. package/dist/server/chunk-EGTDJ4PL.js.map +0 -1
  162. package/dist/server/chunk-HOY77YBF.js.map +0 -1
  163. package/dist/server/chunk-I6K5REFT.mjs.map +0 -1
  164. package/dist/server/chunk-LCYGQDAB.mjs +0 -835
  165. package/dist/server/chunk-LCYGQDAB.mjs.map +0 -1
  166. package/dist/server/chunk-N3PX76AP.mjs.map +0 -1
  167. package/dist/server/chunk-R5B6IOFQ.js.map +0 -1
  168. package/dist/server/chunk-TNYU5EIO.js +0 -835
  169. package/dist/server/chunk-TNYU5EIO.js.map +0 -1
  170. package/dist/server/chunk-U2NI3TS3.mjs.map +0 -1
  171. package/dist/server/chunk-USQF2XTU.mjs.map +0 -1
  172. package/dist/server/chunk-VHDDXCK6.js.map +0 -1
  173. package/dist/server/loadPage-DLC7DJZP.js +0 -11
  174. package/dist/server/loadPage-GEGN4UAL.mjs +0 -11
  175. /package/dist/server/{chunk-ES6QDZUX.mjs.map → chunk-C6FIJC7T.mjs.map} +0 -0
  176. /package/dist/server/{chunk-24F6FTCI.mjs.map → chunk-OSF34JTQ.mjs.map} +0 -0
  177. /package/dist/server/{chunk-OP2GHK27.mjs.map → chunk-Z5ZA6Q4D.mjs.map} +0 -0
  178. /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) {
@@ -15096,6 +15822,28 @@ var ENDPOINT_DEFINITIONS = {
15096
15822
  tags: ["site-{siteId}", "api-keys", "access-logs"],
15097
15823
  responseKind: "json"
15098
15824
  },
15825
+ // Management API Keys (SDK write operations)
15826
+ listManagementKeys: {
15827
+ path: "/sites/{siteId}/api-keys/management",
15828
+ method: "GET",
15829
+ auth: "user",
15830
+ tags: ["site-{siteId}", "api-keys", "management-keys"],
15831
+ responseKind: "json"
15832
+ },
15833
+ createManagementKey: {
15834
+ path: "/sites/{siteId}/api-keys/management",
15835
+ method: "POST",
15836
+ auth: "user",
15837
+ tags: ["site-{siteId}", "api-keys", "management-keys"],
15838
+ responseKind: "json"
15839
+ },
15840
+ revokeManagementKey: {
15841
+ path: "/sites/{siteId}/api-keys/management",
15842
+ method: "DELETE",
15843
+ auth: "user",
15844
+ tags: ["site-{siteId}", "api-keys", "management-keys"],
15845
+ responseKind: "json"
15846
+ },
15099
15847
  getBookingSettings: {
15100
15848
  path: "/sites/{siteId}/bookings/settings",
15101
15849
  method: "GET",
@@ -16371,6 +17119,15 @@ var ENDPOINT_DEFINITIONS = {
16371
17119
  auth: "public",
16372
17120
  responseKind: "json"
16373
17121
  },
17122
+ // Resolve event occurrence by URL segment (date or UUID)
17123
+ resolveEventOccurrence: {
17124
+ path: "/public/sites/{siteId}/events/occurrences/resolve",
17125
+ method: "GET",
17126
+ revalidate: 60,
17127
+ tags: ["public-events-{siteId}", "event-occurrence"],
17128
+ auth: "public",
17129
+ responseKind: "json"
17130
+ },
16374
17131
  // Public event registration
16375
17132
  registerForEvent: {
16376
17133
  path: "/public/sites/{siteId}/events/register",
@@ -16545,6 +17302,20 @@ if (typeof window === "undefined") {
16545
17302
  revalidateTag = null;
16546
17303
  }
16547
17304
  }
17305
+ var sdkVersion;
17306
+ function generateRequestId() {
17307
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
17308
+ return crypto.randomUUID();
17309
+ }
17310
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
17311
+ const r2 = Math.random() * 16 | 0;
17312
+ const v = c === "x" ? r2 : r2 & 3 | 8;
17313
+ return v.toString(16);
17314
+ });
17315
+ }
17316
+ function setSdkVersion(version2) {
17317
+ sdkVersion = version2;
17318
+ }
16548
17319
  var ApiRequestError = class extends Error {
16549
17320
  constructor(message, options) {
16550
17321
  super(message);
@@ -16556,6 +17327,7 @@ var ApiRequestError = class extends Error {
16556
17327
  this.requestId = options.requestId;
16557
17328
  this.body = options.body;
16558
17329
  this.cause = options.cause;
17330
+ this.errorCode = options.errorCode;
16559
17331
  }
16560
17332
  };
16561
17333
  function buildEndpointURL(baseURL, endpoint) {
@@ -16592,6 +17364,29 @@ async function parseErrorBody(response) {
16592
17364
  return null;
16593
17365
  }
16594
17366
  }
17367
+ function buildSuccessEnvelope(data, requestId) {
17368
+ return {
17369
+ success: true,
17370
+ data,
17371
+ meta: {
17372
+ requestId: requestId ?? generateRequestId(),
17373
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
17374
+ apiVersion: "2025-01-01"
17375
+ }
17376
+ };
17377
+ }
17378
+ function buildErrorEnvelope(code, message, status, requestId) {
17379
+ return {
17380
+ success: false,
17381
+ error: {
17382
+ code,
17383
+ message,
17384
+ requestId: requestId ?? generateRequestId(),
17385
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
17386
+ status
17387
+ }
17388
+ };
17389
+ }
16595
17390
  async function parseSuccessResponse(endpoint, response, config) {
16596
17391
  const responseKind = config.responseKind ?? "json";
16597
17392
  const auth = config.auth ?? "user";
@@ -16599,14 +17394,15 @@ async function parseSuccessResponse(endpoint, response, config) {
16599
17394
  switch (responseKind) {
16600
17395
  case "json": {
16601
17396
  if (response.status === 204 || response.status === 205 || response.status === 304) {
16602
- return void 0;
17397
+ return buildSuccessEnvelope(void 0, requestId);
16603
17398
  }
16604
17399
  const raw = await response.text();
16605
17400
  if (!raw.trim()) {
16606
- return void 0;
17401
+ return buildSuccessEnvelope(void 0, requestId);
16607
17402
  }
17403
+ let parsed;
16608
17404
  try {
16609
- return JSON.parse(raw);
17405
+ parsed = JSON.parse(raw);
16610
17406
  } catch (cause) {
16611
17407
  throw new ApiRequestError(
16612
17408
  `Failed to parse JSON response for endpoint ${String(endpoint)}`,
@@ -16621,32 +17417,33 @@ async function parseSuccessResponse(endpoint, response, config) {
16621
17417
  }
16622
17418
  );
16623
17419
  }
17420
+ if (parsed && typeof parsed === "object" && "success" in parsed && typeof parsed.success === "boolean") {
17421
+ return parsed;
17422
+ }
17423
+ return buildSuccessEnvelope(parsed, requestId);
16624
17424
  }
16625
17425
  case "text": {
16626
- return await response.text();
17426
+ const text2 = await response.text();
17427
+ return buildSuccessEnvelope(text2, requestId);
16627
17428
  }
16628
17429
  case "stream": {
16629
17430
  const body = response.body;
16630
17431
  if (!body) {
16631
- throw new ApiRequestError(
17432
+ return buildErrorEnvelope(
17433
+ "server:internal_error",
16632
17434
  `Expected a streamed body for endpoint ${String(endpoint)}`,
16633
- {
16634
- endpoint,
16635
- status: response.status,
16636
- method: config.method,
16637
- auth,
16638
- requestId
16639
- }
17435
+ response.status,
17436
+ requestId
16640
17437
  );
16641
17438
  }
16642
17439
  const stream = body;
16643
- return stream;
17440
+ return buildSuccessEnvelope(stream, requestId);
16644
17441
  }
16645
17442
  case "void": {
16646
- return void 0;
17443
+ return buildSuccessEnvelope(void 0, requestId);
16647
17444
  }
16648
17445
  default: {
16649
- return void 0;
17446
+ return buildSuccessEnvelope(void 0, requestId);
16650
17447
  }
16651
17448
  }
16652
17449
  }
@@ -16712,11 +17509,15 @@ function createRawCMSClient(headers = {}, baseUrl) {
16712
17509
  const requestInit = {
16713
17510
  method,
16714
17511
  ...options,
17512
+ // Include credentials for same-origin requests (sends cookies for auth)
17513
+ credentials: "same-origin",
16715
17514
  // Don't include body for GET/HEAD requests
16716
17515
  body: isGetOrHead ? void 0 : isFormData ? body : body ? JSON.stringify(body) : void 0,
16717
17516
  headers: {
16718
17517
  ...options.headers,
16719
17518
  ...headers,
17519
+ // Include SDK version if set
17520
+ ...sdkVersion && { "x-sdk-version": sdkVersion },
16720
17521
  // Don't set Content-Type for GET/HEAD requests without body
16721
17522
  ...isGetOrHead ? {} : isFormData ? {} : { "Content-Type": "application/json" }
16722
17523
  },
@@ -16770,94 +17571,30 @@ function createBearerAPIClient(token, baseUrl) {
16770
17571
  return createCMSClient(authHeaders, baseUrl);
16771
17572
  }
16772
17573
 
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
- });
17574
+ // ../api/src/common/envelope.ts
17575
+ function isApiError(result) {
17576
+ return result.success === false;
17577
+ }
17578
+ function isApiSuccess(result) {
17579
+ return result.success === true;
17580
+ }
17581
+ var ApiEnvelopeError = class extends Error {
17582
+ constructor(error) {
17583
+ super(error.message);
17584
+ this.name = "ApiEnvelopeError";
17585
+ this.code = error.code;
17586
+ this.requestId = error.requestId;
17587
+ this.timestamp = error.timestamp;
17588
+ this.status = error.status;
17589
+ this.fieldErrors = error.fieldErrors;
17590
+ }
17591
+ };
17592
+ function unwrapResponse(result) {
17593
+ if (isApiSuccess(result)) {
17594
+ return result.data;
17595
+ }
17596
+ throw new ApiEnvelopeError(result.error);
17597
+ }
16861
17598
 
16862
17599
  // src/client/cache.ts
16863
17600
  var SimpleCache = class {
@@ -16895,7 +17632,100 @@ var SimpleCache = class {
16895
17632
  }
16896
17633
  };
16897
17634
 
17635
+ // src/version.ts
17636
+ var SDK_VERSION = "0.5.0";
17637
+
17638
+ // src/client/error.ts
17639
+ var RiverbankApiError = class _RiverbankApiError extends Error {
17640
+ constructor(apiError) {
17641
+ super(apiError.message);
17642
+ this.name = "RiverbankApiError";
17643
+ this.code = apiError.code;
17644
+ this.requestId = apiError.requestId;
17645
+ this.status = apiError.status;
17646
+ this.fieldErrors = apiError.fieldErrors;
17647
+ this.timestamp = apiError.timestamp;
17648
+ Object.setPrototypeOf(this, _RiverbankApiError.prototype);
17649
+ }
17650
+ /**
17651
+ * Check if this error matches a specific error code
17652
+ *
17653
+ * @example
17654
+ * ```ts
17655
+ * if (error.is('auth:unauthenticated')) {
17656
+ * // Redirect to login
17657
+ * }
17658
+ * ```
17659
+ */
17660
+ is(code) {
17661
+ return this.code === code;
17662
+ }
17663
+ /**
17664
+ * Check if this is an authentication or authorization error
17665
+ *
17666
+ * Matches: auth:unauthenticated, auth:token_expired, auth:token_invalid,
17667
+ * auth:forbidden, auth:mfa_required, auth:insufficient_permissions
17668
+ */
17669
+ isAuthError() {
17670
+ return this.code.startsWith("auth:");
17671
+ }
17672
+ /**
17673
+ * Check if this is a validation error
17674
+ *
17675
+ * Matches: validation:invalid_input, validation:missing_field, validation:invalid_format
17676
+ */
17677
+ isValidationError() {
17678
+ return this.code.startsWith("validation:");
17679
+ }
17680
+ /**
17681
+ * Check if this is a resource error (not found, conflict, etc.)
17682
+ *
17683
+ * Matches: resource:not_found, resource:already_exists, resource:conflict, resource:gone
17684
+ */
17685
+ isResourceError() {
17686
+ return this.code.startsWith("resource:");
17687
+ }
17688
+ /**
17689
+ * Check if this is a rate limiting error
17690
+ */
17691
+ isRateLimitError() {
17692
+ return this.code.startsWith("rate_limit:");
17693
+ }
17694
+ /**
17695
+ * Check if this is a billing/payment error
17696
+ */
17697
+ isBillingError() {
17698
+ return this.code.startsWith("billing:");
17699
+ }
17700
+ /**
17701
+ * Check if this is a server error
17702
+ */
17703
+ isServerError() {
17704
+ return this.code.startsWith("server:");
17705
+ }
17706
+ };
17707
+
16898
17708
  // src/client/index.ts
17709
+ setSdkVersion(SDK_VERSION);
17710
+ function convertToTypedError(error) {
17711
+ if (error instanceof ApiEnvelopeError) {
17712
+ throw new RiverbankApiError({
17713
+ code: error.code,
17714
+ message: error.message,
17715
+ requestId: error.requestId,
17716
+ timestamp: error.timestamp,
17717
+ status: error.status,
17718
+ fieldErrors: error.fieldErrors
17719
+ });
17720
+ }
17721
+ if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
17722
+ const body = error.body;
17723
+ if (isApiError(body)) {
17724
+ throw new RiverbankApiError(body.error);
17725
+ }
17726
+ }
17727
+ throw error;
17728
+ }
16899
17729
  function createRiverbankClient(config) {
16900
17730
  if (!config.baseUrl) {
16901
17731
  throw new Error(
@@ -16922,7 +17752,13 @@ function createRiverbankClient(config) {
16922
17752
  return cached;
16923
17753
  }
16924
17754
  }
16925
- const data = await fetcher();
17755
+ let data;
17756
+ try {
17757
+ const response = await fetcher();
17758
+ data = unwrapResponse(response);
17759
+ } catch (error) {
17760
+ convertToTypedError(error);
17761
+ }
16926
17762
  if (cacheEnabled) {
16927
17763
  cache.set(cacheKey, data);
16928
17764
  }
@@ -16957,33 +17793,27 @@ function createRiverbankClient(config) {
16957
17793
  const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
16958
17794
  const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
16959
17795
  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
- }
17796
+ let orderParam;
16973
17797
  if (order === "newest") {
16974
- apiParams.order = "published_at.desc";
17798
+ orderParam = "published_at.desc";
16975
17799
  } else if (order === "oldest") {
16976
- apiParams.order = "published_at.asc";
17800
+ orderParam = "published_at.asc";
16977
17801
  } 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);
17802
+ orderParam = "title.asc";
16986
17803
  }
17804
+ const apiParams = {
17805
+ siteId,
17806
+ type: contentType,
17807
+ ...typeof limit === "number" && { limit: String(limit) },
17808
+ ...typeof offset === "number" && { offset: String(offset) },
17809
+ ...includeMeta && { meta: "true" },
17810
+ ...orderParam && { order: orderParam },
17811
+ ...preview && { stage: "preview" },
17812
+ ...mode === "manual" && entryIds?.length && {
17813
+ mode: "manual",
17814
+ entryIds: JSON.stringify(entryIds)
17815
+ }
17816
+ };
16987
17817
  return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
16988
17818
  });
16989
17819
  },
@@ -17011,8 +17841,10 @@ function createRiverbankClient(config) {
17011
17841
  }
17012
17842
  const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
17013
17843
  return cachedFetch(cacheKey, async () => {
17014
- const apiParams = { siteId };
17015
- if (ids) apiParams.ids = ids;
17844
+ const apiParams = {
17845
+ siteId,
17846
+ ...ids && { ids }
17847
+ };
17016
17848
  return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
17017
17849
  });
17018
17850
  },
@@ -17023,14 +17855,42 @@ function createRiverbankClient(config) {
17023
17855
  }
17024
17856
  const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
17025
17857
  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;
17858
+ const apiParams = {
17859
+ siteId,
17860
+ ...typeof limit === "number" && { limit: String(limit) },
17861
+ ...from && { from },
17862
+ ...to && { to },
17863
+ ...stage && { stage }
17864
+ };
17031
17865
  return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
17032
17866
  });
17033
17867
  },
17868
+ async resolveEventOccurrence(params) {
17869
+ const { siteId, entryId, segment } = params;
17870
+ if (!siteId || !entryId || !segment) {
17871
+ throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
17872
+ }
17873
+ const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
17874
+ return cachedFetch(cacheKey, async () => {
17875
+ return await apiClient({
17876
+ endpoint: "resolveEventOccurrence",
17877
+ params: { siteId, entryId, segment }
17878
+ });
17879
+ });
17880
+ },
17881
+ async checkRedirect(params) {
17882
+ const { siteId, path } = params;
17883
+ if (!siteId || !path) {
17884
+ throw new Error("checkRedirect() requires siteId and path");
17885
+ }
17886
+ const cacheKey = `redirect:${siteId}:${path}`;
17887
+ return cachedFetch(cacheKey, async () => {
17888
+ return await apiClient({
17889
+ endpoint: "checkRedirect",
17890
+ params: { site: siteId, path }
17891
+ });
17892
+ });
17893
+ },
17034
17894
  clearCache() {
17035
17895
  cache.clear();
17036
17896
  }