{/* Page Header */}
diff --git a/apps/web-roo-code/src/components/blog/blog-analytics.tsx b/apps/web-roo-code/src/components/blog/blog-analytics.tsx
new file mode 100644
index 0000000000..026a9a3816
--- /dev/null
+++ b/apps/web-roo-code/src/components/blog/blog-analytics.tsx
@@ -0,0 +1,99 @@
+"use client"
+
+import { useEffect, useRef } from "react"
+import type { BlogPost } from "@/lib/blog"
+import { trackBlogIndexView, trackBlogPostView, trackBlogPostScrollDepth, trackBlogPostTimeSpent } from "@/lib/blog"
+
+interface BlogIndexAnalyticsProps {
+ postCount: number
+}
+
+/**
+ * Client component that tracks blog index page view
+ * Place this inside the blog index page
+ */
+export function BlogIndexAnalytics({ postCount }: BlogIndexAnalyticsProps) {
+ const tracked = useRef(false)
+
+ useEffect(() => {
+ if (!tracked.current) {
+ trackBlogIndexView(postCount)
+ tracked.current = true
+ }
+ }, [postCount])
+
+ return null
+}
+
+interface BlogPostAnalyticsProps {
+ post: {
+ slug: string
+ title: string
+ description: string
+ tags: string[]
+ publish_date: string
+ }
+}
+
+/**
+ * Client component that tracks blog post view, scroll depth, and time spent
+ * Place this inside the blog post page
+ */
+export function BlogPostAnalytics({ post }: BlogPostAnalyticsProps) {
+ const trackedView = useRef(false)
+ const trackedDepths = useRef>(new Set())
+ const startTime = useRef(Date.now())
+
+ useEffect(() => {
+ // Capture start time for cleanup function
+ const effectStartTime = startTime.current
+
+ // Track page view on mount
+ if (!trackedView.current) {
+ trackBlogPostView(post as BlogPost)
+ trackedView.current = true
+ }
+
+ // Track scroll depth
+ const handleScroll = () => {
+ const scrollHeight = document.documentElement.scrollHeight - window.innerHeight
+ if (scrollHeight <= 0) return
+
+ const scrollPercent = (window.scrollY / scrollHeight) * 100
+
+ const depths: (25 | 50 | 75 | 100)[] = [25, 50, 75, 100]
+ for (const depth of depths) {
+ if (scrollPercent >= depth && !trackedDepths.current.has(depth)) {
+ trackedDepths.current.add(depth)
+ trackBlogPostScrollDepth(post as BlogPost, depth)
+ }
+ }
+ }
+
+ // Track time spent on page when leaving
+ const handleVisibilityChange = () => {
+ if (document.visibilityState === "hidden") {
+ const timeSpent = Date.now() - effectStartTime
+ trackBlogPostTimeSpent(post as BlogPost, timeSpent)
+ }
+ }
+
+ window.addEventListener("scroll", handleScroll, { passive: true })
+ document.addEventListener("visibilitychange", handleVisibilityChange)
+
+ // Check initial scroll position
+ handleScroll()
+
+ return () => {
+ window.removeEventListener("scroll", handleScroll)
+ document.removeEventListener("visibilitychange", handleVisibilityChange)
+
+ // Track time spent when component unmounts
+ const timeSpent = Date.now() - effectStartTime
+ trackBlogPostTimeSpent(post as BlogPost, timeSpent)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [post.slug])
+
+ return null
+}
diff --git a/apps/web-roo-code/src/lib/blog/analytics.ts b/apps/web-roo-code/src/lib/blog/analytics.ts
new file mode 100644
index 0000000000..15fd16a1d9
--- /dev/null
+++ b/apps/web-roo-code/src/lib/blog/analytics.ts
@@ -0,0 +1,96 @@
+/**
+ * Blog Analytics Events
+ *
+ * PostHog tracking events for the blog section.
+ * These events help understand blog engagement and attribution.
+ */
+
+import posthog from "posthog-js"
+import type { BlogPost } from "./types"
+
+/**
+ * Track blog index page view
+ * Called when user views /blog
+ */
+export function trackBlogIndexView(postCount: number): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_index_viewed", {
+ post_count: postCount,
+ page_type: "blog_index",
+ })
+}
+
+/**
+ * Track blog post view
+ * Called when user views /blog/[slug]
+ */
+export function trackBlogPostView(post: BlogPost): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_post_viewed", {
+ post_slug: post.slug,
+ post_title: post.title,
+ post_tags: post.tags,
+ publish_date: post.publish_date,
+ page_type: "blog_post",
+ })
+}
+
+/**
+ * Track blog post scroll depth
+ * Called at various scroll thresholds (25%, 50%, 75%, 100%)
+ */
+export function trackBlogPostScrollDepth(post: BlogPost, depth: 25 | 50 | 75 | 100): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_post_scroll_depth", {
+ post_slug: post.slug,
+ post_title: post.title,
+ scroll_depth: depth,
+ })
+}
+
+/**
+ * Track blog post share
+ * Called when user clicks a share button
+ */
+export function trackBlogPostShare(post: BlogPost, platform: string): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_post_shared", {
+ post_slug: post.slug,
+ post_title: post.title,
+ share_platform: platform,
+ })
+}
+
+/**
+ * Track blog post CTA click
+ * Called when user clicks a CTA within a blog post
+ */
+export function trackBlogPostCTAClick(post: BlogPost, ctaType: string, ctaTarget: string): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_post_cta_click", {
+ post_slug: post.slug,
+ post_title: post.title,
+ cta_type: ctaType,
+ cta_target: ctaTarget,
+ })
+}
+
+/**
+ * Track time spent on blog post
+ * Called when user leaves the page (or at intervals)
+ */
+export function trackBlogPostTimeSpent(post: BlogPost, timeMs: number): void {
+ if (typeof window === "undefined" || !posthog.__loaded) return
+
+ posthog.capture("blog_post_time_spent", {
+ post_slug: post.slug,
+ post_title: post.title,
+ time_spent_ms: timeMs,
+ time_spent_seconds: Math.round(timeMs / 1000),
+ })
+}
diff --git a/apps/web-roo-code/src/lib/blog/index.ts b/apps/web-roo-code/src/lib/blog/index.ts
index cee8f97acb..8aecd2de45 100644
--- a/apps/web-roo-code/src/lib/blog/index.ts
+++ b/apps/web-roo-code/src/lib/blog/index.ts
@@ -44,3 +44,13 @@ export {
getBlogPostBreadcrumbStructuredData,
getBlogPostUrl,
} from "./structured-data"
+
+// Analytics (PostHog events)
+export {
+ trackBlogIndexView,
+ trackBlogPostView,
+ trackBlogPostScrollDepth,
+ trackBlogPostShare,
+ trackBlogPostCTAClick,
+ trackBlogPostTimeSpent,
+} from "./analytics"