@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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
pros?: string[];
|
|
4
|
+
cons?: string[];
|
|
5
|
+
}
|
|
6
|
+
const { pros = [], cons = [] } = Astro.props;
|
|
7
|
+
const hasAny = (pros?.length ?? 0) > 0 || (cons?.length ?? 0) > 0;
|
|
8
|
+
---
|
|
9
|
+
{hasAny && (
|
|
10
|
+
<div class="bk-procon">
|
|
11
|
+
{pros.length > 0 && (
|
|
12
|
+
<div class="bk-procon__card bk-procon__card--pro" data-kind="pro">
|
|
13
|
+
<h4 class="bk-procon__heading">Pros</h4>
|
|
14
|
+
<ul class="bk-procon__list">
|
|
15
|
+
{pros.map((item) => (
|
|
16
|
+
<li class="bk-procon__item">
|
|
17
|
+
<span class="bk-procon__mark" aria-hidden="true">✓</span>
|
|
18
|
+
<span class="bk-procon__text">{item}</span>
|
|
19
|
+
</li>
|
|
20
|
+
))}
|
|
21
|
+
</ul>
|
|
22
|
+
</div>
|
|
23
|
+
)}
|
|
24
|
+
{cons.length > 0 && (
|
|
25
|
+
<div class="bk-procon__card bk-procon__card--con" data-kind="con">
|
|
26
|
+
<h4 class="bk-procon__heading">Cons</h4>
|
|
27
|
+
<ul class="bk-procon__list">
|
|
28
|
+
{cons.map((item) => (
|
|
29
|
+
<li class="bk-procon__item">
|
|
30
|
+
<span class="bk-procon__mark" aria-hidden="true">✕</span>
|
|
31
|
+
<span class="bk-procon__text">{item}</span>
|
|
32
|
+
</li>
|
|
33
|
+
))}
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { cx } from "../core/lib";
|
|
3
|
+
|
|
4
|
+
interface ProseListItem {
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
interface Props {
|
|
9
|
+
variant?: "ul" | "ol";
|
|
10
|
+
items?: Array<string | ProseListItem>;
|
|
11
|
+
divided?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { variant = "ul", items = [], divided = false } = Astro.props;
|
|
15
|
+
const list = Array.isArray(items) ? items : [];
|
|
16
|
+
const v: "ul" | "ol" = variant === "ol" ? "ol" : "ul";
|
|
17
|
+
const Tag = v === "ol" ? "ol" : "ul";
|
|
18
|
+
const isGrid = list.some(
|
|
19
|
+
(it) => it && typeof it === "object" && (it.title || it.description)
|
|
20
|
+
);
|
|
21
|
+
const cls = cx(
|
|
22
|
+
"bk-prose-list",
|
|
23
|
+
`bk-prose-list--${v}`,
|
|
24
|
+
divided && "bk-prose-list--divided",
|
|
25
|
+
isGrid && "bk-prose-list--grid"
|
|
26
|
+
);
|
|
27
|
+
---
|
|
28
|
+
{list.length > 0 && (
|
|
29
|
+
<Tag class={cls}>
|
|
30
|
+
{list.map((item) => {
|
|
31
|
+
if (isGrid) {
|
|
32
|
+
const obj = item && typeof item === "object" ? item : { description: String(item ?? "") };
|
|
33
|
+
return (
|
|
34
|
+
<li class="bk-prose-list__item">
|
|
35
|
+
<span class="bk-prose-list__marker" aria-hidden="true" />
|
|
36
|
+
<span class="bk-prose-list__title" set:html={obj.title || ""} />
|
|
37
|
+
<span class="bk-prose-list__desc" set:html={obj.description || ""} />
|
|
38
|
+
</li>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return (
|
|
42
|
+
<li class="bk-prose-list__item" set:html={typeof item === "string" ? item : ""} />
|
|
43
|
+
);
|
|
44
|
+
})}
|
|
45
|
+
</Tag>
|
|
46
|
+
)}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ICONS } from "../core/icons";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
quote?: string;
|
|
6
|
+
author?: string;
|
|
7
|
+
role?: string;
|
|
8
|
+
company?: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
sourceUrl?: string;
|
|
11
|
+
avatarUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Google's public favicon-resolver URL for the source URL's host, or null when
|
|
16
|
+
* the URL is missing/unparseable so the avatar slot can be dropped entirely.
|
|
17
|
+
*/
|
|
18
|
+
function deriveFaviconUrl(sourceUrl?: string): string | null {
|
|
19
|
+
if (!sourceUrl) return null;
|
|
20
|
+
try {
|
|
21
|
+
const host = new URL(sourceUrl).hostname;
|
|
22
|
+
if (!host) return null;
|
|
23
|
+
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(host)}&sz=64`;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { quote, author, role, company, source, sourceUrl, avatarUrl } = Astro.props;
|
|
30
|
+
const subtitle = [role, company].filter(Boolean).join(" · ");
|
|
31
|
+
const avatarSrc = avatarUrl ?? deriveFaviconUrl(sourceUrl);
|
|
32
|
+
---
|
|
33
|
+
<figure class="bk-quote">
|
|
34
|
+
<span class="bk-quote__mark" aria-hidden>“</span>
|
|
35
|
+
<blockquote class="bk-quote__body">{quote ?? <slot />}</blockquote>
|
|
36
|
+
{(author || subtitle) && (
|
|
37
|
+
<figcaption class="bk-quote__cite">
|
|
38
|
+
{avatarSrc && (
|
|
39
|
+
<img
|
|
40
|
+
class="bk-quote__avatar"
|
|
41
|
+
src={avatarSrc}
|
|
42
|
+
alt=""
|
|
43
|
+
aria-hidden
|
|
44
|
+
width={40}
|
|
45
|
+
height={40}
|
|
46
|
+
/>
|
|
47
|
+
)}
|
|
48
|
+
<span class="bk-quote__who">
|
|
49
|
+
{author && <cite class="bk-quote__author">{author}</cite>}
|
|
50
|
+
{subtitle && <span class="bk-quote__role">{subtitle}</span>}
|
|
51
|
+
</span>
|
|
52
|
+
</figcaption>
|
|
53
|
+
)}
|
|
54
|
+
{source &&
|
|
55
|
+
(sourceUrl ? (
|
|
56
|
+
<a
|
|
57
|
+
class="bk-quote__source bk-quote__source--link"
|
|
58
|
+
href={sourceUrl}
|
|
59
|
+
target="_blank"
|
|
60
|
+
rel="nofollow noopener noreferrer"
|
|
61
|
+
>
|
|
62
|
+
<span class="bk-quote__source-kicker">Source</span>
|
|
63
|
+
{source}
|
|
64
|
+
<span class="bk-quote__source-icon" set:html={ICONS["external"]} />
|
|
65
|
+
</a>
|
|
66
|
+
) : (
|
|
67
|
+
<div class="bk-quote__source">
|
|
68
|
+
<span class="bk-quote__source-kicker">Source</span>
|
|
69
|
+
{source}
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
</figure>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Office {
|
|
3
|
+
name: string;
|
|
4
|
+
phone?: string;
|
|
5
|
+
flag?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
offices?: Office[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { offices = [] } = Astro.props;
|
|
13
|
+
---
|
|
14
|
+
<div class="bk-region-callout">
|
|
15
|
+
{offices.map((o) => (
|
|
16
|
+
<div class="bk-region-callout__cell">
|
|
17
|
+
<div class="bk-region-callout__flag" {...(o.flag ? { style: `background:${o.flag}` } : {})} />
|
|
18
|
+
<div class="bk-region-callout__info">
|
|
19
|
+
<div class="bk-region-callout__name">{o.name}</div>
|
|
20
|
+
{o.phone && <div class="bk-region-callout__phone">{o.phone}</div>}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
))}
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface RelatedPost {
|
|
3
|
+
slug: string;
|
|
4
|
+
title: string;
|
|
5
|
+
excerpt?: string;
|
|
6
|
+
date?: string;
|
|
7
|
+
category?: string;
|
|
8
|
+
featuredImage?: string;
|
|
9
|
+
featuredImageAlt?: string;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
posts: RelatedPost[];
|
|
13
|
+
heading?: string;
|
|
14
|
+
hrefBase?: string;
|
|
15
|
+
}
|
|
16
|
+
const { posts, heading = "Keep reading", hrefBase = "/blog/" } = Astro.props;
|
|
17
|
+
---
|
|
18
|
+
{posts && posts.length > 0 && (
|
|
19
|
+
<div class="bk-related">
|
|
20
|
+
<p class="bk-related__eyebrow">{heading}</p>
|
|
21
|
+
<div class="bk-related__grid">
|
|
22
|
+
{posts.map((post, i) => (
|
|
23
|
+
<a class="bk-related__card" href={`${hrefBase}${post.slug}`}>
|
|
24
|
+
<div
|
|
25
|
+
class={`bk-related__thumb${post.featuredImage ? "" : ` bk-related__thumb--g${(i % 3) + 1}`}`}
|
|
26
|
+
aria-hidden={post.featuredImage ? undefined : true}
|
|
27
|
+
>
|
|
28
|
+
{post.featuredImage && (
|
|
29
|
+
<img
|
|
30
|
+
class="bk-related__img"
|
|
31
|
+
src={post.featuredImage}
|
|
32
|
+
alt={post.featuredImageAlt || ""}
|
|
33
|
+
loading="lazy"
|
|
34
|
+
decoding="async"
|
|
35
|
+
/>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
<div class="bk-related__body">
|
|
39
|
+
{post.category && <span class="bk-related__cat">{post.category}</span>}
|
|
40
|
+
<h5 class="bk-related__title">{post.title}</h5>
|
|
41
|
+
{post.excerpt && <p class="bk-related__excerpt">{post.excerpt}</p>}
|
|
42
|
+
</div>
|
|
43
|
+
</a>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ICONS } from "../core/icons";
|
|
3
|
+
|
|
4
|
+
interface Kpi {
|
|
5
|
+
label: string;
|
|
6
|
+
num: string;
|
|
7
|
+
delta?: string;
|
|
8
|
+
deltaDirection?: "up" | "down";
|
|
9
|
+
source?: string;
|
|
10
|
+
sourceUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
kpis?: Kpi[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { kpis = [] } = Astro.props;
|
|
18
|
+
---
|
|
19
|
+
{kpis && kpis.length > 0 && (
|
|
20
|
+
<div class="bk-results-strip">
|
|
21
|
+
{kpis.map((k) => (
|
|
22
|
+
<div class="bk-results-strip__cell">
|
|
23
|
+
<div class="bk-results-strip__label">{k.label}</div>
|
|
24
|
+
<div class="bk-results-strip__num">{k.num}</div>
|
|
25
|
+
{k.delta && (
|
|
26
|
+
<div
|
|
27
|
+
class={`bk-results-strip__delta${
|
|
28
|
+
k.deltaDirection === "down" ? " bk-results-strip__delta--down" : ""
|
|
29
|
+
}`}
|
|
30
|
+
>
|
|
31
|
+
{k.delta}
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
{k.source &&
|
|
35
|
+
(k.sourceUrl ? (
|
|
36
|
+
<a
|
|
37
|
+
class="bk-results-strip__source bk-results-strip__source--link"
|
|
38
|
+
href={k.sourceUrl}
|
|
39
|
+
target="_blank"
|
|
40
|
+
rel="nofollow noopener noreferrer"
|
|
41
|
+
>
|
|
42
|
+
<span class="bk-results-strip__source-kicker">Source</span>
|
|
43
|
+
{k.source}
|
|
44
|
+
<span
|
|
45
|
+
class="bk-results-strip__source-icon"
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
set:html={ICONS["external"]}
|
|
48
|
+
/>
|
|
49
|
+
</a>
|
|
50
|
+
) : (
|
|
51
|
+
<div class="bk-results-strip__source">
|
|
52
|
+
<span class="bk-results-strip__source-kicker">Source</span>
|
|
53
|
+
{k.source}
|
|
54
|
+
</div>
|
|
55
|
+
))}
|
|
56
|
+
</div>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
label: string;
|
|
4
|
+
score: number;
|
|
5
|
+
maxScore?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { label, score, maxScore = 10 } = Astro.props;
|
|
9
|
+
const percentage = Math.max(0, Math.min(100, (score / maxScore) * 100));
|
|
10
|
+
---
|
|
11
|
+
<div class="bk-score-bar">
|
|
12
|
+
<div class="bk-score-bar__head">
|
|
13
|
+
<span class="bk-score-bar__label">{label}</span>
|
|
14
|
+
<span class="bk-score-bar__score">{score}</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="bk-score-bar__track">
|
|
17
|
+
<div class="bk-score-bar__fill" style={`width: ${percentage}%`} />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ICONS } from "../core/icons";
|
|
3
|
+
|
|
4
|
+
interface SerpResult {
|
|
5
|
+
title: string;
|
|
6
|
+
url: string;
|
|
7
|
+
description: string;
|
|
8
|
+
ad?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
query: string;
|
|
13
|
+
results?: SerpResult[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { query, results = [] } = Astro.props;
|
|
17
|
+
---
|
|
18
|
+
<div class="bk-serp-preview">
|
|
19
|
+
<div class="bk-serp-preview__bar">
|
|
20
|
+
<span class="bk-serp-preview__bar-icon" aria-hidden="true" set:html={ICONS["search"]} />
|
|
21
|
+
<input class="bk-serp-preview__input" value={query} readonly />
|
|
22
|
+
</div>
|
|
23
|
+
<div class="bk-serp-preview__results">
|
|
24
|
+
{results.map((r) => (
|
|
25
|
+
<div class="bk-serp-preview__result">
|
|
26
|
+
{r.ad && (
|
|
27
|
+
<span class="bk-serp-preview__tag bk-serp-preview__tag--ad">Sponsored</span>
|
|
28
|
+
)}
|
|
29
|
+
<span class="bk-serp-preview__url" set:html={r.url} />
|
|
30
|
+
<h4 class="bk-serp-preview__heading">{r.title}</h4>
|
|
31
|
+
<p class="bk-serp-preview__desc" set:html={r.description} />
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ICONS } from "../core/icons";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
title: string;
|
|
6
|
+
sub?: string;
|
|
7
|
+
href?: string;
|
|
8
|
+
icon?: string;
|
|
9
|
+
arrowLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { title, sub, href = "#", icon, arrowLabel = "Learn more →" } = Astro.props;
|
|
13
|
+
---
|
|
14
|
+
<a class="bk-service-promo-card" href={href}>
|
|
15
|
+
<span class="bk-service-promo-card__icon" aria-hidden="true" set:html={icon || ICONS["search"]} />
|
|
16
|
+
<span class="bk-service-promo-card__body">
|
|
17
|
+
<span class="bk-service-promo-card__title">{title}</span>
|
|
18
|
+
{sub && <span class="bk-service-promo-card__sub">{sub}</span>}
|
|
19
|
+
</span>
|
|
20
|
+
<span class="bk-service-promo-card__arrow">{arrowLabel}</span>
|
|
21
|
+
</a>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ICONS } from "../core/icons";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
unit?: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
sourceUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Split a raw value like "3.2×" into its numeric head and trailing unit. */
|
|
14
|
+
function splitValue(raw: string): { num: string; unit: string } {
|
|
15
|
+
const m = raw.match(/^([\d.,\-+]+)(.*)$/);
|
|
16
|
+
if (!m) return { num: raw, unit: "" };
|
|
17
|
+
return { num: m[1], unit: m[2].trim() };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { value, label, description, unit, source, sourceUrl } = Astro.props;
|
|
21
|
+
const parts = unit !== undefined ? { num: value, unit } : splitValue(value);
|
|
22
|
+
---
|
|
23
|
+
<div class="bk-stat-card">
|
|
24
|
+
<div class="bk-stat-card__value">
|
|
25
|
+
<span class="bk-stat-card__num">{parts.num}</span>
|
|
26
|
+
{parts.unit && <span class="bk-stat-card__unit">{parts.unit}</span>}
|
|
27
|
+
</div>
|
|
28
|
+
<div class="bk-stat-card__label">{label}</div>
|
|
29
|
+
{description && <div class="bk-stat-card__desc">{description}</div>}
|
|
30
|
+
{source &&
|
|
31
|
+
(sourceUrl ? (
|
|
32
|
+
<a
|
|
33
|
+
class="bk-stat-card__source bk-stat-card__source--link"
|
|
34
|
+
href={sourceUrl}
|
|
35
|
+
target="_blank"
|
|
36
|
+
rel="nofollow noopener noreferrer"
|
|
37
|
+
>
|
|
38
|
+
<span class="bk-stat-card__source-kicker">Source</span>
|
|
39
|
+
{source}
|
|
40
|
+
<span class="bk-stat-card__source-icon" aria-hidden="true" set:html={ICONS["external"]} />
|
|
41
|
+
</a>
|
|
42
|
+
) : (
|
|
43
|
+
<div class="bk-stat-card__source">
|
|
44
|
+
<span class="bk-stat-card__source-kicker">Source</span>
|
|
45
|
+
{source}
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props { stepNumber?: number | string; title?: string; }
|
|
3
|
+
const { stepNumber, title } = Astro.props;
|
|
4
|
+
---
|
|
5
|
+
<div class="bk-step"><div class="bk-step__num">{stepNumber}</div><div class="bk-step__main">{title ? <h3 class="bk-step__title">{title}</h3> : null}<div class="bk-step__body"><slot /></div></div></div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
heading?: string;
|
|
4
|
+
scope?: string;
|
|
5
|
+
}
|
|
6
|
+
const { heading, scope } = Astro.props;
|
|
7
|
+
const label = heading ?? "In this article";
|
|
8
|
+
---
|
|
9
|
+
<nav class="bk-toc" data-bk-toc data-bk-toc-scope={scope ?? "article"} aria-label={label}>
|
|
10
|
+
<p class="bk-toc__heading">{label}</p>
|
|
11
|
+
<ul class="bk-toc__list"></ul>
|
|
12
|
+
</nav>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
/* blog-kit Astro adapter — TimelineBlock
|
|
3
|
+
Thin wrapper emitting the SAME markup/classes as the React adapter.
|
|
4
|
+
Styling: core/css/components.css. No <style> block — the kit's CSS is
|
|
5
|
+
shared/global so React and Astro render byte-identically. */
|
|
6
|
+
interface TimelineEvent {
|
|
7
|
+
date: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
events?: TimelineEvent[];
|
|
13
|
+
}
|
|
14
|
+
const { events = [] } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
{events.length > 0 ? (
|
|
17
|
+
<div class="bk-timeline">
|
|
18
|
+
<span class="bk-timeline__rail" aria-hidden="true" />
|
|
19
|
+
{events.map((event) => (
|
|
20
|
+
<div class="bk-timeline__item">
|
|
21
|
+
<span class="bk-timeline__dot" aria-hidden="true" />
|
|
22
|
+
<div class="bk-timeline__date">{event.date}</div>
|
|
23
|
+
<h4 class="bk-timeline__title">{event.title}</h4>
|
|
24
|
+
<p class="bk-timeline__desc">{event.description}</p>
|
|
25
|
+
</div>
|
|
26
|
+
))}
|
|
27
|
+
</div>
|
|
28
|
+
) : (
|
|
29
|
+
<div class="bk-timeline"><slot /></div>
|
|
30
|
+
)}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
tip: string;
|
|
4
|
+
context?: string;
|
|
5
|
+
}
|
|
6
|
+
const { tip, context } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
<div class="bk-tip">
|
|
9
|
+
<span class="bk-tip__badge">Pro tip</span>
|
|
10
|
+
<p class="bk-tip__body">
|
|
11
|
+
<strong class="bk-tip__lead">{tip}</strong>
|
|
12
|
+
{context ? <span class="bk-tip__context"> {context}</span> : null}
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Series {
|
|
3
|
+
current?: number[];
|
|
4
|
+
previous?: number[];
|
|
5
|
+
}
|
|
6
|
+
interface Props {
|
|
7
|
+
title?: string;
|
|
8
|
+
series?: Series;
|
|
9
|
+
labels?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const W = 600;
|
|
13
|
+
const H = 220;
|
|
14
|
+
const PAD = 40;
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
title = "Organic sessions · 6 months",
|
|
18
|
+
series = {},
|
|
19
|
+
labels = [],
|
|
20
|
+
} = Astro.props;
|
|
21
|
+
|
|
22
|
+
const current = series.current || [];
|
|
23
|
+
const previous = series.previous || [];
|
|
24
|
+
const max = Math.max(...current, ...previous, 1);
|
|
25
|
+
const xs = (i: number, len: number) =>
|
|
26
|
+
PAD + 20 + ((W - PAD - 30) * i) / Math.max(len - 1, 1);
|
|
27
|
+
const ys = (v: number) => H - 30 - ((H - 50) * v) / max;
|
|
28
|
+
const points = (arr: number[]) =>
|
|
29
|
+
arr.map((v, i) => `${xs(i, arr.length)},${ys(v)}`).join(" ");
|
|
30
|
+
---
|
|
31
|
+
<div class="bk-traffic-chart">
|
|
32
|
+
<div class="bk-traffic-chart__head">
|
|
33
|
+
<div class="bk-traffic-chart__title">{title}</div>
|
|
34
|
+
<div class="bk-traffic-chart__legend">
|
|
35
|
+
<span class="bk-traffic-chart__item">
|
|
36
|
+
<span class="bk-traffic-chart__dot" />This period
|
|
37
|
+
</span>
|
|
38
|
+
<span class="bk-traffic-chart__item">
|
|
39
|
+
<span class="bk-traffic-chart__dot bk-traffic-chart__dot--prev" />Previous period
|
|
40
|
+
</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<svg class="bk-traffic-chart__svg" viewBox={`0 0 ${W} ${H}`} role="img">
|
|
44
|
+
<line class="bk-traffic-chart__axis" x1={PAD} y1={20} x2={PAD} y2={H - 30} />
|
|
45
|
+
<line class="bk-traffic-chart__axis" x1={PAD} y1={H - 30} x2={W - 10} y2={H - 30} />
|
|
46
|
+
{previous.length > 0 && (
|
|
47
|
+
<polyline class="bk-traffic-chart__line bk-traffic-chart__line--prev" points={points(previous)} />
|
|
48
|
+
)}
|
|
49
|
+
{current.length > 0 && (
|
|
50
|
+
<polyline class="bk-traffic-chart__line" points={points(current)} />
|
|
51
|
+
)}
|
|
52
|
+
{current.map((v, i) => (
|
|
53
|
+
<circle class="bk-traffic-chart__pt" cx={xs(i, current.length)} cy={ys(v)} r={4} />
|
|
54
|
+
))}
|
|
55
|
+
<g class="bk-traffic-chart__labels">
|
|
56
|
+
{labels.map((l, i) => (
|
|
57
|
+
<text x={xs(i, labels.length) - 10} y={H - 10}>{l}</text>
|
|
58
|
+
))}
|
|
59
|
+
</g>
|
|
60
|
+
</svg>
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
/* blog-kit Astro adapter — VerdictCard
|
|
3
|
+
Thin wrapper: all styling lives in core/css/components.css (.bk-verdict-card).
|
|
4
|
+
Byte-identical markup to the React adapter. */
|
|
5
|
+
interface Props {
|
|
6
|
+
verdict: string;
|
|
7
|
+
score?: number;
|
|
8
|
+
summary: string;
|
|
9
|
+
kicker?: string;
|
|
10
|
+
bullets?: string[];
|
|
11
|
+
winner?: string;
|
|
12
|
+
}
|
|
13
|
+
const {
|
|
14
|
+
verdict,
|
|
15
|
+
score,
|
|
16
|
+
summary,
|
|
17
|
+
kicker = "The verdict",
|
|
18
|
+
bullets,
|
|
19
|
+
winner,
|
|
20
|
+
} = Astro.props;
|
|
21
|
+
---
|
|
22
|
+
<div class="bk-verdict-card">
|
|
23
|
+
<div class="bk-verdict-card__header">
|
|
24
|
+
<span class="bk-verdict-card__kicker">{kicker}</span>
|
|
25
|
+
{score !== undefined && (
|
|
26
|
+
<span class="bk-verdict-card__score">
|
|
27
|
+
{score}
|
|
28
|
+
<span class="bk-verdict-card__score-max">/10</span>
|
|
29
|
+
</span>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
<div class="bk-verdict-card__body">
|
|
33
|
+
{winner && (
|
|
34
|
+
<div class="bk-verdict-card__winner">
|
|
35
|
+
Winner: <span class="bk-verdict-card__winner-name">{winner}</span>
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
<h3 class="bk-verdict-card__verdict">{verdict}</h3>
|
|
39
|
+
<p class="bk-verdict-card__summary">{summary}</p>
|
|
40
|
+
{bullets && bullets.length > 0 && (
|
|
41
|
+
<div class="bk-verdict-card__bullets">
|
|
42
|
+
{bullets.map((b) => (
|
|
43
|
+
<span class="bk-verdict-card__chip">{b}</span>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
/* ============================================================
|
|
3
|
+
<Article> — the single-post route surface for ASTRO.
|
|
4
|
+
------------------------------------------------------------
|
|
5
|
+
The Astro counterpart of package/article.tsx. Source-agnostic:
|
|
6
|
+
it asks the kit's Astro data layer for the post + the RUNTIME-
|
|
7
|
+
COMPILED body HTML, then renders it under `.blog-root` with the
|
|
8
|
+
standard tail (author / newsletter / related). Identical markup
|
|
9
|
+
to the Next path and to the vendored Astro path — eject-safe.
|
|
10
|
+
|
|
11
|
+
The body is compiled from a remote MDX string at REQUEST time
|
|
12
|
+
(see @highjumpdigitalsoftware/blog-kit/astro -> renderMdxToHtml), so a newly
|
|
13
|
+
published post renders with no redeploy. <BlogBehaviors /> wires
|
|
14
|
+
the interactive components client-side, exactly as the vendored
|
|
15
|
+
Astro integration does.
|
|
16
|
+
|
|
17
|
+
Usage in a route (app/src/pages/blog/[slug].astro):
|
|
18
|
+
const data = await loadArticle(config, slug);
|
|
19
|
+
if (!data) return Astro.redirect("/404"); // or set status 404
|
|
20
|
+
<Article data={data} config={config} />
|
|
21
|
+
============================================================ */
|
|
22
|
+
import type { ArticleData, ResolvedConfig } from "@highjumpdigitalsoftware/blog-kit/astro";
|
|
23
|
+
import { articleHref } from "@highjumpdigitalsoftware/blog-kit/astro";
|
|
24
|
+
import BlogBehaviors from "../BlogBehaviors.astro";
|
|
25
|
+
import AuthorCard from "../AuthorCard.astro";
|
|
26
|
+
import NewsletterCTA from "../NewsletterCTA.astro";
|
|
27
|
+
import RelatedPosts from "../RelatedPosts.astro";
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
data: ArticleData;
|
|
31
|
+
config: ResolvedConfig;
|
|
32
|
+
/** Set false to omit the AuthorCard / Newsletter / Related tail. Default true. */
|
|
33
|
+
showTail?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { data, config, showTail = true } = Astro.props;
|
|
37
|
+
const { post, bodyHtml, related } = data;
|
|
38
|
+
|
|
39
|
+
const hrefBase =
|
|
40
|
+
`/${config.listing.articleBasePath.replace(/^\/+|\/+$/g, "")}/`.replace(
|
|
41
|
+
"//",
|
|
42
|
+
"/",
|
|
43
|
+
);
|
|
44
|
+
const authorName = String(post.author ?? config.brand.name);
|
|
45
|
+
const authorRole = String((post as Record<string, unknown>).authorRole ?? config.brand.name);
|
|
46
|
+
const authorBio = (post as Record<string, unknown>).authorBio as string | undefined;
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<div class="blog-root">
|
|
50
|
+
<slot name="hero" />
|
|
51
|
+
<BlogBehaviors />
|
|
52
|
+
<div class="bk-prose" set:html={bodyHtml} />
|
|
53
|
+
|
|
54
|
+
{showTail && (
|
|
55
|
+
<div style="margin-top:48px">
|
|
56
|
+
<AuthorCard name={authorName} role={authorRole} bio={authorBio} />
|
|
57
|
+
<NewsletterCTA />
|
|
58
|
+
{related.length > 0 && (
|
|
59
|
+
<RelatedPosts posts={related} hrefBase={hrefBase} />
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|