[AAASM-3558] 📈 (docs): Advanced Consent Mode so GA fires + always-visible feedback#170
Conversation
Override Material's analytics partials so gtag.js loads on every page with analytics_storage denied by default (cookieless pings until consent, full hits after), restoring granted state from stored consent and assigning window.gtag so the tag is detectable. The feedback widget is revealed and its single consent-gated GA `feedback` event still flows through Material's own handler (no double-count). Refs AAASM-3558. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Override Material's outdated.html so the script passes `location` as the
base to `new URL("{{ base_url }}", location)`. Material's generated code
calls `new URL("..")` with no base, which throws
`TypeError: Failed to construct 'URL'` on every page load.
Refs AAASM-3558.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The version-warning banner derived versions.json by popping the page's last path segment, which 404s on deep pages. Resolve it from `base_url` (the deploy root) instead, so it always points one level above the version directory regardless of page depth. Refs AAASM-3558. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a custom mike redirect template with a self-contained gtag.js snippet (advanced Consent Mode v2, cookieless-until-consent) and pass it to `mike set-default --template` so Google's tag test detects GA on the bare-root URL (/python-sdk/) too. Refs AAASM-3558. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude Code — review result ✅ CI green — all 4 checks pass (Analyze python, CodeQL, Build documentation); Scope vs AAASM-3558 — all parts covered, theme-overrides only, no new dependency:
Live-verified (Chromium, deep page
Two honest notes (both correct calls): the mike root-stub tag lands on release deploys (where Ready for approval + merge. (Post-merge: re-verify on the live versioned site that |
… hide on reject) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
♻️ Gate feedback widget on analytics consentPer owner decision, the feedback 👍/👎 widget is now gated on analytics consent — visible only after the user Accepts analytics cookies; hidden on Reject and while undecided. GA loading/reporting is left unchanged (still loads + sends cookieless pings for everyone — GA itself is not gated). ChangeIn the overridden var __aa_fb_consent = __md_get("__consent");
if (!(__aa_fb_consent && __aa_fb_consent.analytics)) return;The GA loader + Validation
GA fires a Commit: 🤖 Generated with Claude Code |
Description
On the published python-sdk docs site, Google Analytics never fired for a normal visitor —
window.gtagwasundefinedand zero/g/collecthits occurred on a content page. mkdocs-material uses basic Consent Mode: it loads gtag.js and wires the feedback widget only inside its generated__md_analytics(), which runs only after the analytics consent cookie is granted. Our consent config is opt-in (cookies.analytics.checked: false, off by default), so GA was never loaded at all.This PR brings python in line with the other four doc sites (node, hub, core, go), which all use advanced Consent Mode v2: gtag.js loads on every page with
analytics_storage: 'denied'default (cookieless modelled pings until consent, full hits after), so GA always works and is detectable.What changed (theme overrides under
docs/_overrides, no source/runtime code):partials/integrations/analytics/google.html— keep Material's__md_analytics()body (gtag loader +config+ feedback wiring) but assignwindow.gtag(so the tag is detectable), emit Consent Mode v2 default denied foranalytics_storage/ad_storage/ad_user_data/ad_personalizationbeforeconfig, and restoregrantedfrom Material's stored consent (__md_get('__consent').analytics).partials/integrations/analytics.html— call__md_analytics()unconditionally (Consent Mode now handles denial), instead of only when consent is granted. gtag is loaded exactly once.__md_analytics(), so it is always visible and its single consent-gated GAfeedbackevent flows through Material's own handler — no custom handler, no double-count. (Folds in the AAASM-3557 / PR [AAASM-3557] 🐛 (docs): Make feedback widget always visible + fix version-banner JS #169 feedback goal.)new URL("{{ base_url }}", location)crash fix, and theversions.jsondeep-page path fix (resolve frombase_urldeploy root).mike set-default --template, so Google's tag test detects GA on the bare-root/python-sdk/too.Type of Change
Breaking Changes
GDPR posture note: this changes python from "no GA data without consent" to "cookieless, modelled data until consent" (Consent Mode v2 denied-default) — consistent with the already-shipped posture on the other four doc sites. No raw analytics cookies are set until the visitor grants consent.
Related Issues
Testing
uv sync --group docsthenuv run mkdocs build --strictbuilds clean. The built site was served and driven with Playwright's bundled Chromium (require('playwright').chromium) on a deep content page (/concepts/architecture/):With NO consent:
typeof window.gtag === "function"✅/g/collecthit fires —gcs=G100(cookieless, analytics+ad denied),en=page_view,tid=G-L8JTJDG8G7✅feedbackevent indataLayer(delta = 1, not 2 — no double-count) ✅After granting analytics consent (seed Material's scoped
<root>.__consent = {"analytics":true}, reload):typeof window.gtag === "function"; Consent Mode updates toanalytics_storage: 'granted'✅feedbackevent delta; pre-filled GitHub issue link present ✅No
Failed to construct 'URL'error anywhere (the only console errors on that page are a pre-existing mermaid-diagram parse error + 2 unrelated 404 resources, not introduced here). Theversions.jsondeep-page fix resolves frombase_url(deploy root) — on a deep pagebase_url=../../→versionsUrl = <deploy-parent>/versions.json, i.e. one level above the version dir at any depth (the old code popped only the page's last segment and 404'd).Checklist
--strictclean; live browser verification above)🤖 Generated with Claude Code