From e37dff716f4574bd1bb829090333cc8dbbe7af82 Mon Sep 17 00:00:00 2001 From: Greg Miller Date: Thu, 13 Aug 2026 11:16:55 -0400 Subject: [PATCH 1/4] chore(gtag): add analytics tracking - add new tracking property - update docs --- .env.example | 6 ++++++ README.md | 6 +++++- src/env.d.ts | 2 ++ src/layouts/BaseLayout.astro | 38 ++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 5f200cf..c2c9cd2 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,12 @@ PUBLIC_SANITY_STRICT_MODE=false # Optional. Used by `` in BaseLayout. PUBLIC_FB_APP_ID= +# ---- Google Analytics (gtag.js / GA4) -------------------------------------- +# Optional. When set, BaseLayout loads gtag and tracks page views +# (including Astro ClientRouter navigations). +# Example: PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXX +PUBLIC_GA_MEASUREMENT_ID= + # ---- Deploy trigger (server-only) ----------------------------------------- # Used by /deploy and /api/trigger-deploy in local dev + Vercel. # Do NOT prefix with PUBLIC_. Keep these secret. diff --git a/README.md b/README.md index 63e8776..ec11f4d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Release history: [CHANGELOG.md](CHANGELOG.md). - **Three.js background** — Particle field and wireframe accents (respects reduced motion; pause control) - **Design system** — Scoped component styles plus tokens in `src/styles/global.css` (self-hosted **Tiny5** + **Nunito**) - **Configurable imagery** — Header brand, org logos, footer sponsors (`src/data/siteLogos.ts`) -- **Analytics** — [Vercel Analytics](https://vercel.com/docs/analytics) (`@vercel/analytics`) +- **Analytics** — [Vercel Analytics](https://vercel.com/docs/analytics) (`@vercel/analytics`); optional GA4 via `PUBLIC_GA_MEASUREMENT_ID` - **TypeScript**, sitemap, client-side transitions (`ClientRouter`) ## Requirements @@ -56,9 +56,11 @@ npm run preview PUBLIC_SANITY_PROJECT_ID=your_project_id PUBLIC_SANITY_DATASET=production PUBLIC_SANITY_STRICT_MODE=false +PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXX ``` - Set `PUBLIC_SANITY_STRICT_MODE=true` in production to fail builds when Sanity fetches fail instead of silently shipping empty fallback content. +- Set `PUBLIC_GA_MEASUREMENT_ID` to enable Google Analytics 4 (gtag). Leave unset to skip loading the tag. **Standalone Studio** (`studio-hack-michigan-/`) resolves project and dataset in `sanity.env.ts`: @@ -70,6 +72,8 @@ The Sanity CLI loads `.env` from `studio-hack-michigan-/` when you run `sanity d For Facebook Open Graph sharing you can set `PUBLIC_FB_APP_ID` (see `src/env.d.ts`). +For Google Analytics 4, set `PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXX`. When present, `BaseLayout` loads gtag.js and records page views on first load and on Astro `ClientRouter` navigations. + ### Deploy trigger env The internal `/deploy/` page posts to `/api/trigger-deploy/`, which forwards to an n8n webhook. diff --git a/src/env.d.ts b/src/env.d.ts index 298dde6..9b218ed 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -12,6 +12,8 @@ declare module "*.astro" { interface ImportMetaEnv { /** Facebook App ID for `fb:app_id` (Open Graph). Set in Vercel / `.env`. */ readonly PUBLIC_FB_APP_ID?: string; + /** GA4 Measurement ID (e.g. `G-XXXXXXXX`). When set, BaseLayout injects gtag. */ + readonly PUBLIC_GA_MEASUREMENT_ID?: string; /** Sanity project ID. Defaults to the HackMI project in `astro.config.mjs` if unset. */ readonly PUBLIC_SANITY_PROJECT_ID?: string; /** Sanity dataset. Defaults to `production` in `astro.config.mjs` if unset. */ diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index d344569..731fa6c 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -24,6 +24,7 @@ const { } = Astro.props; const fbAppId = import.meta.env.PUBLIC_FB_APP_ID?.trim(); +const gaMeasurementId = import.meta.env.PUBLIC_GA_MEASUREMENT_ID?.trim(); const defaultOgPath = `${base}social-card.jpg`; const image = imageProp ?? defaultOgPath; @@ -151,6 +152,43 @@ const selfHostedFontFaces = ` /> {title} + + { + gaMeasurementId && ( + <> + + + ) + } From 65b3712754058235844333e50df74d46db59d8d1 Mon Sep 17 00:00:00 2001 From: Greg Miller Date: Thu, 13 Aug 2026 11:20:58 -0400 Subject: [PATCH 2/4] Update BaseLayout.astro Removed the redundant window.gtag = gtag; from src/layouts/BaseLayout.astro. The function gtag() declaration already puts it on window. --- src/layouts/BaseLayout.astro | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 731fa6c..ee9785b 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -165,7 +165,6 @@ const selfHostedFontFaces = ` function gtag() { dataLayer.push(arguments); } - window.gtag = gtag; // Avoid double-init if ClientRouter re-evaluates head scripts if (!window.__hackmiGtagInit) { From 64ad75e50b7130a7974a1987408728a7ccbd1cc0 Mon Sep 17 00:00:00 2001 From: Greg Miller Date: Thu, 13 Aug 2026 11:38:09 -0400 Subject: [PATCH 3/4] fix(gtag): match the official gtag pattern without arguments. - pushing via window.dataLayer explicitly --- src/layouts/BaseLayout.astro | 4 ++-- tsconfig.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index ee9785b..ca8e650 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -162,8 +162,8 @@ const selfHostedFontFaces = ` /> +