Skip to content

feat(analytics): gate GA4/GTM behind Google Consent Mode v2#1360

Merged
sserrata merged 3 commits into
masterfrom
feat/gtm-consent-mode
Jul 21, 2026
Merged

feat(analytics): gate GA4/GTM behind Google Consent Mode v2#1360
sserrata merged 3 commits into
masterfrom
feat/gtm-consent-mode

Conversation

@sserrata

Copy link
Copy Markdown
Member

Summary

Adds a GDPR-style cookie consent banner and gates GA4/GTM behind Google Consent Mode v2, by folding the behavior of docusaurus-plugin-cookie-consent into our own docusaurus-plugin-gtm instead of adopting the third-party package.

Co-locating the consent signaling and the GTM loader in a single injectHtmlTags call means the consent default = denied script is guaranteed to run before GTM loads (by array order), independent of plugin registration order — which is the fragile part of wiring the two as separate plugins.

Changes

  • docusaurus-plugin-gtm/index.js — injects Consent Mode v2 defaults (all denied except security_storage), replays the stored choice from localStorage, and sets ads_data_redaction, all before the GTM loader. Prod-only, otherwise unchanged. New options: storageKey, waitForUpdate.
  • src/components/CookieConsent/ — SSR-safe bottom banner (Accept all / Reject optional) plus a shared helper (consent.js) that writes localStorage and fires gtag('consent','update', …) using the category → Google signal mapping.
  • src/theme/Root.js — renders the banner globally.

Category → Google signal mapping

Consent category Google signals
analytics analytics_storage
marketing ad_storage, ad_user_data, ad_personalization
functional functionality_storage, personalization_storage

Verification

  • Unit: plugin emits the consent script at index 0 and the GTM loader at index 1; dev mode emits nothing.
  • End-to-end: scoped PRODUCTS_INCLUDE=contributing production build succeeds; compiled build/index.html contains consent","default before gtm.js.

Follow-ups before/after merge

  • Privacy link — no in-site privacy page exists, so the banner links to paloaltonetworks.com/legal-notices/privacy. Swap if there's a preferred URL.
  • GTM container — GA4 honors analytics_storage automatically under Consent Mode; configure EU region behavior / ping modeling in the GTM-PLXD79N container as desired.

🤖 Generated with Claude Code

Fold cookie-consent behavior into the custom docusaurus-plugin-gtm rather
than adopting a third-party plugin, so the consent signaling and the GTM
loader are emitted from a single injectHtmlTags call. This guarantees the
`consent default = denied` script runs before GTM loads by array order,
independent of plugin registration order.

- docusaurus-plugin-gtm: inject Consent Mode v2 defaults (all denied except
  security_storage), replay stored choice from localStorage, and set
  ads_data_redaction, all before the GTM loader. Prod-only, unchanged
  otherwise. Adds storageKey/waitForUpdate options.
- src/components/CookieConsent: SSR-safe bottom banner (Accept all / Reject
  optional) plus a shared consent helper that writes localStorage and fires
  gtag('consent','update') with the category -> Google signal mapping.
- src/theme/Root.js: render the banner globally.

Verified against a production build: compiled HTML emits the consent
default script before the GTM loader.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 9235c08):

https://pan-dev-f1b58--pr1360-nsi6124w.web.app

(expires Tue, 28 Jul 2026 17:32:44 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 9359a9fa100d5fdb082f75280473a90b1f96eb51

BlogCard scrapes content_html for a card image and injects it via innerHTML,
so Medium-hosted images and the medium.com/_/stat tracking pixel get requested
by the browser and set third-party cookies before consent.

feed2json.js now downloads each post's hero image into static/img/blog-feed/
(gitignored, regenerated at build), points thumbnail at the local copy, and
strips all remote <img> tags (including the stat pixel) from content_html. The
client no longer requests anything off medium.com, so those cookies are never
set — independent of consent. On download failure the thumbnail is left unset
and BlogCard falls back to the local stock image.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the localStorage key coupling between the GTM head script and the
CookieConsent banner explicit in one place, rather than relying on both
sides defaulting to the same string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sserrata
sserrata marked this pull request as ready for review July 21, 2026 17:33
@sserrata
sserrata requested a review from a team as a code owner July 21, 2026 17:33
@sserrata
sserrata merged commit a7e58e5 into master Jul 21, 2026
6 checks passed
@sserrata
sserrata deleted the feat/gtm-consent-mode branch July 21, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant