Merge pull request #2 from cojocaru-david/feat/added-posthead-tracking-and-improved-seo

feat: enhance PostHead component with SEO improvements and add Postho…
This commit is contained in:
Cojocaru David
2025-04-22 22:28:05 +03:00
committed by GitHub
5 changed files with 131 additions and 21 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@@ -10,38 +10,55 @@ const { post } = Astro.props
const title = post.data.title || SITE.title const title = post.data.title || SITE.title
const description = post.data.description || SITE.description 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 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> <title>{`${title} - ${SITE.title}`}</title>
<meta name="title" content={`${title} | ${SITE.title}`} /> <meta name="title" content={`${title} | ${SITE.title}`} />
<meta name="description" content={description} /> <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:title" content={title} />
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />
<meta <meta property="og:image" content={image} />
property="og:image"
content={`${SITE.href}${post?.data?.image?.src}` || image}
/>
<meta property="og:image:alt" content={title} /> <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:locale" content={SITE.locale} />
<meta property="og:site_name" content={SITE.title} /> <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="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:title" content={title} />
<meta name="twitter:description" content={description} /> <meta name="twitter:description" content={description} />
<meta <meta property="twitter:image" content={image} />
property="twitter:image"
content={`${SITE.href}${post?.data?.image?.src}` || image}
/>
<meta name="twitter:image:alt" content={title} /> <meta name="twitter:image:alt" content={title} />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content={author} /> <meta name="twitter:creator" content={author} />
<!-- Individual Tags -->
{ {
post?.data.tags && post?.data.tags &&
post.data.tags.map((tag: string) => { post.data.tags.map((tag: string) => {
@@ -49,11 +66,32 @@ const author = post.data.authors ? post.data.authors.join(', ') : SITE.author
}) })
} }
<meta <!-- JSON-LD Structured Data -->
property="article:published_time" <script type="application/ld+json" set:html={JSON.stringify({
content={post.data.date.toISOString()} "@context": "https://schema.org",
/> "@type": "BlogPosting",
<meta property="article:modified_time" content={post.data.date.toISOString()} /> "headline": title,
<meta property="article:author" content={author} /> "description": description,
<meta property="article:published" content={post.data.date.toISOString()} /> "image": image,
<meta property="article:modified" content={post.data.date.toISOString()} /> "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`
})} />

View 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>

View File

@@ -7,6 +7,7 @@ import Head from '@/components/Head.astro'
import Navbar from '@/components/react/navbar' import Navbar from '@/components/react/navbar'
import { SITE } from '@/consts' import { SITE } from '@/consts'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import Posthog from '@/components/Posthog.astro'
const { isWide = false } = Astro.props 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/GeistVF.woff2" as="font" crossorigin="anonymous" />
<link rel="preload" href="/fonts/_montserrat_bold.ttf" 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="preload" href="/fonts/_montserrat_regular.ttf" as="font" crossorigin="anonymous" />
<link rel="sitemap" href="/sitemap-index.xml" />
<script is:inline data-astro-rerun> <script is:inline data-astro-rerun>
(function() { (function() {
try { try {
@@ -45,6 +47,7 @@ const { isWide = false } = Astro.props
} }
})(); })();
</script> </script>
<Posthog />
</Head> </Head>
<body> <body>
<div class="flex h-fit min-h-screen w-full flex-col gap-y-4 sm:gap-y-6 font-sans"> <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> </main>
<Footer /> <Footer />
</div> </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> </body>
</html> </html>

View File

@@ -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;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;"> <div style="flex: 4; padding: 48px 50px; display: flex; flex-direction: column; justify-content: center; position: relative;">