@highjumpdigitalsoftware/blog-kit 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/INTEGRATION.md +76 -0
  2. package/LICENSE +74 -0
  3. package/README.md +102 -0
  4. package/astro/AdPreview.astro +64 -0
  5. package/astro/AdPreviewPair.astro +10 -0
  6. package/astro/AuditFindings.astro +29 -0
  7. package/astro/AuditScores.astro +60 -0
  8. package/astro/AuthorCard.astro +32 -0
  9. package/astro/BeforeAfter.astro +26 -0
  10. package/astro/BlogBehaviors.astro +15 -0
  11. package/astro/CTABanner.astro +28 -0
  12. package/astro/CalloutBox.astro +28 -0
  13. package/astro/CaseStudyHero.astro +45 -0
  14. package/astro/ChannelMixBars.astro +33 -0
  15. package/astro/Checklist.astro +24 -0
  16. package/astro/ChecklistItem.astro +15 -0
  17. package/astro/CodeSnippet.astro +20 -0
  18. package/astro/ComparisonTable.astro +103 -0
  19. package/astro/Definition.astro +30 -0
  20. package/astro/DeliveryComparison.astro +40 -0
  21. package/astro/FAQList.astro +43 -0
  22. package/astro/FurtherReading.astro +34 -0
  23. package/astro/ImageFeature.astro +22 -0
  24. package/astro/Infographic.astro +12 -0
  25. package/astro/KeyMetric.astro +40 -0
  26. package/astro/KeywordTable.astro +69 -0
  27. package/astro/List.astro +46 -0
  28. package/astro/MetricHighlight.astro +77 -0
  29. package/astro/NewsletterCTA.astro +40 -0
  30. package/astro/NumberedCard.astro +6 -0
  31. package/astro/ProConBlock.astro +38 -0
  32. package/astro/ProseList.astro +46 -0
  33. package/astro/QuoteBlock.astro +72 -0
  34. package/astro/RegionCallout.astro +24 -0
  35. package/astro/RelatedPosts.astro +47 -0
  36. package/astro/ResultsStrip.astro +59 -0
  37. package/astro/ScoreBar.astro +19 -0
  38. package/astro/SerpPreview.astro +35 -0
  39. package/astro/ServicePromoCard.astro +21 -0
  40. package/astro/StatCard.astro +48 -0
  41. package/astro/StepBlock.astro +5 -0
  42. package/astro/TableOfContents.astro +12 -0
  43. package/astro/TimelineBlock.astro +30 -0
  44. package/astro/TipBox.astro +14 -0
  45. package/astro/TrafficChart.astro +61 -0
  46. package/astro/VerdictCard.astro +48 -0
  47. package/astro/blogkit/Article.astro +63 -0
  48. package/astro/blogkit/BlogIndex.astro +144 -0
  49. package/core/behaviors/code.js +78 -0
  50. package/core/behaviors/comparison.js +52 -0
  51. package/core/behaviors/delivery-comparison.js +52 -0
  52. package/core/behaviors/faq.js +61 -0
  53. package/core/behaviors/index.d.ts +3 -0
  54. package/core/behaviors/index.js +35 -0
  55. package/core/behaviors/keyword-table.js +52 -0
  56. package/core/behaviors/toc.js +130 -0
  57. package/core/css/base.css +146 -0
  58. package/core/css/components.css +2632 -0
  59. package/core/css/index-listing.css +207 -0
  60. package/core/css/index.css +13 -0
  61. package/core/css/tokens.css +127 -0
  62. package/core/icons.ts +20 -0
  63. package/core/lib.ts +70 -0
  64. package/core/manifest/components.json +573 -0
  65. package/core/manifest/frontmatter.json +19 -0
  66. package/core/manifest/templates.json +77 -0
  67. package/dist/core/behaviors/code.js +78 -0
  68. package/dist/core/behaviors/comparison.js +52 -0
  69. package/dist/core/behaviors/delivery-comparison.js +52 -0
  70. package/dist/core/behaviors/faq.js +61 -0
  71. package/dist/core/behaviors/index.d.ts +3 -0
  72. package/dist/core/behaviors/index.js +35 -0
  73. package/dist/core/behaviors/keyword-table.js +52 -0
  74. package/dist/core/behaviors/toc.js +130 -0
  75. package/dist/core/css/base.css +146 -0
  76. package/dist/core/css/components.css +2632 -0
  77. package/dist/core/css/index-listing.css +207 -0
  78. package/dist/core/css/index.css +13 -0
  79. package/dist/core/css/tokens.css +127 -0
  80. package/dist/core/icons.d.ts +2 -0
  81. package/dist/core/icons.d.ts.map +1 -0
  82. package/dist/core/icons.js +20 -0
  83. package/dist/core/icons.js.map +1 -0
  84. package/dist/core/lib.d.ts +21 -0
  85. package/dist/core/lib.d.ts.map +1 -0
  86. package/dist/core/lib.js +57 -0
  87. package/dist/core/lib.js.map +1 -0
  88. package/dist/core/manifest/components.json +573 -0
  89. package/dist/core/manifest/frontmatter.json +19 -0
  90. package/dist/core/manifest/templates.json +77 -0
  91. package/dist/package/adapters/hjd-api.d.ts +14 -0
  92. package/dist/package/adapters/hjd-api.d.ts.map +1 -0
  93. package/dist/package/adapters/hjd-api.js +57 -0
  94. package/dist/package/adapters/hjd-api.js.map +1 -0
  95. package/dist/package/adapters/index.d.ts +13 -0
  96. package/dist/package/adapters/index.d.ts.map +1 -0
  97. package/dist/package/adapters/index.js +16 -0
  98. package/dist/package/adapters/index.js.map +1 -0
  99. package/dist/package/adapters/local.d.ts +13 -0
  100. package/dist/package/adapters/local.d.ts.map +1 -0
  101. package/dist/package/adapters/local.js +72 -0
  102. package/dist/package/adapters/local.js.map +1 -0
  103. package/dist/package/adapters/source.d.ts +39 -0
  104. package/dist/package/adapters/source.d.ts.map +1 -0
  105. package/dist/package/adapters/source.js +19 -0
  106. package/dist/package/adapters/source.js.map +1 -0
  107. package/dist/package/article.d.ts +17 -0
  108. package/dist/package/article.d.ts.map +1 -0
  109. package/dist/package/article.js +37 -0
  110. package/dist/package/article.js.map +1 -0
  111. package/dist/package/astro/data.d.ts +45 -0
  112. package/dist/package/astro/data.d.ts.map +1 -0
  113. package/dist/package/astro/data.js +81 -0
  114. package/dist/package/astro/data.js.map +1 -0
  115. package/dist/package/astro/freshness.d.ts +11 -0
  116. package/dist/package/astro/freshness.d.ts.map +1 -0
  117. package/dist/package/astro/freshness.js +48 -0
  118. package/dist/package/astro/freshness.js.map +1 -0
  119. package/dist/package/astro/index.d.ts +12 -0
  120. package/dist/package/astro/index.d.ts.map +1 -0
  121. package/dist/package/astro/index.js +31 -0
  122. package/dist/package/astro/index.js.map +1 -0
  123. package/dist/package/blog-index.d.ts +10 -0
  124. package/dist/package/blog-index.d.ts.map +1 -0
  125. package/dist/package/blog-index.js +27 -0
  126. package/dist/package/blog-index.js.map +1 -0
  127. package/dist/package/cli/exchange.d.ts +27 -0
  128. package/dist/package/cli/exchange.d.ts.map +1 -0
  129. package/dist/package/cli/exchange.js +94 -0
  130. package/dist/package/cli/exchange.js.map +1 -0
  131. package/dist/package/cli/index.d.ts +3 -0
  132. package/dist/package/cli/index.d.ts.map +1 -0
  133. package/dist/package/cli/index.js +301 -0
  134. package/dist/package/cli/index.js.map +1 -0
  135. package/dist/package/config/define.d.ts +13 -0
  136. package/dist/package/config/define.d.ts.map +1 -0
  137. package/dist/package/config/define.js +14 -0
  138. package/dist/package/config/define.js.map +1 -0
  139. package/dist/package/config/resolve.d.ts +11 -0
  140. package/dist/package/config/resolve.d.ts.map +1 -0
  141. package/dist/package/config/resolve.js +43 -0
  142. package/dist/package/config/resolve.js.map +1 -0
  143. package/dist/package/config/types.d.ts +74 -0
  144. package/dist/package/config/types.d.ts.map +1 -0
  145. package/dist/package/config/types.js +13 -0
  146. package/dist/package/config/types.js.map +1 -0
  147. package/dist/package/index-core.d.ts +28 -0
  148. package/dist/package/index-core.d.ts.map +1 -0
  149. package/dist/package/index-core.js +102 -0
  150. package/dist/package/index-core.js.map +1 -0
  151. package/dist/package/index.d.ts +13 -0
  152. package/dist/package/index.d.ts.map +1 -0
  153. package/dist/package/index.js +25 -0
  154. package/dist/package/index.js.map +1 -0
  155. package/dist/package/mdx/render-astro.d.ts +18 -0
  156. package/dist/package/mdx/render-astro.d.ts.map +1 -0
  157. package/dist/package/mdx/render-astro.js +75 -0
  158. package/dist/package/mdx/render-astro.js.map +1 -0
  159. package/dist/package/mdx/render.d.ts +13 -0
  160. package/dist/package/mdx/render.d.ts.map +1 -0
  161. package/dist/package/mdx/render.js +37 -0
  162. package/dist/package/mdx/render.js.map +1 -0
  163. package/dist/react/AdPreview.d.ts +26 -0
  164. package/dist/react/AdPreview.d.ts.map +1 -0
  165. package/dist/react/AdPreview.js +8 -0
  166. package/dist/react/AdPreview.js.map +1 -0
  167. package/dist/react/AdPreviewPair.d.ts +7 -0
  168. package/dist/react/AdPreviewPair.d.ts.map +1 -0
  169. package/dist/react/AdPreviewPair.js +5 -0
  170. package/dist/react/AdPreviewPair.js.map +1 -0
  171. package/dist/react/AuditFindings.d.ts +14 -0
  172. package/dist/react/AuditFindings.d.ts.map +1 -0
  173. package/dist/react/AuditFindings.js +5 -0
  174. package/dist/react/AuditFindings.js.map +1 -0
  175. package/dist/react/AuditScores.d.ts +12 -0
  176. package/dist/react/AuditScores.d.ts.map +1 -0
  177. package/dist/react/AuditScores.js +25 -0
  178. package/dist/react/AuditScores.js.map +1 -0
  179. package/dist/react/AuthorCard.d.ts +10 -0
  180. package/dist/react/AuthorCard.d.ts.map +1 -0
  181. package/dist/react/AuthorCard.js +6 -0
  182. package/dist/react/AuthorCard.js.map +1 -0
  183. package/dist/react/BeforeAfter.d.ts +12 -0
  184. package/dist/react/BeforeAfter.d.ts.map +1 -0
  185. package/dist/react/BeforeAfter.js +7 -0
  186. package/dist/react/BeforeAfter.js.map +1 -0
  187. package/dist/react/BlogBehaviors.d.ts +10 -0
  188. package/dist/react/BlogBehaviors.d.ts.map +1 -0
  189. package/dist/react/BlogBehaviors.js +20 -0
  190. package/dist/react/BlogBehaviors.js.map +1 -0
  191. package/dist/react/CTABanner.d.ts +8 -0
  192. package/dist/react/CTABanner.d.ts.map +1 -0
  193. package/dist/react/CTABanner.js +9 -0
  194. package/dist/react/CTABanner.js.map +1 -0
  195. package/dist/react/CalloutBox.d.ts +13 -0
  196. package/dist/react/CalloutBox.d.ts.map +1 -0
  197. package/dist/react/CalloutBox.js +9 -0
  198. package/dist/react/CalloutBox.js.map +1 -0
  199. package/dist/react/CaseStudyHero.d.ts +20 -0
  200. package/dist/react/CaseStudyHero.d.ts.map +1 -0
  201. package/dist/react/CaseStudyHero.js +7 -0
  202. package/dist/react/CaseStudyHero.js.map +1 -0
  203. package/dist/react/ChannelMixBars.d.ts +18 -0
  204. package/dist/react/ChannelMixBars.d.ts.map +1 -0
  205. package/dist/react/ChannelMixBars.js +6 -0
  206. package/dist/react/ChannelMixBars.js.map +1 -0
  207. package/dist/react/Checklist.d.ts +10 -0
  208. package/dist/react/Checklist.d.ts.map +1 -0
  209. package/dist/react/Checklist.js +7 -0
  210. package/dist/react/Checklist.js.map +1 -0
  211. package/dist/react/ChecklistItem.d.ts +7 -0
  212. package/dist/react/ChecklistItem.d.ts.map +1 -0
  213. package/dist/react/ChecklistItem.js +5 -0
  214. package/dist/react/ChecklistItem.js.map +1 -0
  215. package/dist/react/CodeSnippet.d.ts +17 -0
  216. package/dist/react/CodeSnippet.d.ts.map +1 -0
  217. package/dist/react/CodeSnippet.js +14 -0
  218. package/dist/react/CodeSnippet.js.map +1 -0
  219. package/dist/react/ComparisonTable.d.ts +22 -0
  220. package/dist/react/ComparisonTable.d.ts.map +1 -0
  221. package/dist/react/ComparisonTable.js +35 -0
  222. package/dist/react/ComparisonTable.js.map +1 -0
  223. package/dist/react/Definition.d.ts +9 -0
  224. package/dist/react/Definition.d.ts.map +1 -0
  225. package/dist/react/Definition.js +19 -0
  226. package/dist/react/Definition.js.map +1 -0
  227. package/dist/react/DeliveryComparison.d.ts +16 -0
  228. package/dist/react/DeliveryComparison.d.ts.map +1 -0
  229. package/dist/react/DeliveryComparison.js +7 -0
  230. package/dist/react/DeliveryComparison.js.map +1 -0
  231. package/dist/react/FAQList.d.ts +20 -0
  232. package/dist/react/FAQList.d.ts.map +1 -0
  233. package/dist/react/FAQList.js +19 -0
  234. package/dist/react/FAQList.js.map +1 -0
  235. package/dist/react/FurtherReading.d.ts +21 -0
  236. package/dist/react/FurtherReading.d.ts.map +1 -0
  237. package/dist/react/FurtherReading.js +13 -0
  238. package/dist/react/FurtherReading.js.map +1 -0
  239. package/dist/react/ImageFeature.d.ts +9 -0
  240. package/dist/react/ImageFeature.d.ts.map +1 -0
  241. package/dist/react/ImageFeature.js +6 -0
  242. package/dist/react/ImageFeature.js.map +1 -0
  243. package/dist/react/Infographic.d.ts +6 -0
  244. package/dist/react/Infographic.d.ts.map +1 -0
  245. package/dist/react/Infographic.js +7 -0
  246. package/dist/react/Infographic.js.map +1 -0
  247. package/dist/react/KeyMetric.d.ts +16 -0
  248. package/dist/react/KeyMetric.d.ts.map +1 -0
  249. package/dist/react/KeyMetric.js +15 -0
  250. package/dist/react/KeyMetric.js.map +1 -0
  251. package/dist/react/KeywordTable.d.ts +18 -0
  252. package/dist/react/KeywordTable.d.ts.map +1 -0
  253. package/dist/react/KeywordTable.js +23 -0
  254. package/dist/react/KeywordTable.js.map +1 -0
  255. package/dist/react/List.d.ts +11 -0
  256. package/dist/react/List.d.ts.map +1 -0
  257. package/dist/react/List.js +21 -0
  258. package/dist/react/List.js.map +1 -0
  259. package/dist/react/MetricHighlight.d.ts +15 -0
  260. package/dist/react/MetricHighlight.d.ts.map +1 -0
  261. package/dist/react/MetricHighlight.js +26 -0
  262. package/dist/react/MetricHighlight.js.map +1 -0
  263. package/dist/react/NewsletterCTA.d.ts +9 -0
  264. package/dist/react/NewsletterCTA.d.ts.map +1 -0
  265. package/dist/react/NewsletterCTA.js +5 -0
  266. package/dist/react/NewsletterCTA.js.map +1 -0
  267. package/dist/react/NumberedCard.d.ts +9 -0
  268. package/dist/react/NumberedCard.d.ts.map +1 -0
  269. package/dist/react/NumberedCard.js +7 -0
  270. package/dist/react/NumberedCard.js.map +1 -0
  271. package/dist/react/ProConBlock.d.ts +6 -0
  272. package/dist/react/ProConBlock.d.ts.map +1 -0
  273. package/dist/react/ProConBlock.js +7 -0
  274. package/dist/react/ProConBlock.js.map +1 -0
  275. package/dist/react/ProseList.d.ts +17 -0
  276. package/dist/react/ProseList.d.ts.map +1 -0
  277. package/dist/react/ProseList.js +26 -0
  278. package/dist/react/ProseList.js.map +1 -0
  279. package/dist/react/QuoteBlock.d.ts +17 -0
  280. package/dist/react/QuoteBlock.d.ts.map +1 -0
  281. package/dist/react/QuoteBlock.js +26 -0
  282. package/dist/react/QuoteBlock.js.map +1 -0
  283. package/dist/react/RegionCallout.d.ts +13 -0
  284. package/dist/react/RegionCallout.d.ts.map +1 -0
  285. package/dist/react/RegionCallout.js +5 -0
  286. package/dist/react/RegionCallout.js.map +1 -0
  287. package/dist/react/RelatedPosts.d.ts +20 -0
  288. package/dist/react/RelatedPosts.d.ts.map +1 -0
  289. package/dist/react/RelatedPosts.js +7 -0
  290. package/dist/react/RelatedPosts.js.map +1 -0
  291. package/dist/react/ResultsStrip.d.ts +18 -0
  292. package/dist/react/ResultsStrip.d.ts.map +1 -0
  293. package/dist/react/ResultsStrip.js +22 -0
  294. package/dist/react/ResultsStrip.js.map +1 -0
  295. package/dist/react/ScoreBar.d.ts +8 -0
  296. package/dist/react/ScoreBar.d.ts.map +1 -0
  297. package/dist/react/ScoreBar.js +6 -0
  298. package/dist/react/ScoreBar.js.map +1 -0
  299. package/dist/react/SerpPreview.d.ts +18 -0
  300. package/dist/react/SerpPreview.d.ts.map +1 -0
  301. package/dist/react/SerpPreview.js +13 -0
  302. package/dist/react/SerpPreview.js.map +1 -0
  303. package/dist/react/ServicePromoCard.d.ts +14 -0
  304. package/dist/react/ServicePromoCard.d.ts.map +1 -0
  305. package/dist/react/ServicePromoCard.js +12 -0
  306. package/dist/react/ServicePromoCard.js.map +1 -0
  307. package/dist/react/StatCard.d.ts +13 -0
  308. package/dist/react/StatCard.d.ts.map +1 -0
  309. package/dist/react/StatCard.js +20 -0
  310. package/dist/react/StatCard.js.map +1 -0
  311. package/dist/react/StepBlock.d.ts +8 -0
  312. package/dist/react/StepBlock.d.ts.map +1 -0
  313. package/dist/react/StepBlock.js +5 -0
  314. package/dist/react/StepBlock.js.map +1 -0
  315. package/dist/react/TableOfContents.d.ts +14 -0
  316. package/dist/react/TableOfContents.d.ts.map +1 -0
  317. package/dist/react/TableOfContents.js +12 -0
  318. package/dist/react/TableOfContents.js.map +1 -0
  319. package/dist/react/TimelineBlock.d.ts +14 -0
  320. package/dist/react/TimelineBlock.d.ts.map +1 -0
  321. package/dist/react/TimelineBlock.js +8 -0
  322. package/dist/react/TimelineBlock.js.map +1 -0
  323. package/dist/react/TipBox.d.ts +6 -0
  324. package/dist/react/TipBox.d.ts.map +1 -0
  325. package/dist/react/TipBox.js +5 -0
  326. package/dist/react/TipBox.js.map +1 -0
  327. package/dist/react/TrafficChart.d.ts +16 -0
  328. package/dist/react/TrafficChart.d.ts.map +1 -0
  329. package/dist/react/TrafficChart.js +14 -0
  330. package/dist/react/TrafficChart.js.map +1 -0
  331. package/dist/react/VerdictCard.d.ts +15 -0
  332. package/dist/react/VerdictCard.d.ts.map +1 -0
  333. package/dist/react/VerdictCard.js +5 -0
  334. package/dist/react/VerdictCard.js.map +1 -0
  335. package/dist/react/components-map.d.ts +133 -0
  336. package/dist/react/components-map.d.ts.map +1 -0
  337. package/dist/react/components-map.js +120 -0
  338. package/dist/react/components-map.js.map +1 -0
  339. package/dist/react/index.d.ts +5 -0
  340. package/dist/react/index.d.ts.map +1 -0
  341. package/dist/react/index.js +13 -0
  342. package/dist/react/index.js.map +1 -0
  343. package/package.json +116 -0
  344. package/react/AdPreview.tsx +94 -0
  345. package/react/AdPreviewPair.tsx +16 -0
  346. package/react/AuditFindings.tsx +43 -0
  347. package/react/AuditScores.tsx +73 -0
  348. package/react/AuthorCard.tsx +35 -0
  349. package/react/BeforeAfter.tsx +27 -0
  350. package/react/BlogBehaviors.tsx +21 -0
  351. package/react/CTABanner.tsx +32 -0
  352. package/react/CalloutBox.tsx +31 -0
  353. package/react/CaseStudyHero.tsx +71 -0
  354. package/react/ChannelMixBars.tsx +50 -0
  355. package/react/Checklist.tsx +31 -0
  356. package/react/ChecklistItem.tsx +19 -0
  357. package/react/CodeSnippet.tsx +36 -0
  358. package/react/ComparisonTable.tsx +114 -0
  359. package/react/Definition.tsx +36 -0
  360. package/react/DeliveryComparison.tsx +62 -0
  361. package/react/FAQList.tsx +61 -0
  362. package/react/FurtherReading.tsx +46 -0
  363. package/react/ImageFeature.tsx +26 -0
  364. package/react/Infographic.tsx +18 -0
  365. package/react/KeyMetric.tsx +61 -0
  366. package/react/KeywordTable.tsx +92 -0
  367. package/react/List.tsx +58 -0
  368. package/react/MetricHighlight.tsx +86 -0
  369. package/react/NewsletterCTA.tsx +48 -0
  370. package/react/NumberedCard.tsx +7 -0
  371. package/react/ProConBlock.tsx +42 -0
  372. package/react/ProseList.tsx +72 -0
  373. package/react/QuoteBlock.tsx +89 -0
  374. package/react/RegionCallout.tsx +38 -0
  375. package/react/RelatedPosts.tsx +58 -0
  376. package/react/ResultsStrip.tsx +77 -0
  377. package/react/ScoreBar.tsx +27 -0
  378. package/react/SerpPreview.tsx +59 -0
  379. package/react/ServicePromoCard.tsx +43 -0
  380. package/react/StatCard.tsx +62 -0
  381. package/react/StepBlock.tsx +5 -0
  382. package/react/TableOfContents.tsx +27 -0
  383. package/react/TimelineBlock.tsx +35 -0
  384. package/react/TipBox.tsx +16 -0
  385. package/react/TrafficChart.tsx +79 -0
  386. package/react/VerdictCard.tsx +60 -0
  387. package/react/components-map.ts +122 -0
  388. package/react/index.ts +13 -0
  389. package/templates/blogkit/app/api/blogkit/revalidate/route.ts.tmpl +32 -0
  390. package/templates/blogkit/app/blog/[slug]/page.tsx.tmpl +41 -0
  391. package/templates/blogkit/app/blog/page.tsx.tmpl +18 -0
  392. package/templates/blogkit/blogkit.config.ts.tmpl +23 -0
  393. package/templates/blogkit-astro/BLOGKIT_ASTRO_SETUP.md.tmpl +49 -0
  394. package/templates/blogkit-astro/src/blogkit.config.ts.tmpl +29 -0
  395. package/templates/blogkit-astro/src/pages/api/blogkit/revalidate.ts.tmpl +46 -0
  396. package/templates/blogkit-astro/src/pages/blog/[slug].astro.tmpl +39 -0
  397. package/templates/blogkit-astro/src/pages/blog/index.astro.tmpl +29 -0
