feat: enhance PostHead component with SEO improvements and add Posthog tracking
This commit is contained in:
@@ -10,38 +10,55 @@ const { post } = Astro.props
|
||||
|
||||
const title = post.data.title || SITE.title
|
||||
const description = post.data.description || SITE.description
|
||||
const image = new URL('/image/' + post.id + '.png', Astro.site).toString()
|
||||
const postUrl = new URL(post.id, SITE.href).toString()
|
||||
const image = SITE.href + '/image/' + post.id + '.png';
|
||||
const author = post.data.authors ? post.data.authors.join(', ') : SITE.author
|
||||
|
||||
const wordsPerMinute = 200;
|
||||
const wordCount = post.body ? post.body.split(/\s+/).length : 0;
|
||||
const readTime = Math.max(1, Math.round(wordCount / wordsPerMinute));
|
||||
---
|
||||
|
||||
<!-- Basic Meta Tags -->
|
||||
<title>{`${title} - ${SITE.title}`}</title>
|
||||
<meta name="title" content={`${title} | ${SITE.title}`} />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="canonical" href={SITE.href} />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="author" content={author} />
|
||||
{post?.data.tags && <meta name="keywords" content={post.data.tags.join(', ')} />}
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={postUrl} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta
|
||||
property="og:image"
|
||||
content={`${SITE.href}${post?.data?.image?.src}` || image}
|
||||
/>
|
||||
<meta property="og:image" content={image} />
|
||||
<meta property="og:image:alt" content={title} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:locale" content={SITE.locale} />
|
||||
<meta property="og:site_name" content={SITE.title} />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:url" content={postUrl} />
|
||||
<meta property="og:author" content={author} />
|
||||
<meta property="article:published_time" content={post.data.date.toISOString()} />
|
||||
<meta property="article:modified_time" content={post.data.date.toISOString()} />
|
||||
<meta property="article:section" content={post.data.tags ? post.data.tags.join(', ') : ''} />
|
||||
<meta property="article:published" content={post.data.date.toISOString()} />
|
||||
<meta property="article:author" content={author} />
|
||||
<meta property="article:publisher" content={SITE.title} />
|
||||
<meta property="article:tag" content={post?.data.tags ? post.data.tags.join(', ') : ''} />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta
|
||||
property="twitter:image"
|
||||
content={`${SITE.href}${post?.data?.image?.src}` || image}
|
||||
/>
|
||||
<meta property="twitter:image" content={image} />
|
||||
<meta name="twitter:image:alt" content={title} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:creator" content={author} />
|
||||
|
||||
<!-- Individual Tags -->
|
||||
{
|
||||
post?.data.tags &&
|
||||
post.data.tags.map((tag: string) => {
|
||||
@@ -49,11 +66,32 @@ const author = post.data.authors ? post.data.authors.join(', ') : SITE.author
|
||||
})
|
||||
}
|
||||
|
||||
<meta
|
||||
property="article:published_time"
|
||||
content={post.data.date.toISOString()}
|
||||
/>
|
||||
<meta property="article:modified_time" content={post.data.date.toISOString()} />
|
||||
<meta property="article:author" content={author} />
|
||||
<meta property="article:published" content={post.data.date.toISOString()} />
|
||||
<meta property="article:modified" content={post.data.date.toISOString()} />
|
||||
<!-- JSON-LD Structured Data -->
|
||||
<script type="application/ld+json" set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
"headline": title,
|
||||
"description": description,
|
||||
"image": image,
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": author
|
||||
},
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": SITE.title,
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": new URL("/favicon.ico", SITE.href).toString()
|
||||
}
|
||||
},
|
||||
"datePublished": post.data.date.toISOString(),
|
||||
"dateModified": post.data.date.toISOString(),
|
||||
"mainEntityOfPage": {
|
||||
"@type": "WebPage",
|
||||
"@id": postUrl
|
||||
},
|
||||
"keywords": post?.data.tags ? post.data.tags.join(', ') : '',
|
||||
"url": postUrl,
|
||||
"readingTime": `${readTime} min read`
|
||||
})} />
|
||||
59
src/components/Posthog.astro
Normal file
59
src/components/Posthog.astro
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<script is:inline>
|
||||
!(function (t, e) {
|
||||
var o, n, p, r;
|
||||
e.__SV ||
|
||||
((window.posthog = e),
|
||||
(e._i = []),
|
||||
(e.init = function (i, s, a) {
|
||||
function g(t, e) {
|
||||
var o = e.split(".");
|
||||
2 == o.length && ((t = t[o[0]]), (e = o[1])),
|
||||
(t[e] = function () {
|
||||
t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
|
||||
});
|
||||
}
|
||||
((p = t.createElement("script")).type = "text/javascript"),
|
||||
(p.crossOrigin = "anonymous"),
|
||||
(p.async = !0),
|
||||
(p.src =
|
||||
s.api_host.replace(".i.posthog.com", "-assets.i.posthog.com") +
|
||||
"/static/array.js"),
|
||||
(r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(
|
||||
p,
|
||||
r
|
||||
);
|
||||
var u = e;
|
||||
for (
|
||||
void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
|
||||
u.people = u.people || [],
|
||||
u.toString = function (t) {
|
||||
var e = "posthog";
|
||||
return (
|
||||
"posthog" !== a && (e += "." + a), t || (e += " (stub)"), e
|
||||
);
|
||||
},
|
||||
u.people.toString = function () {
|
||||
return u.toString(1) + ".people (stub)";
|
||||
},
|
||||
o =
|
||||
"init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId captureTraceFeedback captureTraceMetric".split(
|
||||
" "
|
||||
),
|
||||
n = 0;
|
||||
n < o.length;
|
||||
n++
|
||||
)
|
||||
g(u, o[n]);
|
||||
e._i.push([i, s, a]);
|
||||
}),
|
||||
(e.__SV = 1));
|
||||
})(document, window.posthog || []);
|
||||
posthog.init("phc_orIpAm9v5xuxdhe4wd5FhkyTJ7ygykslwxxeCESZquz", {
|
||||
api_host: "https://eu.i.posthog.com",
|
||||
person_profiles: "identified_only",
|
||||
});
|
||||
</script>
|
||||
@@ -7,6 +7,7 @@ import Head from '@/components/Head.astro'
|
||||
import Navbar from '@/components/react/navbar'
|
||||
import { SITE } from '@/consts'
|
||||
import { cn } from '@/lib/utils'
|
||||
import Posthog from '@/components/Posthog.astro'
|
||||
|
||||
const { isWide = false } = Astro.props
|
||||
---
|
||||
@@ -23,6 +24,7 @@ const { isWide = false } = Astro.props
|
||||
<link rel="preload" href="/fonts/GeistVF.woff2" as="font" crossorigin="anonymous" />
|
||||
<link rel="preload" href="/fonts/_montserrat_bold.ttf" as="font" crossorigin="anonymous" />
|
||||
<link rel="preload" href="/fonts/_montserrat_regular.ttf" as="font" crossorigin="anonymous" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<script is:inline data-astro-rerun>
|
||||
(function() {
|
||||
try {
|
||||
@@ -45,6 +47,7 @@ const { isWide = false } = Astro.props
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<Posthog />
|
||||
</Head>
|
||||
<body>
|
||||
<div class="flex h-fit min-h-screen w-full flex-col gap-y-4 sm:gap-y-6 font-sans">
|
||||
@@ -61,5 +64,15 @@ const { isWide = false } = Astro.props
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-QVK59XQK72"
|
||||
></script>
|
||||
<script is:inline>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
gtag("config", "G-QVK59XQK72");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -63,7 +63,7 @@ export async function GET(context: APIContext) {
|
||||
<div style="position: absolute;display: flex; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.01); opacity: 0.6;"></div>
|
||||
|
||||
|
||||
<div style="position: absolute; width: 350px; height: 350px;display: flex; background: radial-gradient(circle, rgba(100, 100, 255, 0.12) 0%, transparent 70%); top: -100px; right: -50px; border-radius: 50%;"></div>
|
||||
<div style="position: absolute; width: 350px; height: 350px;display: flex; background: radial-gradient(circle, rgba(250, 255, 100, 0.12) 0%, transparent 70%); top: -100px; right: -50px; border-radius: 50%;"></div>
|
||||
|
||||
|
||||
<div style="flex: 4; padding: 48px 50px; display: flex; flex-direction: column; justify-content: center; position: relative;">
|
||||
|
||||
Reference in New Issue
Block a user