Skip to content

Track outbound GitHub clicks explicitly in PostHog#4974

Open
vfanucci wants to merge 1 commit into
mainfrom
track-outbound-github-clicks
Open

Track outbound GitHub clicks explicitly in PostHog#4974
vfanucci wants to merge 1 commit into
mainfrom
track-outbound-github-clicks

Conversation

@vfanucci

Copy link
Copy Markdown
Contributor

Summary

Adds an explicit outbound_click event for clicks on github.com links across the site. We currently have no way to measure how many visitors click through to the GitHub repo, which blocks OSS-conversion attribution (e.g. measuring whether the Reddit always-on campaign or organic traffic drives repo visits).

Why this is needed (root cause)

PostHog is initialized in src/scripts/cookieconsent.ts with autocapture disabled:

posthog.init(response.posthog.token, {
    api_host: window.location.origin + "/t/",
    capture_pageview: false,
    capture_pageleave: true,
    autocapture: false,          // ← no clicks are captured automatically
    disable_session_recording: false,
})

Because autocapture: false, PostHog never records any click events — including outbound clicks to the repo. This is not a markup issue: the GitHub links themselves are standard <a href="https://github.com/kestra-io/kestra" target="_blank"> (17 files). The capture mechanism is simply off.

Rather than re-enable global autocapture (more event volume, and it was likely turned off deliberately), this PR adds a single delegated listener that fires one explicit, well-named event only for GitHub outbound clicks. Surgical, no change to existing PostHog config.

Changes

  1. New file: src/scripts/outbound.js — a delegated, passive, capture-phase click listener on document that captures github.com outbound links.
  2. src/components/layout.astro — register the script alongside analytics.js (same PROD/PREVIEW guard).

Design notes (for review)

  • Delegated on document, not per-link. Survives astro:page-load SPA navigations and covers links injected after load. No re-binding needed.
  • { passive: true }. We never call preventDefault — the browser follows the link normally — so the handler is declared passive. The browser doesn't wait on it, protecting INP.
  • { capture: true }. Ensures the handler runs before navigation on same-tab links (most GitHub links are target="_blank", but this covers the rest). posthog.capture() batches via sendBeacon, so the event survives page unload.
  • Aggressive early return. closest("a[href]") short-circuits ~99% of clicks in one operation before any further work.
  • No new dependency. Reuses the existing posthog-js singleton already imported by analytics.js / cookieconsent.ts.

Core Web Vitals impact

Negligible by design. LCP and CLS: zero (no rendering, no DOM mutation, no layout shift). INP: the only theoretically affected metric, but the handler does only a closest() + string test + async non-blocking capture() — sub-millisecond, well under the 200 ms threshold — and { passive: true } keeps it off the critical interaction path. Bundle weight: a few hundred bytes gzipped.

Known limitation (not solved here)

Because PostHog only initializes after analytics consent (EU = opt-in, see cookieconsent.ts), this event will not fire for sessions without consent. Any outbound_click count is therefore a lower bound, consistent with the existing Consent Mode v2 gap. This PR does not attempt to fix consent gating; it should be documented wherever the metric is reported.

How to verify after merge

  1. Load any page with a GitHub link, accept analytics cookies.
  2. Open PostHog → Activity, click a GitHub link.
  3. Confirm an outbound_click event appears with url, source_path, link_text, target properties.
  4. Build an insight: outbound_click broken down by utm_source / source_path to start attributing repo clicks to channels.

🤖 Generated with Claude Code

Add an explicit `outbound_click` event for clicks on github.com links
across the site. PostHog is initialized with autocapture disabled, so no
click events are currently recorded — including clicks through to the
repo, which blocks OSS-conversion attribution.

Rather than re-enabling global autocapture, add a single delegated,
passive, capture-phase listener on document that fires one well-named
event only for github.com outbound links. Survives Astro SPA navigations
and covers dynamically injected links with no re-binding. No new
dependency (reuses the existing posthog-js singleton); no change to
existing PostHog config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/scripts/outbound.js
if (!link) return

const url = link.href
if (!url.includes("github.com")) return
@vfanucci vfanucci added area/frontend Needs frontend code changes kind/website Website-related change labels Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

☁️ Cloudflare Worker Preview Deployed!

🔗 https://ks-track-outbound-github-clicks-docs.kestra-io.workers.dev
🔗 https://90c2bbc0-docs.kestra-io.workers.dev

## 🔦 Lighthouse Benchmark

Tested: https://ks-track-outbound-github-clicks-docs.kestra-io.workers.dev on 2026-06-16 13:41 UTC
No baseline available — scores will appear after the first merge to main

Scores (0–100, higher is better)

Page Performance Accessibility Best Practices SEO
Home 73 83 56 92
Pricing 98 91 56 100
Enterprise 96 82 56 100
Cloud 89 87 56 100
About Us 84 91 56 100
Docs Landing 96 88 56 92
Contribute to Kestra (simple docs) 99 88 56 92
Flow (full featured docs) 90 90 56 92
Blog Index 62 91 56 100
Blog Post (sample) 99 87 56 100
VS Page (sample) 97 88 56 100
Plugins Landing 90 81 56 92
Plugin Page (sample) 94 87 56 100
Plugin Debug Page (sample) 94 87 56 100
Plugin Debug Return Page (sample) 91 88 56 100
Blueprints Landing 93 80 56 92
Blueprint Audit Logs CSV Export 64 86 56 100

Core Web Vitals (lower is better)

Page LCP FCP TBT CLS Speed Index
Home 1.44 s 0.73 s 387 ms 0.000 2.21 s
Pricing 1.10 s 0.58 s 17 ms 0.000 0.81 s
Enterprise 1.33 s 0.64 s 19 ms 0.003 0.91 s
Cloud 2.24 s 0.62 s 27 ms 0.000 0.88 s
About Us 2.88 s 0.65 s 20 ms 0.000 0.89 s
Docs Landing 0.94 s 0.63 s 142 ms 0.000 0.99 s
Contribute to Kestra (simple docs) 0.95 s 0.58 s 59 ms 0.000 0.81 s
Flow (full featured docs) 1.78 s 0.69 s 114 ms 0.000 1.28 s
Blog Index 5.73 s 1.38 s 98 ms 0.001 53.22 s
Blog Post (sample) 0.80 s 0.54 s 13 ms 0.000 0.81 s
VS Page (sample) 1.10 s 0.59 s 13 ms 0.064 0.74 s
Plugins Landing 1.18 s 0.64 s 75 ms 0.000 2.84 s
Plugin Page (sample) 1.16 s 0.60 s 104 ms 0.051 1.70 s
Plugin Debug Page (sample) 1.00 s 0.51 s 125 ms 0.001 1.73 s
Plugin Debug Return Page (sample) 1.10 s 0.53 s 128 ms 0.025 2.23 s
Blueprints Landing 1.35 s 0.72 s 19 ms 0.000 1.91 s
Blueprint Audit Logs CSV Export 0.96 s 0.62 s 242 ms 0.485 2.33 s
Legend

🟢 improved  ·  🔻 regressed  ·  (blank) no significant change
Score threshold: ±10 pts  ·  Metric threshold: ±30% of baseline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/frontend Needs frontend code changes kind/website Website-related change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants