feat: GA4 consent banner — load Google only after opt-in#65
Merged
Conversation
Keep GA4 but gate it properly: the Google script does NOT load until the visitor accepts. Declining loads nothing and sets no cookies (verified in-browser: gtag undefined, no googletagmanager script). - components/site/consent-analytics.tsx — client gate + bottom banner. Equal-weight Accept/Decline (no dark pattern), choice persisted in localStorage (not a cookie), reopenable via footer. measurementId validated before script interpolation. - components/site/cookie-settings-link.tsx — footer 'Cookie choices' reopener that self-hides until a choice exists (no dead link on cookieless/none deployments). - analytics.tsx — ga4 branch now renders the consent gate; Plausible/ Umami stay direct (cookieless, no consent needed). - privacy page — corrected the now-false 'no third-party analytics / no cookies' claims; documents GA4, the opt-in, essential vs analytics cookies, and how to change the choice. Verified end-to-end in browser: no-choice shows banner + no GA4; decline loads zero Google and persists; reopen works; accept injects GA4 with the right measurement ID. tsc + 45 tests + build all pass.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
You're keeping GA4, so this adds a consent banner that actually does its job — GA4 does not load until the visitor opts in. A banner that lets Google load anyway is the useless kind; this isn't that.
Behavior (verified in-browser, end to end)
gtagundefined, no script in DOM)measurementIdis validated against^[A-Za-z0-9-]+$before being interpolated into the script tag.Privacy policy corrected
The page previously claimed "No third-party analytics or tracking scripts," "No advertising cookies," and "No behavioral tracking" — all false the moment GA4 is on. Rewritten to be truthful: documents GA4, that it's opt-in only, essential vs. analytics cookies, and how to change the choice. (A false privacy policy on a privacy-positioning firm is worse than no banner.)
Files
components/site/consent-analytics.tsx(new) — gate + bannercomponents/site/cookie-settings-link.tsx(new) — footer reopenercomponents/site/analytics.tsx— ga4 branch → consent gatecomponents/site/site-footer.tsx— adds the reopenerapp/privacy/page.tsx— truthful analytics/cookies sectionVerification
npx tsc --noEmit✓ ·npx vitest run✓ (45) ·npm run build✓To activate
Set
analytics_provider=ga4+analytics_site_id=G-XXXXXXXXXXin/dashboard/settings. Until then the banner stays dormant (nothing to consent to). Note: akritos.com still has no DMARC record published — separate from this.Test plan