Multi-Site GA Tag Audit & Modal Widget Refactor: Fixing Cross-Property Contamination and Unifying Booking UX
During a routine Google Search Console audit across our managed properties, we discovered a critical issue: Google Analytics tags were being accessed from incorrect Google accounts, and booking modal functionality was inconsistent across properties. This post documents the audit methodology, remediation approach, and the architectural refactor we implemented to unify the booking experience while maintaining clean property boundaries.
The Problem: Cross-Property Contamination & Account Ownership
When auditing GSC submissions for quickdumpnow.com, we found the property was being verified and accessed through jadasailing@gmail.com—the same account that manages sailjada.com. While GA tag *values* were correct (QDN used G-539T97NM1Z, JADA used G-N6HKL4KLKT), the account ownership meant reporting and access controls were blurred across distinct business entities.
Additionally, we discovered that three critical QDN inner pages were missing GA tags entirely:
/book/track/service-areas/carlsbad/
And more pressingly: sailjada.com/crew/ had a broken, non-modal booking widget that looked unprofessional compared to the sophisticated date-picker modal on queenofsandiego.com.
Audit Methodology: Systematic Tag Scanning
Rather than spot-check, we built a comprehensive audit across all six properties. Our approach:
- Homepage scan: Check GA tags and robots.txt on primary domains
- Subpage recursion: Python script to fetch all subpages and extract GA tag values via regex
- Cross-property validation: Build a matrix of which GA tags appear on which sites
- Missing-tag detection: Flag pages that should have a GA tag but don't
The Python scanner used requests to fetch HTML, then applied regex patterns to extract both modern GA4 tags (G-[A-Z0-9]+) and legacy Universal Analytics tags (UA-[0-9]+-[0-9]+). We logged exact byte ranges of script tags and page URLs to our memory system for precise remediation.
The Modal Widget Problem: Architecture Fragmentation
On queenofsandiego.com, the booking modal was hardcoded into the homepage HTML and invoked by CTA buttons using CSS classes and inline JavaScript. The modal itself—a sophisticated date picker with Stripe integration and real-time availability checking—was embedded as a single block of CSS, HTML, and JavaScript.
On sailjada.com/crew/, the booking widget was a basic, non-modal form that lacked the visual polish and interactivity users expected. Worse, the modal code wasn't abstracted as a reusable asset; it lived only in the QOD homepage HTML file.
Decision: Extract the modal into a shared JavaScript asset that could be loaded on any page, in any site, and invoked by multiple button selectors.
Technical Implementation: Modal Extraction & Deployment
Step 1: Audit Modal Structure
We pulled the complete queenofsandiego.com homepage HTML and identified:
- CSS block: ~600 lines defining
.jada-modal,.jada-calendar,.jada-stripe-containerstyles - HTML block: Modal wrapper with anchor IDs, form fields, hidden Stripe div, and QR code image tags
- JavaScript block: IIFE wrapping ~1200 lines of event handlers, date logic, Stripe tokenization, and button-click dispatchers
We discovered that CTA buttons used multiple selectors: homepage had .reserve-btn, while crew page used .check-availability. The extraction needed to support both.
Step 2: Extract & Refactor as Shared Asset
We created /assets/js/jada-modal.js with the following structure:
// Global namespace to avoid collisions
window.JadaModal = window.JadaModal || {};
(function() {
// Inject CSS into <head> dynamically
const styles = `... all modal CSS ...`;
const styleEl = document.createElement('style');
styleEl.textContent = styles;
document.head.appendChild(styleEl);
// Inject HTML modal container into DOM
const modalHTML = `... full modal structure ...`;
document.body.insertAdjacentHTML('beforeend', modalHTML);
// Bind CTA buttons to modal trigger
const buttonSelectors = ['.reserve-btn', '.check-availability'];
buttonSelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(btn => {
btn.addEventListener('click', () => window.JadaModal.open());
});
});
// Export public API
window.JadaModal.open = function() { /* ... */ };
window.JadaModal.close = function() { /* ... */ };
})();
Why this approach? An IIFE prevents variable leakage, dynamic style injection works on any page, and we fixed QR image paths from relative to absolute URLs (e.g., /assets/images/zelle-qr.png instead of broken relative references).
Step 3: Patch All Affected Subpages
We identified 17 subpages on sailjada.com that needed the modal. Using a bulk-patching strategy:
- Sync production versions from S3 to local cache
- Add
<script src="/assets/js/jada-modal.js"></script>before</body>(after existing Stripe.js and GA tags) - Replace inline booking forms with single
<button class="reserve-btn">...</button>elements - Re-upload to staging S3 bucket (
sailjada-com-staging)
We also added a lint guard in deploy_qos.sh to prevent malformed JavaScript deployments:
#!/bin/bash
# deploy_qos.sh — with lint validation
JSHINT_PATH="./node_modules/.bin/jshint"
if ! $JSHINT_PATH assets/js/jada-modal.js; then
echo "❌ JSHint failed. Aborting deploy."
exit 4
fi
# Proceed with S3 sync + CloudFront invalidation
aws s3 sync ./dist/ s3://sailjada-com-prod/ --delete
aws cloudfront create-invalidation --distribution-id E2ABC123XYZ --paths "/*"
Exit code 4 on lint failure prevents