Skip to content

Commit ba98dd1

Browse files
committed
Web: drop consent banner, PostHog runs unconditionally with the key
PostHog runs in anonymous mode (person_profiles: 'never', all inputs masked), so no consent gate is needed. Banner is removed; analytics.ts loads PostHog whenever PUBLIC_POSTHOG_KEY is set. Unset the key to disable.
1 parent b3dc694 commit ba98dd1

4 files changed

Lines changed: 34 additions & 155 deletions

File tree

web/ANALYTICS.md

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -133,37 +133,23 @@ The GA4 data lives in property `530680522`. For programmatic access (Looker
133133
Studio, scripts, CI), use the service-account key at
134134
`~/.config/gcp-keys/ga-reader-key.json`. Don't commit it.
135135
136-
## Privacy & consent
136+
## Privacy
137137
138-
Two layers ship today:
138+
Two layers ship today, both anonymous, both no-banner:
139139
140-
1. **GA4** (loaded sitewide, no banner). IP anonymization on, no PII, no
141-
cross-site identifiers. The custom events above carry only the params
142-
the docs declare — no free-form text, no form contents, no path-with-PII.
143-
2. **PostHog** (opt-in, gated on the consent banner). Session recordings
144-
and heatmaps. Only loads when the user clicked **Allow** on the banner
145-
in `src/components/ConsentBanner.astro`. Loaded with
146-
`person_profiles: 'never'` (anonymous mode), `maskAllInputs: true`, and
147-
any element marked `[data-private]` is masked in recordings.
140+
1. **GA4** (loaded sitewide). IP anonymization on, no PII, no cross-site
141+
identifiers. The custom events above carry only the params the docs
142+
declare — no free-form text, no form contents, no path-with-PII.
143+
2. **PostHog** (loaded sitewide when `PUBLIC_POSTHOG_KEY` is set). Session
144+
recordings and heatmaps in `person_profiles: 'never'` (anonymous mode),
145+
`maskAllInputs: true`. Anything marked `[data-private]` is masked in
146+
recordings.
148147
149-
The banner shows once per device (persisted in `localStorage` as
150-
`pilot-analytics-consent``accepted | declined`). Cleared the
151-
choice? The banner reappears on next visit.
152-
153-
PostHog only initialises when **both** conditions hold:
154-
- `localStorage['pilot-analytics-consent'] === 'accepted'`, AND
155-
- `PUBLIC_POSTHOG_KEY` is defined (set in your `.env` or Cloudflare Pages
156-
environment vars).
157-
158-
If the key is unset, the consent banner is decorative — clicking Allow does
159-
nothing harmful, just no recordings happen. To turn PostHog on for real:
148+
PostHog is the kill switch — unset `PUBLIC_POSTHOG_KEY` and nothing loads.
160149
161150
```bash
162151
# Sign up at https://posthog.com/, create a project, copy the API key.
163-
# Then in the Cloudflare Pages project settings:
152+
# In the Cloudflare Pages project settings:
164153
PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxxxx
165154
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com # or eu.i.posthog.com
166155
```
167-
168-
To turn it off: unset the env var. The banner can stay; without a key,
169-
nothing loads.

web/src/components/ConsentBanner.astro

Lines changed: 0 additions & 88 deletions
This file was deleted.

web/src/components/Footer.astro

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
---
22
// Full site footer - large display text + 4-column nav + fine print
3-
import ConsentBanner from './ConsentBanner.astro';
43
---
5-
<ConsentBanner />
64
<footer class="site-footer">
75
<div class="wrap">
86
<div class="foot-big">

web/src/scripts/analytics.ts

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ declare global {
2222
dataLayer?: unknown[];
2323
gtag?: (...args: unknown[]) => void;
2424
pilotTrack?: (event: string, params?: Record<string, string | number | undefined>) => void;
25-
pilotInitPostHog?: () => void;
2625
}
2726
}
2827

@@ -46,52 +45,36 @@ function track(event: string, params: GtagParams = {}): void {
4645

4746
window.pilotTrack = track;
4847

49-
// --- PostHog (heatmaps + session recordings, gated on consent + key) ----
48+
// --- PostHog (heatmaps + session recordings, gated on key only) --------
5049
const POSTHOG_KEY = (import.meta as { env?: Record<string, string | undefined> }).env
5150
?.PUBLIC_POSTHOG_KEY;
5251
const POSTHOG_HOST =
5352
(import.meta as { env?: Record<string, string | undefined> }).env?.PUBLIC_POSTHOG_HOST ||
5453
'https://us.i.posthog.com';
5554

56-
let posthogStarted = false;
57-
58-
async function initPostHog(): Promise<void> {
59-
if (posthogStarted || !POSTHOG_KEY) return;
60-
posthogStarted = true;
61-
try {
62-
const mod = await import('posthog-js');
63-
const ph = (mod as { default: typeof mod }).default || mod;
64-
(ph as unknown as { init: (k: string, opts: Record<string, unknown>) => void }).init(
65-
POSTHOG_KEY,
66-
{
67-
api_host: POSTHOG_HOST,
68-
person_profiles: 'never',
69-
capture_pageview: true,
70-
autocapture: true,
71-
persistence: 'localStorage',
72-
session_recording: {
73-
maskAllInputs: true,
74-
maskTextSelector: '[data-private]',
55+
if (POSTHOG_KEY) {
56+
import('posthog-js')
57+
.then((mod) => {
58+
const ph = (mod as { default: typeof mod }).default || mod;
59+
(ph as unknown as { init: (k: string, opts: Record<string, unknown>) => void }).init(
60+
POSTHOG_KEY,
61+
{
62+
api_host: POSTHOG_HOST,
63+
person_profiles: 'never',
64+
capture_pageview: true,
65+
autocapture: true,
66+
persistence: 'localStorage',
67+
session_recording: {
68+
maskAllInputs: true,
69+
maskTextSelector: '[data-private]',
70+
},
7571
},
76-
},
77-
);
78-
} catch (err) {
79-
posthogStarted = false;
80-
// Silent - PostHog is opt-in additive signal; failure must not break GA4.
81-
console.warn('[analytics] posthog init failed', err);
82-
}
83-
}
84-
85-
window.pilotInitPostHog = initPostHog;
86-
87-
if (typeof localStorage !== 'undefined') {
88-
try {
89-
if (localStorage.getItem('pilot-analytics-consent') === 'accepted') {
90-
initPostHog();
91-
}
92-
} catch {
93-
// localStorage may throw in privacy mode - skip silently
94-
}
72+
);
73+
})
74+
.catch((err) => {
75+
// PostHog is additive; failure must not break GA4.
76+
console.warn('[analytics] posthog init failed', err);
77+
});
9578
}
9679

9780
// --- Click delegation (incl. outbound, blog→docs handoff, dead_click) ---

0 commit comments

Comments
 (0)