@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.
- package/INTEGRATION.md +76 -0
- package/LICENSE +74 -0
- package/README.md +102 -0
- package/astro/AdPreview.astro +64 -0
- package/astro/AdPreviewPair.astro +10 -0
- package/astro/AuditFindings.astro +29 -0
- package/astro/AuditScores.astro +60 -0
- package/astro/AuthorCard.astro +32 -0
- package/astro/BeforeAfter.astro +26 -0
- package/astro/BlogBehaviors.astro +15 -0
- package/astro/CTABanner.astro +28 -0
- package/astro/CalloutBox.astro +28 -0
- package/astro/CaseStudyHero.astro +45 -0
- package/astro/ChannelMixBars.astro +33 -0
- package/astro/Checklist.astro +24 -0
- package/astro/ChecklistItem.astro +15 -0
- package/astro/CodeSnippet.astro +20 -0
- package/astro/ComparisonTable.astro +103 -0
- package/astro/Definition.astro +30 -0
- package/astro/DeliveryComparison.astro +40 -0
- package/astro/FAQList.astro +43 -0
- package/astro/FurtherReading.astro +34 -0
- package/astro/ImageFeature.astro +22 -0
- package/astro/Infographic.astro +12 -0
- package/astro/KeyMetric.astro +40 -0
- package/astro/KeywordTable.astro +69 -0
- package/astro/List.astro +46 -0
- package/astro/MetricHighlight.astro +77 -0
- package/astro/NewsletterCTA.astro +40 -0
- package/astro/NumberedCard.astro +6 -0
- package/astro/ProConBlock.astro +38 -0
- package/astro/ProseList.astro +46 -0
- package/astro/QuoteBlock.astro +72 -0
- package/astro/RegionCallout.astro +24 -0
- package/astro/RelatedPosts.astro +47 -0
- package/astro/ResultsStrip.astro +59 -0
- package/astro/ScoreBar.astro +19 -0
- package/astro/SerpPreview.astro +35 -0
- package/astro/ServicePromoCard.astro +21 -0
- package/astro/StatCard.astro +48 -0
- package/astro/StepBlock.astro +5 -0
- package/astro/TableOfContents.astro +12 -0
- package/astro/TimelineBlock.astro +30 -0
- package/astro/TipBox.astro +14 -0
- package/astro/TrafficChart.astro +61 -0
- package/astro/VerdictCard.astro +48 -0
- package/astro/blogkit/Article.astro +63 -0
- package/astro/blogkit/BlogIndex.astro +144 -0
- package/core/behaviors/code.js +78 -0
- package/core/behaviors/comparison.js +52 -0
- package/core/behaviors/delivery-comparison.js +52 -0
- package/core/behaviors/faq.js +61 -0
- package/core/behaviors/index.d.ts +3 -0
- package/core/behaviors/index.js +35 -0
- package/core/behaviors/keyword-table.js +52 -0
- package/core/behaviors/toc.js +130 -0
- package/core/css/base.css +146 -0
- package/core/css/components.css +2632 -0
- package/core/css/index-listing.css +207 -0
- package/core/css/index.css +13 -0
- package/core/css/tokens.css +127 -0
- package/core/icons.ts +20 -0
- package/core/lib.ts +70 -0
- package/core/manifest/components.json +573 -0
- package/core/manifest/frontmatter.json +19 -0
- package/core/manifest/templates.json +77 -0
- package/dist/core/behaviors/code.js +78 -0
- package/dist/core/behaviors/comparison.js +52 -0
- package/dist/core/behaviors/delivery-comparison.js +52 -0
- package/dist/core/behaviors/faq.js +61 -0
- package/dist/core/behaviors/index.d.ts +3 -0
- package/dist/core/behaviors/index.js +35 -0
- package/dist/core/behaviors/keyword-table.js +52 -0
- package/dist/core/behaviors/toc.js +130 -0
- package/dist/core/css/base.css +146 -0
- package/dist/core/css/components.css +2632 -0
- package/dist/core/css/index-listing.css +207 -0
- package/dist/core/css/index.css +13 -0
- package/dist/core/css/tokens.css +127 -0
- package/dist/core/icons.d.ts +2 -0
- package/dist/core/icons.d.ts.map +1 -0
- package/dist/core/icons.js +20 -0
- package/dist/core/icons.js.map +1 -0
- package/dist/core/lib.d.ts +21 -0
- package/dist/core/lib.d.ts.map +1 -0
- package/dist/core/lib.js +57 -0
- package/dist/core/lib.js.map +1 -0
- package/dist/core/manifest/components.json +573 -0
- package/dist/core/manifest/frontmatter.json +19 -0
- package/dist/core/manifest/templates.json +77 -0
- package/dist/package/adapters/hjd-api.d.ts +14 -0
- package/dist/package/adapters/hjd-api.d.ts.map +1 -0
- package/dist/package/adapters/hjd-api.js +57 -0
- package/dist/package/adapters/hjd-api.js.map +1 -0
- package/dist/package/adapters/index.d.ts +13 -0
- package/dist/package/adapters/index.d.ts.map +1 -0
- package/dist/package/adapters/index.js +16 -0
- package/dist/package/adapters/index.js.map +1 -0
- package/dist/package/adapters/local.d.ts +13 -0
- package/dist/package/adapters/local.d.ts.map +1 -0
- package/dist/package/adapters/local.js +72 -0
- package/dist/package/adapters/local.js.map +1 -0
- package/dist/package/adapters/source.d.ts +39 -0
- package/dist/package/adapters/source.d.ts.map +1 -0
- package/dist/package/adapters/source.js +19 -0
- package/dist/package/adapters/source.js.map +1 -0
- package/dist/package/article.d.ts +17 -0
- package/dist/package/article.d.ts.map +1 -0
- package/dist/package/article.js +37 -0
- package/dist/package/article.js.map +1 -0
- package/dist/package/astro/data.d.ts +45 -0
- package/dist/package/astro/data.d.ts.map +1 -0
- package/dist/package/astro/data.js +81 -0
- package/dist/package/astro/data.js.map +1 -0
- package/dist/package/astro/freshness.d.ts +11 -0
- package/dist/package/astro/freshness.d.ts.map +1 -0
- package/dist/package/astro/freshness.js +48 -0
- package/dist/package/astro/freshness.js.map +1 -0
- package/dist/package/astro/index.d.ts +12 -0
- package/dist/package/astro/index.d.ts.map +1 -0
- package/dist/package/astro/index.js +31 -0
- package/dist/package/astro/index.js.map +1 -0
- package/dist/package/blog-index.d.ts +10 -0
- package/dist/package/blog-index.d.ts.map +1 -0
- package/dist/package/blog-index.js +27 -0
- package/dist/package/blog-index.js.map +1 -0
- package/dist/package/cli/exchange.d.ts +27 -0
- package/dist/package/cli/exchange.d.ts.map +1 -0
- package/dist/package/cli/exchange.js +94 -0
- package/dist/package/cli/exchange.js.map +1 -0
- package/dist/package/cli/index.d.ts +3 -0
- package/dist/package/cli/index.d.ts.map +1 -0
- package/dist/package/cli/index.js +301 -0
- package/dist/package/cli/index.js.map +1 -0
- package/dist/package/config/define.d.ts +13 -0
- package/dist/package/config/define.d.ts.map +1 -0
- package/dist/package/config/define.js +14 -0
- package/dist/package/config/define.js.map +1 -0
- package/dist/package/config/resolve.d.ts +11 -0
- package/dist/package/config/resolve.d.ts.map +1 -0
- package/dist/package/config/resolve.js +43 -0
- package/dist/package/config/resolve.js.map +1 -0
- package/dist/package/config/types.d.ts +74 -0
- package/dist/package/config/types.d.ts.map +1 -0
- package/dist/package/config/types.js +13 -0
- package/dist/package/config/types.js.map +1 -0
- package/dist/package/index-core.d.ts +28 -0
- package/dist/package/index-core.d.ts.map +1 -0
- package/dist/package/index-core.js +102 -0
- package/dist/package/index-core.js.map +1 -0
- package/dist/package/index.d.ts +13 -0
- package/dist/package/index.d.ts.map +1 -0
- package/dist/package/index.js +25 -0
- package/dist/package/index.js.map +1 -0
- package/dist/package/mdx/render-astro.d.ts +18 -0
- package/dist/package/mdx/render-astro.d.ts.map +1 -0
- package/dist/package/mdx/render-astro.js +75 -0
- package/dist/package/mdx/render-astro.js.map +1 -0
- package/dist/package/mdx/render.d.ts +13 -0
- package/dist/package/mdx/render.d.ts.map +1 -0
- package/dist/package/mdx/render.js +37 -0
- package/dist/package/mdx/render.js.map +1 -0
- package/dist/react/AdPreview.d.ts +26 -0
- package/dist/react/AdPreview.d.ts.map +1 -0
- package/dist/react/AdPreview.js +8 -0
- package/dist/react/AdPreview.js.map +1 -0
- package/dist/react/AdPreviewPair.d.ts +7 -0
- package/dist/react/AdPreviewPair.d.ts.map +1 -0
- package/dist/react/AdPreviewPair.js +5 -0
- package/dist/react/AdPreviewPair.js.map +1 -0
- package/dist/react/AuditFindings.d.ts +14 -0
- package/dist/react/AuditFindings.d.ts.map +1 -0
- package/dist/react/AuditFindings.js +5 -0
- package/dist/react/AuditFindings.js.map +1 -0
- package/dist/react/AuditScores.d.ts +12 -0
- package/dist/react/AuditScores.d.ts.map +1 -0
- package/dist/react/AuditScores.js +25 -0
- package/dist/react/AuditScores.js.map +1 -0
- package/dist/react/AuthorCard.d.ts +10 -0
- package/dist/react/AuthorCard.d.ts.map +1 -0
- package/dist/react/AuthorCard.js +6 -0
- package/dist/react/AuthorCard.js.map +1 -0
- package/dist/react/BeforeAfter.d.ts +12 -0
- package/dist/react/BeforeAfter.d.ts.map +1 -0
- package/dist/react/BeforeAfter.js +7 -0
- package/dist/react/BeforeAfter.js.map +1 -0
- package/dist/react/BlogBehaviors.d.ts +10 -0
- package/dist/react/BlogBehaviors.d.ts.map +1 -0
- package/dist/react/BlogBehaviors.js +20 -0
- package/dist/react/BlogBehaviors.js.map +1 -0
- package/dist/react/CTABanner.d.ts +8 -0
- package/dist/react/CTABanner.d.ts.map +1 -0
- package/dist/react/CTABanner.js +9 -0
- package/dist/react/CTABanner.js.map +1 -0
- package/dist/react/CalloutBox.d.ts +13 -0
- package/dist/react/CalloutBox.d.ts.map +1 -0
- package/dist/react/CalloutBox.js +9 -0
- package/dist/react/CalloutBox.js.map +1 -0
- package/dist/react/CaseStudyHero.d.ts +20 -0
- package/dist/react/CaseStudyHero.d.ts.map +1 -0
- package/dist/react/CaseStudyHero.js +7 -0
- package/dist/react/CaseStudyHero.js.map +1 -0
- package/dist/react/ChannelMixBars.d.ts +18 -0
- package/dist/react/ChannelMixBars.d.ts.map +1 -0
- package/dist/react/ChannelMixBars.js +6 -0
- package/dist/react/ChannelMixBars.js.map +1 -0
- package/dist/react/Checklist.d.ts +10 -0
- package/dist/react/Checklist.d.ts.map +1 -0
- package/dist/react/Checklist.js +7 -0
- package/dist/react/Checklist.js.map +1 -0
- package/dist/react/ChecklistItem.d.ts +7 -0
- package/dist/react/ChecklistItem.d.ts.map +1 -0
- package/dist/react/ChecklistItem.js +5 -0
- package/dist/react/ChecklistItem.js.map +1 -0
- package/dist/react/CodeSnippet.d.ts +17 -0
- package/dist/react/CodeSnippet.d.ts.map +1 -0
- package/dist/react/CodeSnippet.js +14 -0
- package/dist/react/CodeSnippet.js.map +1 -0
- package/dist/react/ComparisonTable.d.ts +22 -0
- package/dist/react/ComparisonTable.d.ts.map +1 -0
- package/dist/react/ComparisonTable.js +35 -0
- package/dist/react/ComparisonTable.js.map +1 -0
- package/dist/react/Definition.d.ts +9 -0
- package/dist/react/Definition.d.ts.map +1 -0
- package/dist/react/Definition.js +19 -0
- package/dist/react/Definition.js.map +1 -0
- package/dist/react/DeliveryComparison.d.ts +16 -0
- package/dist/react/DeliveryComparison.d.ts.map +1 -0
- package/dist/react/DeliveryComparison.js +7 -0
- package/dist/react/DeliveryComparison.js.map +1 -0
- package/dist/react/FAQList.d.ts +20 -0
- package/dist/react/FAQList.d.ts.map +1 -0
- package/dist/react/FAQList.js +19 -0
- package/dist/react/FAQList.js.map +1 -0
- package/dist/react/FurtherReading.d.ts +21 -0
- package/dist/react/FurtherReading.d.ts.map +1 -0
- package/dist/react/FurtherReading.js +13 -0
- package/dist/react/FurtherReading.js.map +1 -0
- package/dist/react/ImageFeature.d.ts +9 -0
- package/dist/react/ImageFeature.d.ts.map +1 -0
- package/dist/react/ImageFeature.js +6 -0
- package/dist/react/ImageFeature.js.map +1 -0
- package/dist/react/Infographic.d.ts +6 -0
- package/dist/react/Infographic.d.ts.map +1 -0
- package/dist/react/Infographic.js +7 -0
- package/dist/react/Infographic.js.map +1 -0
- package/dist/react/KeyMetric.d.ts +16 -0
- package/dist/react/KeyMetric.d.ts.map +1 -0
- package/dist/react/KeyMetric.js +15 -0
- package/dist/react/KeyMetric.js.map +1 -0
- package/dist/react/KeywordTable.d.ts +18 -0
- package/dist/react/KeywordTable.d.ts.map +1 -0
- package/dist/react/KeywordTable.js +23 -0
- package/dist/react/KeywordTable.js.map +1 -0
- package/dist/react/List.d.ts +11 -0
- package/dist/react/List.d.ts.map +1 -0
- package/dist/react/List.js +21 -0
- package/dist/react/List.js.map +1 -0
- package/dist/react/MetricHighlight.d.ts +15 -0
- package/dist/react/MetricHighlight.d.ts.map +1 -0
- package/dist/react/MetricHighlight.js +26 -0
- package/dist/react/MetricHighlight.js.map +1 -0
- package/dist/react/NewsletterCTA.d.ts +9 -0
- package/dist/react/NewsletterCTA.d.ts.map +1 -0
- package/dist/react/NewsletterCTA.js +5 -0
- package/dist/react/NewsletterCTA.js.map +1 -0
- package/dist/react/NumberedCard.d.ts +9 -0
- package/dist/react/NumberedCard.d.ts.map +1 -0
- package/dist/react/NumberedCard.js +7 -0
- package/dist/react/NumberedCard.js.map +1 -0
- package/dist/react/ProConBlock.d.ts +6 -0
- package/dist/react/ProConBlock.d.ts.map +1 -0
- package/dist/react/ProConBlock.js +7 -0
- package/dist/react/ProConBlock.js.map +1 -0
- package/dist/react/ProseList.d.ts +17 -0
- package/dist/react/ProseList.d.ts.map +1 -0
- package/dist/react/ProseList.js +26 -0
- package/dist/react/ProseList.js.map +1 -0
- package/dist/react/QuoteBlock.d.ts +17 -0
- package/dist/react/QuoteBlock.d.ts.map +1 -0
- package/dist/react/QuoteBlock.js +26 -0
- package/dist/react/QuoteBlock.js.map +1 -0
- package/dist/react/RegionCallout.d.ts +13 -0
- package/dist/react/RegionCallout.d.ts.map +1 -0
- package/dist/react/RegionCallout.js +5 -0
- package/dist/react/RegionCallout.js.map +1 -0
- package/dist/react/RelatedPosts.d.ts +20 -0
- package/dist/react/RelatedPosts.d.ts.map +1 -0
- package/dist/react/RelatedPosts.js +7 -0
- package/dist/react/RelatedPosts.js.map +1 -0
- package/dist/react/ResultsStrip.d.ts +18 -0
- package/dist/react/ResultsStrip.d.ts.map +1 -0
- package/dist/react/ResultsStrip.js +22 -0
- package/dist/react/ResultsStrip.js.map +1 -0
- package/dist/react/ScoreBar.d.ts +8 -0
- package/dist/react/ScoreBar.d.ts.map +1 -0
- package/dist/react/ScoreBar.js +6 -0
- package/dist/react/ScoreBar.js.map +1 -0
- package/dist/react/SerpPreview.d.ts +18 -0
- package/dist/react/SerpPreview.d.ts.map +1 -0
- package/dist/react/SerpPreview.js +13 -0
- package/dist/react/SerpPreview.js.map +1 -0
- package/dist/react/ServicePromoCard.d.ts +14 -0
- package/dist/react/ServicePromoCard.d.ts.map +1 -0
- package/dist/react/ServicePromoCard.js +12 -0
- package/dist/react/ServicePromoCard.js.map +1 -0
- package/dist/react/StatCard.d.ts +13 -0
- package/dist/react/StatCard.d.ts.map +1 -0
- package/dist/react/StatCard.js +20 -0
- package/dist/react/StatCard.js.map +1 -0
- package/dist/react/StepBlock.d.ts +8 -0
- package/dist/react/StepBlock.d.ts.map +1 -0
- package/dist/react/StepBlock.js +5 -0
- package/dist/react/StepBlock.js.map +1 -0
- package/dist/react/TableOfContents.d.ts +14 -0
- package/dist/react/TableOfContents.d.ts.map +1 -0
- package/dist/react/TableOfContents.js +12 -0
- package/dist/react/TableOfContents.js.map +1 -0
- package/dist/react/TimelineBlock.d.ts +14 -0
- package/dist/react/TimelineBlock.d.ts.map +1 -0
- package/dist/react/TimelineBlock.js +8 -0
- package/dist/react/TimelineBlock.js.map +1 -0
- package/dist/react/TipBox.d.ts +6 -0
- package/dist/react/TipBox.d.ts.map +1 -0
- package/dist/react/TipBox.js +5 -0
- package/dist/react/TipBox.js.map +1 -0
- package/dist/react/TrafficChart.d.ts +16 -0
- package/dist/react/TrafficChart.d.ts.map +1 -0
- package/dist/react/TrafficChart.js +14 -0
- package/dist/react/TrafficChart.js.map +1 -0
- package/dist/react/VerdictCard.d.ts +15 -0
- package/dist/react/VerdictCard.d.ts.map +1 -0
- package/dist/react/VerdictCard.js +5 -0
- package/dist/react/VerdictCard.js.map +1 -0
- package/dist/react/components-map.d.ts +133 -0
- package/dist/react/components-map.d.ts.map +1 -0
- package/dist/react/components-map.js +120 -0
- package/dist/react/components-map.js.map +1 -0
- package/dist/react/index.d.ts +5 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +13 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +116 -0
- package/react/AdPreview.tsx +94 -0
- package/react/AdPreviewPair.tsx +16 -0
- package/react/AuditFindings.tsx +43 -0
- package/react/AuditScores.tsx +73 -0
- package/react/AuthorCard.tsx +35 -0
- package/react/BeforeAfter.tsx +27 -0
- package/react/BlogBehaviors.tsx +21 -0
- package/react/CTABanner.tsx +32 -0
- package/react/CalloutBox.tsx +31 -0
- package/react/CaseStudyHero.tsx +71 -0
- package/react/ChannelMixBars.tsx +50 -0
- package/react/Checklist.tsx +31 -0
- package/react/ChecklistItem.tsx +19 -0
- package/react/CodeSnippet.tsx +36 -0
- package/react/ComparisonTable.tsx +114 -0
- package/react/Definition.tsx +36 -0
- package/react/DeliveryComparison.tsx +62 -0
- package/react/FAQList.tsx +61 -0
- package/react/FurtherReading.tsx +46 -0
- package/react/ImageFeature.tsx +26 -0
- package/react/Infographic.tsx +18 -0
- package/react/KeyMetric.tsx +61 -0
- package/react/KeywordTable.tsx +92 -0
- package/react/List.tsx +58 -0
- package/react/MetricHighlight.tsx +86 -0
- package/react/NewsletterCTA.tsx +48 -0
- package/react/NumberedCard.tsx +7 -0
- package/react/ProConBlock.tsx +42 -0
- package/react/ProseList.tsx +72 -0
- package/react/QuoteBlock.tsx +89 -0
- package/react/RegionCallout.tsx +38 -0
- package/react/RelatedPosts.tsx +58 -0
- package/react/ResultsStrip.tsx +77 -0
- package/react/ScoreBar.tsx +27 -0
- package/react/SerpPreview.tsx +59 -0
- package/react/ServicePromoCard.tsx +43 -0
- package/react/StatCard.tsx +62 -0
- package/react/StepBlock.tsx +5 -0
- package/react/TableOfContents.tsx +27 -0
- package/react/TimelineBlock.tsx +35 -0
- package/react/TipBox.tsx +16 -0
- package/react/TrafficChart.tsx +79 -0
- package/react/VerdictCard.tsx +60 -0
- package/react/components-map.ts +122 -0
- package/react/index.ts +13 -0
- package/templates/blogkit/app/api/blogkit/revalidate/route.ts.tmpl +32 -0
- package/templates/blogkit/app/blog/[slug]/page.tsx.tmpl +41 -0
- package/templates/blogkit/app/blog/page.tsx.tmpl +18 -0
- package/templates/blogkit/blogkit.config.ts.tmpl +23 -0
- package/templates/blogkit-astro/BLOGKIT_ASTRO_SETUP.md.tmpl +49 -0
- package/templates/blogkit-astro/src/blogkit.config.ts.tmpl +29 -0
- package/templates/blogkit-astro/src/pages/api/blogkit/revalidate.ts.tmpl +46 -0
- package/templates/blogkit-astro/src/pages/blog/[slug].astro.tmpl +39 -0
- 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} →</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>
|