package/INTEGRATION.md ADDED
@@ -0,0 +1,76 @@
1
+ # Onboarding a site to blog-kit
2
+
3
+ The repeatable recipe to point a site's `/blog` at the kit. Works for **Next.js** (React adapter) and **Astro** (Astro adapter) — the steps are identical except the adapter and how you load behaviours. This codifies the proven Capture AI integration.
4
+
5
+ Mental model (5 steps): **drawer → paint → plug in → register → publish.**
6
+
7
+ ```
8
+ content repo (theirs) site repo (thin /blog route) blog-publisher MCP
9
+ posts/*.mdx vendored src/_blogkit/ writes posts here
10
+ images/* /blog route + sync + theme reads the manifest
11
+ theme.json ────compiled───► _theme.css (.blog-root tokens)
12
+ ```
13
+
14
+ ---
15
+
16
+ ## 1. Drawer — content repo
17
+ Create (or reuse) the site's content repo holding `content/posts/*.mdx`, `content/images/`, and a `content/theme.json` (see `templates/theme.example.json`). The site already fetches this at build (the existing `fetch-blog` step).
18
+
19
+ ## 2. Paint — theme
20
+ Author `theme.json` (brand colours + font families only — everything else inherits the kit's neutral defaults). At build, compile it to the per-tenant token stylesheet:
21
+ ```
22
+ node scripts/theme-to-css.mjs blog/content/theme.json src/app/blog/_theme.css
23
+ ```
24
+ Map fonts to the host's `next/font` (or Astro font) CSS variables, e.g. `--blog-font-display: var(--font-cairo), 'Cairo', sans-serif`.
25
+
26
+ ## 3. Plug in — vendor the kit + wire the route
27
+ - Copy `templates/sync-blog-kit.mjs` into the site's `scripts/` and run it once to vendor the kit into `src/_blogkit/` (`BLOG_KIT_ADAPTER=astro` for an Astro site). **COMMIT `src/_blogkit/` to the repo — do NOT gitignore it.** The build env (e.g. Railway) has no access to the canonical kit, so the vendored copy must be present in the repo or the `@/_blogkit` import fails the build. To update later: re-run the sync script, then commit the refreshed `src/_blogkit/`. (This is also what makes the site survive a disconnect — the rendering code lives in its own repo.)
28
+ - **Load the CSS** in the blog route/layout, **after** the kit core:
29
+ ```ts
30
+ import "@/_blogkit/core/css/index.css";
31
+ import "./_theme.css";
32
+ ```
33
+ - **Render the post body under `.blog-root`** with the kit's render map. Drop any host `prose`/design-system wrapper — the kit owns prose typography:
34
+ - **Next.js** (`/blog/[slug]` or root `/[slug]`):
35
+ ```tsx
36
+ import { MDXRemote } from "next-mdx-remote/rsc"; // v6+ required
37
+ import { blogComponents } from "@/_blogkit/react";
38
+ // ...
39
+ <div className="blog-root">
40
+ <div className="bk-prose">
41
+ <MDXRemote source={post.content} components={{ ...blogComponents, ...siteCustom }}
42
+ options={{ blockJS: false, mdxOptions: { /* remark/rehype */ } }} />
43
+ </div>
44
+ </div>
45
+ ```
46
+ ⚠️ **`next-mdx-remote` must be v6+ with `blockJS:false`.** v5 defaults to stripping JSX-expression attributes (`score={8}`), which silently breaks kit components.
47
+ - **Astro** (content collection): pass the kit's astro components to `<Content components={...} />` inside a `<div class="blog-root">`.
48
+ - **Per-site custom components** (true one-offs) live in the site, merged on top: `{ ...blogComponents, ...siteCustom }`.
49
+ - **Frame** components (hero, index listing) stay in the site — they're not part of the kit's MDX surface.
50
+
51
+ ## 4. Load behaviours (interactive components only)
52
+ Interactive components (`TableOfContents`, `FAQList`, `ComparisonTable`, `CodeSnippet`, `KeywordTable`, `DeliveryComparison`) need their vanilla-JS behaviour activated once on the client. The kit ships a **drop-in loader** — you don't hand-roll an effect or wire individual scripts:
53
+
54
+ - **Next.js:** render `<BlogBehaviors />` **once inside the post route's `.blog-root`** (it renders nothing):
55
+ ```tsx
56
+ import { blogComponents, BlogBehaviors } from "@/_blogkit/react";
57
+ // ...
58
+ <div className="blog-root">
59
+ <BlogBehaviors />
60
+ <div className="bk-prose"><MDXRemote … /></div>
61
+ </div>
62
+ ```
63
+ It re-runs on client navigation (keyed on the pathname) so a freshly mounted post gets wired.
64
+ - **Astro:** drop `<BlogBehaviors />` from `@/_blogkit/astro/BlogBehaviors.astro` once inside the post layout's `.blog-root`. It runs on load and on `astro:page-load` (View Transitions).
65
+
66
+ Under the hood the loader calls `initBlogBehaviors()` from `core/behaviors/index.js`, which fans out to each behaviour. Every behaviour is framework-neutral, **idempotent** (re-running only wires not-yet-enhanced markup), and a no-op when its component isn't on the page. The `TableOfContents` scans the prose region that contains it (`.bk-prose` → `.blog-root`), so it works without the site wrapping the body in any particular tag.
67
+
68
+ ## 5. Register — MCP
69
+ In `blog-publisher/sites/<id>.json`, set `components` to the kit names the site exposes (+ optional `customComponents`, `+` the repo/deploy/brand blocks). The MCP reads the vendored manifest, so docs come for free; boot warns on any name not in the manifest.
70
+
71
+ ---
72
+
73
+ ## Notes
74
+ - **URL shape is preserved per site** — parameterise the route prefix (Capture serves `/blog/[slug]`, HJD serves root `/[slug]`). Never change existing slugs.
75
+ - **Old component names** keep working via the kit's alias map (e.g. `PullQuote`→`QuoteBlock`), so existing posts don't break during migration.
76
+ - **Ownership / disconnect:** the content repo (theirs) + the vendored kit copy keep rendering with zero dependency on the MCP. The MCP is only the writer.
package/LICENSE ADDED
@@ -0,0 +1,74 @@
1
+ High Jump Digital — Blog Kit Software Licence
2
+
3
+ Copyright © High Jump Digital. All rights reserved.
4
+
5
+ This software, including its source code, components, build output, and
6
+ accompanying documentation (the "Software"), is proprietary and confidential to
7
+ High Jump Digital ("High Jump Digital", "we", "us"). It is licensed, not sold.
8
+ The Software is published to a public package registry only so that it can be
9
+ installed conveniently; that public availability does not make the Software open
10
+ source and does not grant any rights beyond those set out below.
11
+
12
+ 1. GRANT OF USE
13
+ Subject to the terms of this Licence, High Jump Digital grants a limited,
14
+ non-exclusive, non-transferable, revocable licence to use the Software to:
15
+ (a) High Jump Digital; and
16
+ (b) any client of High Jump Digital that we have authorised,
17
+ solely to operate a blog or website that is built or managed by High Jump
18
+ Digital for that client. No other use is permitted except as expressly stated
19
+ in this Licence.
20
+
21
+ 2. YOUR RIGHT TO KEEP USING YOUR OWN SITE (NO LOCK-IN)
22
+ This clause overrides any restriction below that would otherwise conflict with
23
+ it. A client retains the right to continue using the copy of the Software that
24
+ is already installed or deployed on that client's own website, indefinitely,
25
+ including after the client's engagement with High Jump Digital has ended. That
26
+ continued-use right covers operating, running, and serving the client's own
27
+ website from that already-deployed copy. It does not, by itself, grant any
28
+ right to obtain new versions, redistribute the Software, or use the Software
29
+ for anything beyond operating that client's own website.
30
+
31
+ 3. RESTRICTIONS
32
+ Except as expressly permitted in clauses 1 and 2, you may not:
33
+ (a) redistribute, resell, rent, lease, sublicense, publish, or otherwise make
34
+ the Software available to any third party;
35
+ (b) use the Software, or any part of it, to build, train, or operate any
36
+ product or service that competes with High Jump Digital;
37
+ (c) permit any party that High Jump Digital has not authorised to use the
38
+ Software, except for a client's continued-use right under clause 2;
39
+ (d) remove, obscure, or alter any copyright, trademark, or other proprietary
40
+ notice in the Software; or
41
+ (e) reverse engineer, decompile, or disassemble the Software, except to the
42
+ extent that this restriction is prohibited by applicable law. For the
43
+ avoidance of doubt, this does not prevent a client from reading, inspecting,
44
+ or operating the copy of the Software deployed on that client's own website
45
+ as permitted by clauses 1 and 2.
46
+
47
+ 4. OWNERSHIP
48
+ The Software is and remains the property of High Jump Digital. All right, title,
49
+ and interest in and to the Software, including all intellectual property rights,
50
+ remain with High Jump Digital. This Licence grants a right to use the Software
51
+ only; it does not transfer ownership of the Software or any part of it.
52
+
53
+ 5. NO WARRANTY
54
+ THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY
55
+ KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
56
+ IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE,
57
+ AND NON-INFRINGEMENT. HIGH JUMP DIGITAL DOES NOT WARRANT THAT THE SOFTWARE WILL
58
+ BE UNINTERRUPTED, ERROR-FREE, OR SECURE.
59
+
60
+ 6. LIMITATION OF LIABILITY
61
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, HIGH JUMP DIGITAL SHALL NOT
62
+ BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR EXEMPLARY
63
+ DAMAGES, OR FOR ANY LOSS OF PROFITS, REVENUE, DATA, OR GOODWILL, ARISING OUT OF
64
+ OR RELATED TO THE SOFTWARE OR THIS LICENCE, HOWEVER CAUSED AND UNDER ANY THEORY
65
+ OF LIABILITY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. NOTHING IN
66
+ THIS LICENCE LIMITS ANY LIABILITY THAT CANNOT BE LIMITED OR EXCLUDED UNDER
67
+ APPLICABLE LAW.
68
+
69
+ 7. GENERAL
70
+ If any provision of this Licence is held to be unenforceable, the remaining
71
+ provisions remain in full force and effect. The governing law and jurisdiction
72
+ for this Licence are those of the relevant High Jump Digital entity.
73
+
74
+ For permissions beyond the scope of this Licence, contact High Jump Digital.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # blog-kit — shared, multi-framework blog component kit
2
+
3
+ One canonical component library that every blog (Next.js **or** Astro) is built from. A site supplies only its **content** (the MDX in its content repo) and its **paint** (`theme.json`); everything structural — component markup, CSS, behaviours, and the component manifest — lives here and is shared.
4
+
5
+ > Canonical copy lives at `/home/dan/blog-kit` (its own standalone project — destined for its own `highjumpdigital/blog-kit` repo). It is **vendored** into each consumer at build time (a per-site `sync-blog-kit.mjs` copies `core/` + the right adapter into the site). Edit the canonical copy; never hand-edit a vendored copy. Pin each site to a kit version.
6
+
7
+ ---
8
+
9
+ ## Two ways to consume the kit
10
+
11
+ The kit serves both an in-house **vendored** path and an external **installable-package** path from one source of truth.
12
+
13
+ 1. **Vendored (our own sites).** `sync-blog-kit.mjs` copies `core/` + the right adapter (`react/` or `astro/`) into the site's `src/_blogkit/`, committed to the site repo. This is unchanged — see [INTEGRATION.md](./INTEGRATION.md). The blog-publisher MCP reads `core/manifest/` as its single source of truth.
14
+ 2. **Installable package `@highjumpdigitalsoftware/blog-kit` (external clients).** The same `core/` + `react/` + `astro/` ship as a published npm package with three extra abilities the vendored path doesn't need. **The managed path is dual-framework: a first-class Next.js path AND a first-class Astro path** — same content API, same swappable source, same eject.
15
+ - **A swappable content-source adapter** — `local` (read `.mdx` from the repo) **or** `hjd-api` (fetch MDX from our per-tenant content API over Bearer auth). The two satisfy the same `ContentSource` interface (framework-neutral), so eject is a one-line `source.mode` flip on either framework.
16
+ - **Route surfaces + a connect/init CLI** — `<Article>` (`/blog/[slug]`) and `<BlogIndex>` (`/blog`), source-agnostic, on both frameworks. `npx @highjumpdigitalsoftware/blog-kit connect <pairing-code>` (or `init`) scaffolds the routes + an optional revalidate route + writes config/env. **The framework is taken from the exchange, else `--framework next|astro`, else sniffed from the project.**
17
+ - **Next.js**: App-Router **ISR** routes (`export const revalidate`), MDX rendered via `next-mdx-remote/rsc`. New posts appear with no redeploy via the ISR timer; instant via `/api/blogkit/revalidate` (`revalidatePath`).
18
+ - **Astro**: **SSR** routes (`output: 'server'`, `prerender:false`). Astro has no ISR and its MDX is build-time-only, so the remote MDX string is **compiled at request time** with `@mdx-js/mdx` + the kit's React component map → static HTML injected via `set:html` (identical markup to the `.astro` components, eject-safe). Freshness = SSR always-fresh + an in-process TTL cache (the ISR analogue), busted instantly by the scaffolded `/api/blogkit/revalidate` Astro endpoint. Works on a host like Replit with zero cooperation.
19
+ - **npm packaging** — `npm run build` (`tsc -p tsconfig.package.json` + `scripts/pkg-copy-assets.mjs`) emits `dist/`; `npm run selfcheck` is a fast Node-only sanity check. The package source lives under `package/` (`package/astro/` = the Astro managed layer); the `.astro` route surfaces ship raw under `astro/blogkit/`. **The existing `core/` / `react/` components / `astro/` components / `demo/` / manifest are untouched** (the component maps were factored into `react/components-map.ts`, re-exported by `react/index.ts` — same public surface — so the Astro render path can import the map without pulling Next).
20
+
21
+ ```bash
22
+ # Next.js
23
+ npm i @highjumpdigitalsoftware/blog-kit
24
+ npm i next-mdx-remote remark-gfm rehype-slug rehype-autolink-headings rehype-pretty-code
25
+ npx @highjumpdigitalsoftware/blog-kit connect <pairing-code> # scaffolds Next ISR routes + writes config/env
26
+
27
+ # Astro
28
+ npm i @highjumpdigitalsoftware/blog-kit
29
+ npm i @mdx-js/mdx react react-dom remark-gfm rehype-slug rehype-autolink-headings
30
+ npx astro add node # an SSR adapter; set output:"server"
31
+ npx @highjumpdigitalsoftware/blog-kit connect <pairing-code> # scaffolds Astro SSR routes (framework from the exchange)
32
+ ```
33
+ ```ts
34
+ // Next: app/blogkit.config.ts · Astro: src/blogkit.config.ts
35
+ import { defineConfig } from "@highjumpdigitalsoftware/blog-kit"; // Next
36
+ // import { defineConfig } from "@highjumpdigitalsoftware/blog-kit/astro"; // Astro
37
+ export default defineConfig({
38
+ source: { mode: "hjd-api" }, // URL+token from BLOGKIT_CONTENT_URL / BLOGKIT_SITE_TOKEN
39
+ brand: { name: "Acme Co" }, // or { mode: "local", postsDir: "content/posts" } (ejected)
40
+ });
41
+ ```
42
+
43
+ See the design doc `hjd-app/docs/managed-blog-plan.md` (§7 adapter, §8 connection/API/freshness, §9 eject). The Astro path's request-time MDX compile is the key difference from Next's ISR — both meet "new posts appear with no redeploy".
44
+
45
+ ---
46
+
47
+ ## The shape
48
+
49
+ ```
50
+ core/ framework-AGNOSTIC — the real "build once"
51
+ css/
52
+ tokens.css --blog-* default theme, declared on .blog-root
53
+ base.css scoped reset + prose typography (isolation layer)
54
+ components.css every component's .bk-* BEM styling
55
+ index.css entrypoint: @imports the three above (in order)
56
+ icons.ts shared inline SVG strings
57
+ lib.ts shared prop logic (tone resolution, cx, …)
58
+ behaviors/ shared vanilla JS for interactive bits (data-bk-* hooks)
59
+ manifest/
60
+ components.json SINGLE source of truth — the MCP reads this
61
+ templates.json
62
+ frontmatter.json
63
+ react/ thin .tsx wrappers + blogComponents map (Next.js)
64
+ astro/ thin .astro wrappers (Astro)
65
+ demo/index.html static preview of components across themes
66
+ ```
67
+
68
+ A **component = look + wrapper.** The *look* (HTML structure + CSS) is framework-agnostic and lives in `core/`. The *wrapper* is a ~10-line shim per framework that emits the agreed markup. Restyle = edit `core/css` once → every framework + every site updates. Add a component = CSS once + a tiny wrapper in `react/` and `astro/` + a `manifest` entry.
69
+
70
+ ---
71
+
72
+ ## The build contract (every component MUST follow)
73
+
74
+ 1. **Styling lives only in `core/css/components.css`** as global `.bk-<name>` BEM classes. No Astro `<style>` blocks, no Tailwind, no inline `style=` with literal colours. This is what lets React and Astro render byte-identically.
75
+ 2. **Every value keys off a `--blog-*` token** (see `tokens.css`). Never hardcode a brand colour, font, radius, or shadow. A tenant re-skins by overriding tokens only.
76
+ 3. **Everything renders under `.blog-root`.** The base layer re-asserts inherited properties there, so the host page's CSS can't bleed in and the blog can't leak out. The only sanctioned inbound channel is the `--blog-*` token layer.
77
+ 4. **Shared logic goes in `core/lib.ts`; shared markup (icons) in `core/icons.ts`; shared interactivity in `core/behaviors/*.js`** (attached via `data-bk-*` attributes). Wrappers stay thin and identical across frameworks.
78
+ 5. **Both adapters emit the same tag, classes, and structure.** The React wrapper uses `className` + `dangerouslySetInnerHTML`; the Astro wrapper uses `class` + `set:html` + `<slot/>`. Nothing else should differ.
79
+ 6. **Renamed components keep an alias.** If a canonical name differs from what a live site already authored, add the old name to `aliasComponents` (React) / the Astro barrel and to the manifest entry's `aliases`, so existing published posts never break.
80
+ 7. **Add a `manifest/components.json` entry** (`description`, `props`, `example`, `role`, `recommendedFor`, `pack`, `frameworks`, `aliases`). The MCP surfaces this verbatim to authoring agents.
81
+
82
+ `CalloutBox` is the reference implementation of all seven rules — copy its pattern.
83
+
84
+ ## Packs
85
+
86
+ - **core** — universal components every site gets.
87
+ - **capture** / **hjd** — domain packs (review/SaaS vs SEO-agency) enabled per site via the MCP's `components` allowlist.
88
+ - per-site **custom** — code that lives in the site repo's overlay, merged into the render map; not in this kit.
89
+
90
+ ## Loading behaviours
91
+
92
+ The handful of interactive components (`TableOfContents`, `FAQList`, `ComparisonTable`, `CodeSnippet`) get their behaviour from framework-neutral vanilla JS in `core/behaviors/*.js`. The wrappers only render the markup + a `data-bk-*` hook; **the script must be loaded once per site** for the behaviour to attach. Each script auto-initialises on `DOMContentLoaded` (and is idempotent, so loading more than once is harmless).
93
+
94
+ Per-site wiring (handled by `sync-blog-kit.mjs` + the `/blog` route):
95
+ - **Next.js** — import the needed behaviours in the blog route/layout (`import "@blogkit/core/behaviors/toc.js"`), or emit `<script src="/_blogkit/behaviors/toc.js" defer>` in the post layout.
96
+ - **Astro** — add `<script src="/_blogkit/behaviors/toc.js"></script>` (or `import`) in the post layout.
97
+
98
+ Only load the behaviours for components a site actually uses. A behaviour with no matching markup on the page is a no-op.
99
+
100
+ ## Theming
101
+
102
+ A tenant's `theme.json` (in its content repo) is compiled to a stylesheet that overrides `--blog-*` tokens on `.blog-root`, loaded *after* `core/css/index.css`. Only brand colours + font families change; structure stays shared. See `demo/index.html` for the same components under the Capture (orange) and HJD (cyan) themes.
@@ -0,0 +1,64 @@
1
+ ---
2
+ interface Props {
3
+ platform?: "meta" | "google";
4
+ brand?: string;
5
+ sponsored?: string;
6
+ copy?: string;
7
+ image?: string;
8
+ buttonText?: string;
9
+ site?: string;
10
+ url?: string;
11
+ headline?: string;
12
+ description?: string;
13
+ sitelinks?: string[];
14
+ }
15
+
16
+ const {
17
+ platform = "meta",
18
+ brand,
19
+ sponsored = "Sponsored",
20
+ copy,
21
+ image,
22
+ site,
23
+ buttonText,
24
+ headline,
25
+ url,
26
+ description,
27
+ sitelinks,
28
+ } = Astro.props;
29
+ ---
30
+ {platform === "google" ? (
31
+ <div class="bk-ad-preview bk-ad-preview--google">
32
+ <div class="bk-ad-preview__g-head">
33
+ <span class="bk-ad-preview__g-tag">Ad</span>
34
+ <span class="bk-ad-preview__g-dot" aria-hidden="true">·</span>
35
+ <span class="bk-ad-preview__g-site">{site}</span>
36
+ </div>
37
+ <div class="bk-ad-preview__g-url" set:html={url || ""} />
38
+ <h4 class="bk-ad-preview__g-headline">{headline}</h4>
39
+ <p class="bk-ad-preview__g-desc">{description}</p>
40
+ {sitelinks && sitelinks.length > 0 && (
41
+ <div class="bk-ad-preview__g-sitelinks">
42
+ {sitelinks.map((s) => (
43
+ <span class="bk-ad-preview__g-sitelink">{s}</span>
44
+ ))}
45
+ </div>
46
+ )}
47
+ </div>
48
+ ) : (
49
+ <div class="bk-ad-preview">
50
+ <div class="bk-ad-preview__head">
51
+ <div class="bk-ad-preview__avatar">{(brand || "").slice(0, 2).toUpperCase()}</div>
52
+ <div class="bk-ad-preview__head-text">
53
+ <div class="bk-ad-preview__brand">{brand}</div>
54
+ <div class="bk-ad-preview__sponsored">{sponsored}</div>
55
+ </div>
56
+ </div>
57
+ <div class="bk-ad-preview__copy" set:html={copy || ""} />
58
+ <div class="bk-ad-preview__media">{image && <img src={image} alt="" />}</div>
59
+ <div class="bk-ad-preview__cta">
60
+ <div class="bk-ad-preview__site"><strong>{site}</strong></div>
61
+ <button class="bk-ad-preview__btn" type="button">{buttonText || "Learn more"}</button>
62
+ </div>
63
+ </div>
64
+ )}
@@ -0,0 +1,10 @@
1
+ ---
2
+ /* blog-kit Astro adapter — AdPreviewPair
3
+ Thin wrapper emitting the SAME markup/classes as the React adapter.
4
+ Styling: core/css/components.css. A layout container that lays two
5
+ ad-mockup cards side by side, collapsing to one column on narrow screens.
6
+ NOTE: deliberately no <style> block — the kit's CSS is shared/global,
7
+ not Astro-scoped, so React and Astro render byte-identically. */
8
+ ---
9
+
10
+ <div class="bk-ad-preview-pair"><slot /></div>
@@ -0,0 +1,29 @@
1
+ ---
2
+ interface AuditFindingRow {
3
+ title: string;
4
+ description?: string;
5
+ count?: string | number;
6
+ severity?: "high" | "med" | "low";
7
+ }
8
+
9
+ interface Props {
10
+ rows?: AuditFindingRow[];
11
+ }
12
+
13
+ const { rows = [] } = Astro.props;
14
+ ---
15
+ <div class="bk-audit-findings">
16
+ {rows.map((r) => (
17
+ <div class="bk-audit-findings__row">
18
+ <span
19
+ class={`bk-audit-findings__sev bk-audit-findings__sev--${r.severity || "med"}`}
20
+ aria-hidden="true"
21
+ />
22
+ <div class="bk-audit-findings__body">
23
+ <div class="bk-audit-findings__title">{r.title}</div>
24
+ {r.description && <div class="bk-audit-findings__desc">{r.description}</div>}
25
+ </div>
26
+ {r.count != null && <span class="bk-audit-findings__count">{r.count}</span>}
27
+ </div>
28
+ ))}
29
+ </div>
@@ -0,0 +1,60 @@
1
+ ---
2
+ interface AuditScoreItem {
3
+ label: string;
4
+ sub?: string;
5
+ score: number;
6
+ }
7
+
8
+ interface Props {
9
+ items?: AuditScoreItem[];
10
+ }
11
+
12
+ type Tone = "good" | "warn" | "bad";
13
+
14
+ /** Threshold ladder ported verbatim from the hjd source. */
15
+ function scoreTone(score: number): Tone {
16
+ if (score >= 80) return "good";
17
+ if (score >= 50) return "warn";
18
+ return "bad";
19
+ }
20
+
21
+ /** End point of the progress arc along the semicircle (r=40, cx=50, cy=50). */
22
+ function arcEnd(score: number): { x: string; y: string } {
23
+ const angle = Math.PI * (1 - score / 100);
24
+ return {
25
+ x: (50 - 40 * Math.cos(angle)).toFixed(2),
26
+ y: (50 - 40 * Math.sin(angle)).toFixed(2),
27
+ };
28
+ }
29
+
30
+ const { items = [] } = Astro.props;
31
+ ---
32
+ <div class="bk-audit-scores">
33
+ {items.map((it, i) => {
34
+ const tone = scoreTone(it.score);
35
+ const { x, y } = arcEnd(it.score);
36
+ return (
37
+ <div class={`bk-audit-scores__card bk-audit-scores__card--${tone}`}>
38
+ <svg class="bk-audit-scores__gauge" viewBox="0 0 100 60" aria-hidden="true">
39
+ <path
40
+ class="bk-audit-scores__track"
41
+ d="M10 50 A 40 40 0 0 1 90 50"
42
+ fill="none"
43
+ strokeWidth="8"
44
+ strokeLinecap="round"
45
+ />
46
+ <path
47
+ class="bk-audit-scores__arc"
48
+ d={`M10 50 A 40 40 0 0 1 ${x} ${y}`}
49
+ fill="none"
50
+ strokeWidth="8"
51
+ strokeLinecap="round"
52
+ />
53
+ </svg>
54
+ <div class="bk-audit-scores__num">{it.score}</div>
55
+ <div class="bk-audit-scores__label">{it.label}</div>
56
+ {it.sub && <div class="bk-audit-scores__sub">{it.sub}</div>}
57
+ </div>
58
+ );
59
+ })}
60
+ </div>
@@ -0,0 +1,32 @@
1
+ ---
2
+ import { authorInitials, type AuthorLink } from "../core/lib";
3
+ interface Props {
4
+ name: string;
5
+ role?: string;
6
+ avatar?: string;
7
+ bio?: string;
8
+ links?: AuthorLink[];
9
+ }
10
+ const { name, role, avatar, bio, links } = Astro.props;
11
+ ---
12
+ <div class="bk-author">
13
+ <div class="bk-author__media">
14
+ {avatar ? (
15
+ <img class="bk-author__avatar" src={avatar} alt={name} />
16
+ ) : (
17
+ <span class="bk-author__avatar bk-author__avatar--fallback" aria-hidden="true">{authorInitials(name)}</span>
18
+ )}
19
+ </div>
20
+ <div class="bk-author__body">
21
+ <p class="bk-author__name">{name}</p>
22
+ {role && <p class="bk-author__role">{role}</p>}
23
+ {bio && <p class="bk-author__bio">{bio}</p>}
24
+ {links && links.length > 0 && (
25
+ <div class="bk-author__links">
26
+ {links.map((l) => (
27
+ <a class="bk-author__link" href={l.href}>{l.label} &rarr;</a>
28
+ ))}
29
+ </div>
30
+ )}
31
+ </div>
32
+ </div>
@@ -0,0 +1,26 @@
1
+ ---
2
+ interface BeforeAfterSide {
3
+ title: string;
4
+ content: string;
5
+ value?: string;
6
+ }
7
+ interface Props {
8
+ before: BeforeAfterSide;
9
+ after: BeforeAfterSide;
10
+ }
11
+ const { before, after } = Astro.props;
12
+ ---
13
+ {before && after && (
14
+ <div class="bk-before-after">
15
+ <div class="bk-before-after__card bk-before-after__card--before">
16
+ <div class="bk-before-after__label">{before.title}</div>
17
+ {before.value && <div class="bk-before-after__value">{before.value}</div>}
18
+ <p class="bk-before-after__body">{before.content}</p>
19
+ </div>
20
+ <div class="bk-before-after__card bk-before-after__card--after">
21
+ <div class="bk-before-after__label">{after.title}</div>
22
+ {after.value && <div class="bk-before-after__value">{after.value}</div>}
23
+ <p class="bk-before-after__body">{after.content}</p>
24
+ </div>
25
+ </div>
26
+ )}
@@ -0,0 +1,15 @@
1
+ ---
2
+ /**
3
+ * Activates the kit's interactive components on an Astro site
4
+ * (TableOfContents scrollspy, FAQ JSON-LD, table scroll hints, code
5
+ * copy). Drop it once inside the `.blog-root` of the post layout — it
6
+ * renders only a module <script>. It runs on first load and again on
7
+ * `astro:page-load` so View-Transitions navigations re-wire the new
8
+ * post; each behaviour is idempotent, so re-running is safe.
9
+ */
10
+ ---
11
+ <script>
12
+ import { initBlogBehaviors } from "../core/behaviors/index.js";
13
+ initBlogBehaviors();
14
+ document.addEventListener("astro:page-load", initBlogBehaviors);
15
+ </script>
@@ -0,0 +1,28 @@
1
+ ---
2
+ import { ICONS } from "../core/icons";
3
+ interface Props {
4
+ heading?: string;
5
+ description?: string;
6
+ ctaText?: string;
7
+ ctaHref?: string;
8
+ }
9
+ const {
10
+ heading = "See it on your own site",
11
+ description = "Capture AI deploys in under ten minutes — paste a tag, point at your content, watch leads land.",
12
+ ctaText = "Book a demo",
13
+ ctaHref = "/book-demo",
14
+ } = Astro.props;
15
+ ---
16
+ <div class="bk-cta-banner">
17
+ <span class="bk-cta-banner__glow" aria-hidden="true"></span>
18
+ <div class="bk-cta-banner__inner">
19
+ <div class="bk-cta-banner__copy">
20
+ <h3 class="bk-cta-banner__heading">{heading}</h3>
21
+ <p class="bk-cta-banner__desc">{description}</p>
22
+ </div>
23
+ <a class="bk-cta-banner__cta" href={ctaHref}>
24
+ <span class="bk-cta-banner__cta-label">{ctaText}</span>
25
+ <span class="bk-cta-banner__cta-icon" aria-hidden="true" set:html={ICONS["arrowRight"]} />
26
+ </a>
27
+ </div>
28
+ </div>
@@ -0,0 +1,28 @@
1
+ ---
2
+ /* blog-kit Astro adapter — CalloutBox
3
+ Thin wrapper emitting the SAME markup/classes as the React adapter.
4
+ Styling: core/css/components.css. Logic: core/lib.ts. Icons: core/icons.ts.
5
+ NOTE: deliberately no <style> block — the kit's CSS is shared/global,
6
+ not Astro-scoped, so React and Astro render byte-identically. */
7
+ import { ICONS } from "../core/icons";
8
+ import { resolveCalloutTone, CALLOUT_DEFAULT_TITLE } from "../core/lib";
9
+
10
+ interface Props {
11
+ tone?: string;
12
+ type?: string;
13
+ variant?: string;
14
+ title?: string;
15
+ }
16
+
17
+ const { tone, type, variant, title } = Astro.props;
18
+ const t = resolveCalloutTone(tone ?? type ?? variant);
19
+ const heading = title ?? CALLOUT_DEFAULT_TITLE[t];
20
+ ---
21
+
22
+ <aside class={`bk-callout bk-callout--${t}`}>
23
+ <p class="bk-callout__title">
24
+ <span class="bk-callout__icon" set:html={ICONS[t]} />
25
+ {heading}
26
+ </p>
27
+ <div class="bk-callout__body"><slot /></div>
28
+ </aside>
@@ -0,0 +1,45 @@
1
+ ---
2
+ interface CaseStudyResult {
3
+ num: string;
4
+ label: string;
5
+ }
6
+
7
+ interface Props {
8
+ logoImage?: string;
9
+ logo?: string;
10
+ client?: string;
11
+ chips?: string[];
12
+ title?: string;
13
+ sub?: string;
14
+ results?: CaseStudyResult[];
15
+ }
16
+
17
+ const { logoImage, logo, client, chips = [], title, sub, results = [] } = Astro.props;
18
+ const logoHtml = logo || client?.replace(/ /g, "<br/>") || "";
19
+ ---
20
+ <div class="bk-case-study-hero">
21
+ <div class="bk-case-study-hero__logo">
22
+ {logoImage ? (
23
+ <img src={logoImage} alt={client ? `${client} logo` : "Client logo"} />
24
+ ) : (
25
+ <strong set:html={logoHtml} />
26
+ )}
27
+ </div>
28
+ <div class="bk-case-study-hero__body">
29
+ <div class="bk-case-study-hero__chips">
30
+ {chips.map((c) => (
31
+ <span class="bk-case-study-hero__chip">{c}</span>
32
+ ))}
33
+ </div>
34
+ <h2 class="bk-case-study-hero__heading">{title}</h2>
35
+ <p class="bk-case-study-hero__sub">{sub}</p>
36
+ <div class="bk-case-study-hero__results">
37
+ {results.map((r) => (
38
+ <div class="bk-case-study-hero__result">
39
+ <div class="bk-case-study-hero__result-num">{r.num}</div>
40
+ <div class="bk-case-study-hero__result-label">{r.label}</div>
41
+ </div>
42
+ ))}
43
+ </div>
44
+ </div>
45
+ </div>
@@ -0,0 +1,33 @@
1
+ ---
2
+ interface Row {
3
+ name: string;
4
+ value: number;
5
+ display?: string;
6
+ }
7
+ interface Props {
8
+ rows?: Row[];
9
+ total?: string;
10
+ period?: string;
11
+ }
12
+
13
+ const { rows = [], total, period } = Astro.props;
14
+ const max = Math.max(...rows.map((r) => Number(r.value) || 0), 1);
15
+ ---
16
+ <div class="bk-channel-mix">
17
+ <div class="bk-channel-mix__head">
18
+ <div class="bk-channel-mix__title">{period || "Channel split"}</div>
19
+ {total && <div class="bk-channel-mix__total">Total · {total}</div>}
20
+ </div>
21
+ {rows.map((r, i) => (
22
+ <div class="bk-channel-mix__row">
23
+ <div class="bk-channel-mix__name">{r.name}</div>
24
+ <div class="bk-channel-mix__bar">
25
+ <div
26
+ class={`bk-channel-mix__fill${i > 0 ? ` bk-channel-mix__fill--${Math.min(i + 1, 5)}` : ""}`}
27
+ style={`width: ${(Number(r.value) / max) * 100}%`}
28
+ />
29
+ </div>
30
+ <div class="bk-channel-mix__val">{r.display || r.value}</div>
31
+ </div>
32
+ ))}
33
+ </div>