Dev Log
GA4: gtag, 4 auto-tracked events, floating Feedback button, privacy disclosure
What was done
1. gtag.js installed in BaseLayout, production-only
src/layouts/BaseLayout.astro— gtag.js loaded viadocument.createElement("script")only whenlocation.hostname === "baileyengineers.com"(orwww.). Will NOT fire onprototype.baileyengage.com, localhost, or127.0.0.1. This means dev work doesn’t pollute the production property and Kelli’s prototype reviews don’t either.- Measurement ID:
G-8V80EQMMSB(the “Bailey Website - Production” GA4 property). - Uses
is:inlineso Astro doesn’t try to bundle/process the inlineif-guard script.
2. Three event types auto-wired (single delegated listener)
contact_cta_click— fires on click of any element with adata-analytics-eventattribute. The attribute value becomes thecta_labelevent parameter. Existingdata-analytics-eventannotations across the codebase (city page hero CTAs, role cards, project cards, etc.) automatically start firing — zero per-page edits needed.outbound_click— fires on click of any anchor whose href resolves to a different host. Capturesoutbound_urlandoutbound_hostparameters. Useful for understanding which content pushes users to LinkedIn, county sites, project portals, etc.contact_submit— fires on form submission inside/contact/. Capturestopicparameter from the URL?topic=query (or the form’sdata-topicattribute as fallback). Topic values:project,parcel-analysis,careers,iq-meet,feedback,unspecified.- All three guarded by
if (typeof window.gtag !== "function") return;— script is a no-op on prototype/localhost where gtag wasn’t loaded.
3. scroll_75 and other engagement events
Not coded — handled by GA4’s built-in Enhanced Measurement feature. Confirm it’s ON in GA4 admin: Admin → Data Streams → click the stream → “Enhanced measurement” toggle. GA4 will auto-track scroll depth, outbound clicks (we duplicate this for consistency), site search, video engagement, file downloads.
4. Floating “Feedback” FAB
- Bottom-right pill button, brand-black with precision-blue hover,
→glyph - Hidden on viewports under 600px (avoids covering content + accidental taps on phones)
- Navigates to
/contact/?topic=feedback(no JS — preserves zero-client-JS posture) - Annotated with
data-analytics-event="feedback_open"so the click is captured as acontact_cta_clickevent before the user navigates
5. Privacy page rewritten
The previous privacy page asserted “no cookies, no visitor analytics, no marketing pixels” — true at the time, false now. Updated to:
- New “What we collect from visitors” section explicitly disclosing GA4: aggregate, anonymized, IP-anonymized by default, city-level geographic only
- Removed the “no Google Analytics” line from “What we don’t collect”
- Added explicit “no marketing pixels / no cross-site tracking / no Google Signals” to make clear what’s still NOT happening
- Added opt-out instructions: GA Opt-out Browser Add-on, Do Not Track, uBlock Origin, etc.
- Added Google Analytics 4 to the “Third parties that process website data” list
- Added Cloudflare to the same list (was missing — Cloudflare fronts the production site)
- Added “Site feedback submissions go to careers@” line under the routing list
6. CLAUDE.md + memory updated
- New section in
CLAUDE.md: “WhatBaseLayout.astroautomatically handles (do NOT duplicate per-page)” — documents that GA4, OG images, JSON-LD schemas, title-suffix logic, BreadcrumbList, and the Feedback FAB all flow through BaseLayout/Breadcrumbs automatically. Future contributors won’t accidentally duplicate. - Memory:
reference_production_hosting.md— captures the deployment topology (Cloudflare → cPanel Droplet → /home/adminl/public_html/) so the Spaces confusion doesn’t repeat.
Why
Per Ryan: “GA4 only” decided, measurement ID G-8V80EQMMSB provided, careers@ confirmed as feedback destination, production-only firing confirmed. This is the install pass; the next session will be cutover-day work (deploy + Cloudflare cache purge + GA Realtime verification).
Files affected
Modified
src/layouts/BaseLayout.astro— gtag block in<head>, event-tracking script + Feedback FAB in<body>, FAB CSSsrc/pages/privacy.astro— Summary, “What we collect”, “What we don’t collect”, “Third parties” sections rewritten for GA4 honestyCLAUDE.md— new “What BaseLayout automatically handles” sectionsrc/content/devlog/MEMORY.mdindex updated (memory cluster)
Created
~/.claude/projects/.../memory/reference_production_hosting.md— deployment topology memorysrc/content/devlog/2026-04-26-ga4-events-feedback-button-privacy-update.md(this entry)
Cutover-day playbook (when Ryan FTPs to prod)
- Build:
npm run build:verify(already done — 20/20 PASS) - SFTP
dist/*to/home/adminl/public_html/via FileZilla (overwrite existing) - Purge Cloudflare cache — required, or visitors see old cached HTML for hours
- Verify GA4 events arriving: GA4 → Reports → Realtime → click around the live site, watch events appear
- Mark
contact_submitas a key event: GA4 → Admin → Events → toggle “Mark as key event” - Confirm Enhanced Measurement is on for the data stream (auto-tracks scroll, file downloads, outbound clicks, site search, video — useful redundancy)
Verification
- Build verify: 20/20 PASS
- gtag.js block present in
dist/index.html, hostname-guarded (only fires onbaileyengineers.com) - Feedback FAB present on all sampled pages (homepage, pillar, city page) — 1 instance per page (BaseLayout-injected, not duplicated)
- Privacy page contains “Google Analytics 4” disclosure (1 occurrence in summary, more in the third-party list)
- No analytics events fire on prototype/localhost (manual test:
npm run dev→ console shows no GA requests)