← back to Dw Domain Fleet
gtm: pre-stage GTM container snippet for commercialwallcovering + hollywoodwallcovering
94d2a07caae562dee986f2cb744c3b5b42a066af · 2026-08-17 12:48:38 -0700 · Steve Abrams
- shared/render.js: add gtmHeadSnippet() + gtmNoscript() functions; wire
both into head() so every page emits the GTM <script> in <head> and the
<noscript> iframe immediately after <body> — keyed off cfg.gtm per site
- sites/commercialwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- sites/hollywoodwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- server.js: expand CSP to allow www.googletagmanager.com in scriptSrc +
frameSrc; add google-analytics.com / analytics.google.com to connectSrc
Swap GTM-XXXXXXX -> real container id per site once Steve supplies them.
No deploy — drafted to yolo-queue/pending-approval/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
M server.jsM shared/render.jsM sites/commercialwallcovering.jsonM sites/hollywoodwallcovering.json
Diff
commit 94d2a07caae562dee986f2cb744c3b5b42a066af
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 17 12:48:38 2026 -0700
gtm: pre-stage GTM container snippet for commercialwallcovering + hollywoodwallcovering
- shared/render.js: add gtmHeadSnippet() + gtmNoscript() functions; wire
both into head() so every page emits the GTM <script> in <head> and the
<noscript> iframe immediately after <body> — keyed off cfg.gtm per site
- sites/commercialwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- sites/hollywoodwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- server.js: expand CSP to allow www.googletagmanager.com in scriptSrc +
frameSrc; add google-analytics.com / analytics.google.com to connectSrc
Swap GTM-XXXXXXX -> real container id per site once Steve supplies them.
No deploy — drafted to yolo-queue/pending-approval/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
server.js | 8 ++++++--
shared/render.js | 30 ++++++++++++++++++++++++++++--
sites/commercialwallcovering.json | 3 ++-
sites/hollywoodwallcovering.json | 3 ++-
4 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/server.js b/server.js
index 6916029..01ef06e 100644
--- a/server.js
+++ b/server.js
@@ -83,11 +83,15 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
- scriptSrc: ["'self'", "'unsafe-inline'"],
+ // 'unsafe-inline' required by GTM and inline theme/promo scripts.
+ // www.googletagmanager.com serves gtm.js + gtag/js (GA4 + GTM).
+ scriptSrc: ["'self'", "'unsafe-inline'", 'https://www.googletagmanager.com'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
imgSrc: ["'self'", 'data:', 'https:'],
fontSrc: ["'self'", 'https://fonts.gstatic.com', 'data:'],
- connectSrc: ["'self'"],
+ connectSrc: ["'self'", 'https://www.google-analytics.com', 'https://analytics.google.com'],
+ // GTM noscript <iframe src="https://www.googletagmanager.com/ns.html?id=...">
+ frameSrc: ['https://www.googletagmanager.com'],
frameAncestors: ["'none'"], objectSrc: ["'none'"], baseUri: ["'self'"],
formAction: ["'self'"], upgradeInsecureRequests: []
}
diff --git a/shared/render.js b/shared/render.js
index 8380cdc..644f695 100644
--- a/shared/render.js
+++ b/shared/render.js
@@ -460,6 +460,32 @@ function gtagSnippet(mid) {
<!-- /Google Analytics 4 -->`;
}
+/* gtmHeadSnippet / gtmNoscript — Google Tag Manager injection.
+ * id is sourced from cfg.gtm (per-site JSON field). When cfg.gtm is absent
+ * or empty these return '' so non-GTM sites are unaffected.
+ * Swap GTM-XXXXXXX for the real container id once Steve supplies it.
+ *
+ * gtmHeadSnippet goes inside <head> (as high as possible — placed just before
+ * the closing </head> tag, after GA4 and the anti-flash script).
+ * gtmNoscript goes as the FIRST child after <body> (Google requirement). */
+function gtmHeadSnippet(id) {
+ if (!id) return '';
+ return `<!-- Google Tag Manager -->
+<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
+new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
+j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
+})(window,document,'script','dataLayer','${id}');</script>
+<!-- End Google Tag Manager -->`;
+}
+function gtmNoscript(id) {
+ if (!id) return '';
+ return `<!-- Google Tag Manager (noscript) -->
+<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${id}"
+height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
+<!-- End Google Tag Manager (noscript) -->`;
+}
+
function head(cfg, title, desc, path, canonicalOverride, bodyClass) {
const url = 'https://' + cfg.domain + (path || '');
const canonical = canonicalOverride || url;
@@ -474,10 +500,10 @@ function head(cfg, title, desc, path, canonicalOverride, bodyClass) {
<meta property="og:url" content="${esc(url)}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${esc(title)}"><meta name="twitter:description" content="${esc(desc)}">
-${gtagSnippet(cfg.ga4)}<style>:root{--top-fg:#fff}</style>
+${gtagSnippet(cfg.ga4)}${gtmHeadSnippet(cfg.gtm)}<style>:root{--top-fg:#fff}</style>
<script>(function(){var s=localStorage.getItem('${cfg.slug}_theme');if(s)document.documentElement.dataset.theme=s;})();</script>
<style>${css(cfg)}</style>
-</head><body${bodyClass ? ` class="${esc(bodyClass)}"` : ''}>`;
+</head><body${bodyClass ? ` class="${esc(bodyClass)}"` : ''}>${gtmNoscript(cfg.gtm)}`;
}
function jsonld(cfg) {
diff --git a/sites/commercialwallcovering.json b/sites/commercialwallcovering.json
index d634598..4925356 100644
--- a/sites/commercialwallcovering.json
+++ b/sites/commercialwallcovering.json
@@ -31,5 +31,6 @@
"palette": "slate",
"font": "cormorant"
},
- "ga4": "G-22TD8SY6L4"
+ "ga4": "G-22TD8SY6L4",
+ "gtm": "GTM-XXXXXXX"
}
\ No newline at end of file
diff --git a/sites/hollywoodwallcovering.json b/sites/hollywoodwallcovering.json
index 96ef499..199f728 100644
--- a/sites/hollywoodwallcovering.json
+++ b/sites/hollywoodwallcovering.json
@@ -32,5 +32,6 @@
"palette": "gilt",
"font": "bodoni"
},
- "ga4": "G-ZSCWGG7FZW"
+ "ga4": "G-ZSCWGG7FZW",
+ "gtm": "GTM-XXXXXXX"
}
\ No newline at end of file
← aebcb81 GA4: inject gtag via render.js head() + ga4 IDs for commerci
·
back to Dw Domain Fleet
·
Fleet: keep showroom-only vendors (Phillip Jeffries) out of 3161f75 →