Skip to content

Latest commit

 

History

History
774 lines (623 loc) · 26.5 KB

File metadata and controls

774 lines (623 loc) · 26.5 KB

Google Analytics Implementation Plan - California DOGE

Implementation Overview

This plan provides step-by-step instructions for implementing Google Analytics 4 (GA4) tracking on the California DOGE website. Focus on tracking user interactions with government data: search terms, page navigation, filter usage, and data exploration patterns.

✅ UPDATE: Now includes three-layer analytics strategy with Web Vitals integration for comprehensive performance monitoring.

What We Need to Track

Core User Interactions

  • Search queries: What users search for in government data
  • Page views: Which department pages and data sections get attention
  • Filter selections: How users filter spending, workforce, and budget data
  • Text input: Search terms, form inputs, data queries
  • Workforce card clicks: Which employee/position cards users explore
  • External links: Clicks to official government sources (PropuBLICA, fiscal.ca.gov, etc.)
  • Navigation patterns: How users move through department data

Technical Metrics

  • Page load performance on data-heavy pages
  • Search result relevance and success rates
  • User flow through complex government data structures
  • Mobile vs desktop usage patterns for government transparency tools
  • ✅ NEW: Core Web Vitals monitoring (LCP, INP, CLS, FCP, TTFB)

Step 1: Environment Setup ✅ COMPLETED

Add Environment Variables

# Add to .env.local
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXX
NEXT_PUBLIC_GA_ENABLE_DEVELOPMENT=true  # For testing

✅ Status: Environment confirmed, Vercel Pro plan active, dependencies installed.

Step 2: Create Analytics Component ✅ COMPLETED

File: src/components/GoogleAnalytics.tsx

✅ Updated with marketing-focused configuration:

'use client';

import Script from 'next/script';
import { useEffect } from 'react';

declare global {
  interface Window {
    gtag: (command: string, targetId: string | Date, config?: any) => void;
    dataLayer: any[];
  }
}

interface GoogleAnalyticsProps {
  measurementId: string;
  enableDevelopment?: boolean;
}

export default function GoogleAnalytics({ 
  measurementId, 
  enableDevelopment = false 
}: GoogleAnalyticsProps) {
  const isProduction = process.env.NODE_ENV === 'production';
  const shouldLoadGA = isProduction || enableDevelopment;

  useEffect(() => {
    if (!shouldLoadGA) return;

    window.dataLayer = window.dataLayer || [];
    function gtag(...args: any[]) {
      window.dataLayer.push(args);
    }
    window.gtag = gtag;

    gtag('js', new Date());
    gtag('config', measurementId, {
      // ✅ UPDATED: Marketing-focused settings for social media and newsletter campaigns
      anonymize_ip: false, // Allow IP tracking for better demographics
      allow_google_signals: true, // Enable for audience insights and remarketing
      allow_ad_personalization_signals: true, // Enable for marketing insights
      restricted_data_processing: false, // Allow full data processing for marketing
      
      // Enhanced marketing tracking
      enhanced_conversions: true, // Better conversion tracking
      automatic_screen_view: true, // Track single-page app navigation
      
      send_page_view: true,
      debug_mode: !isProduction,
      
      // Custom dimensions for government transparency data
      custom_map: {
        'custom_parameter_1': 'department',
        'custom_parameter_2': 'data_type', 
        'custom_parameter_3': 'search_query',
        'custom_parameter_4': 'fiscal_year',
        'custom_parameter_5': 'user_engagement_level'
      }
    });

  }, [measurementId, shouldLoadGA, isProduction]);

  if (!shouldLoadGA) return null;

  return (
    <Script
      src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
      strategy="afterInteractive"
    />
  );
}

Step 3: Create Analytics Utility ✅ COMPLETED

File: src/lib/analytics.ts

✅ Updated with three-layer analytics strategy:

import { track as vercelTrack } from '@vercel/analytics';
import { 
  trackEvent as gaTrackEvent,
  trackDepartmentView as gaTrackDepartmentView,
  trackSearch as gaTrackSearch,
  trackFilterApplication as gaTrackFilterApplication,
  trackWorkforceCardClick as gaTrackWorkforceCardClick,
  trackExternalLinkClick as gaTrackExternalLinkClick,
  trackNewsletterSignup as gaTrackNewsletterSignup,
  trackSocialShare as gaTrackSocialShare,
  trackPageView as gaTrackPageView
} from '@/components/GoogleAnalytics';

/**
 * Three-Layer Analytics Interface for California DOGE
 * 
 * Layer 1: Google Analytics (GA4) - Marketing, user behavior, demographics
 * Layer 2: Vercel Analytics - Technical performance, simple event tracking  
 * Layer 3: Web Vitals - Core Web Vitals monitoring and optimization
 */
export const analytics = {
  // Track search queries
  search: (query: string, resultCount: number, searchType?: string) => {
    // GA4: Comprehensive search analytics with engagement metrics
    gaTrackSearch(query, resultCount, searchType);
    
    // Vercel: Simple search performance tracking
    const vercelProps: Record<string, string | number> = { 
      query: query.substring(0, 50), // Truncate for Vercel limits
      results: resultCount 
    };
    if (searchType) vercelProps.type = searchType;
    vercelTrack('Search', vercelProps);
  },

  // Track page views with context
  pageView: (pagePath: string, pageTitle?: string, department?: string) => {
    // GA4: Detailed page tracking with custom dimensions
    gaTrackPageView(pagePath, pageTitle);
    // Vercel: Automatic page view tracking (no manual call needed)
  },

  // Track filter usage
  filterUsed: (filterType: string, filterValue: string, pageContext?: string) => {
    // GA4: Detailed filter analytics with department context
    gaTrackFilterApplication(filterType, filterValue, pageContext);
    
    // Vercel: Simple filter tracking for UX optimization
    vercelTrack('Filter Applied', { 
      filter: filterType, 
      value: filterValue.substring(0, 50) // Truncate for Vercel limits
    });
  },

  // Track workforce card clicks
  workforceCardClick: (employeeTitle: string, department: string, salaryRange?: string) => {
    // GA4: Detailed workforce engagement tracking
    gaTrackWorkforceCardClick(department, parseInt(salaryRange?.replace(/\D/g, '') || '0'));
    
    // Vercel: Simple workforce interaction tracking
    vercelTrack('Workforce Card Click', { title: employeeTitle, dept: department, salary: salaryRange });
  },

  // Track external link clicks
  externalLink: (url: string, linkText?: string) => {
    // GA4: Comprehensive outbound link tracking
    gaTrackExternalLinkClick(url, linkText || 'unknown');
    
    // Vercel: Simple external link tracking
    vercelTrack('External Link', { url, text: linkText });
  },

  // Track department navigation
  departmentView: (departmentName: string, departmentSlug?: string, orgCode?: string) => {
    // GA4: Detailed department analytics with custom parameters
    gaTrackDepartmentView(departmentName, departmentSlug);
    
    // Vercel: Simple department tracking for performance correlation
    const vercelProps: Record<string, string> = { department: departmentName };
    if (departmentSlug) vercelProps.slug = departmentSlug;
    if (orgCode) vercelProps.code = orgCode;
    vercelTrack('Department View', vercelProps);
  },

  // ✅ NEW: Newsletter signup tracking
  newsletterSignup: (source: string) => {
    gaTrackNewsletterSignup(source);
    vercelTrack('Newsletter Signup', { source });
  },

  // ✅ NEW: Social media sharing tracking
  socialShare: (platform: string, contentType: string, contentId?: string) => {
    gaTrackSocialShare(platform, contentType, contentId);
    const vercelProps: Record<string, string> = { platform, content: contentType };
    if (contentId) vercelProps.id = contentId;
    vercelTrack('Social Share', vercelProps);
  },

  // Generic event tracking
  event: (eventName: string, parameters?: Record<string, any>) => {
    gaTrackEvent(eventName, parameters);
    vercelTrack(eventName, parameters);
  },

  // ✅ NEW: Performance tracking (Web Vitals integration)
  performance: (metric: string, value: number, pagePath?: string) => {
    // Vercel: Performance-focused tracking
    vercelTrack('Performance', { 
      metric,
      value: Math.round(value),
      page: pagePath?.substring(0, 50) || window.location.pathname.substring(0, 50)
    });
    
    // GA4: Core Web Vitals tracking with government data context
    if (['LCP', 'INP', 'CLS', 'FCP', 'TTFB'].includes(metric)) {
      gaTrackEvent('web_vitals', {
        metric_name: metric,
        metric_value: value,
        page_path: pagePath || window.location.pathname
      });
    }
  }
};

// ✅ NEW: Utility for tracking Core Web Vitals (called by WebVitalsTracker)
export const trackWebVitals = (metric: any) => {
  analytics.performance(metric.name, metric.value, window.location.pathname);
};

Step 4: Update Root Layout ✅ COMPLETED

File: src/app/layout.tsx

✅ Updated with three-layer analytics setup:

import GoogleAnalytics from '@/components/GoogleAnalytics';
import { Analytics } from '@vercel/analytics/react';
import WebVitalsTracker from '@/components/WebVitalsTracker';

// Add this inside the <body> tag, before closing </body>
{/* Three-Layer Analytics Setup */}
{/* Layer 1: Vercel Analytics - Performance and Core Web Vitals */}
<Analytics />

{/* Layer 2: Google Analytics - Marketing and User Behavior */}
<GoogleAnalytics 
  measurementId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || 'G-XXXXXXX'}
  enableDevelopment={process.env.NEXT_PUBLIC_GA_ENABLE_DEVELOPMENT === 'true'}
/>

{/* Layer 3: Web Vitals - Performance Monitoring and Optimization */}
<WebVitalsTracker />

✅ NEW: Step 5: Web Vitals Integration ✅ COMPLETED

File: src/components/WebVitalsTracker.tsx

✅ NEW: Core Web Vitals monitoring for government data optimization:

'use client';

import { useEffect } from 'react';
import { trackWebVitals } from '@/lib/analytics';

/**
 * Web Vitals Tracker for California DOGE
 * 
 * Monitors Core Web Vitals to optimize government transparency site performance:
 * - LCP: How fast department/budget pages load
 * - INP: How responsive search and filters feel (replaces FID)
 * - CLS: How stable data visualizations are
 * - FCP: How quickly users see initial content
 * - TTFB: Server response time for large datasets
 */
export default function WebVitalsTracker() {
  useEffect(() => {
    // Only run in browser environment
    if (typeof window === 'undefined') return;

    // Dynamically import web-vitals to avoid SSR issues
    import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
      // Track Core Web Vitals for government data optimization
      onLCP(trackWebVitals);
      onINP(trackWebVitals);
      onCLS(trackWebVitals);
      onFCP(trackWebVitals);
      onTTFB(trackWebVitals);
    }).catch((error) => {
      console.warn('Failed to load web-vitals library:', error);
    });
  }, []);

  return null;
}

Step 6: Implementation in Components ✅ COMPLETED

✅ Status: Analytics tracking implemented across key components with real user interactions.

✅ Search Components - IMPLEMENTED

Files Updated: src/components/DepartmentSearch.tsx, src/components/EnhancedSearch.tsx, src/app/search/page.tsx

// DepartmentSearch.tsx - Department search tracking
const handleDepartmentClick = (department: DepartmentSearchResult) => {
  analytics.departmentView(department.name, department.slug);
  onClose();
};

// Search query tracking with results count
useEffect(() => {
  if (searchTerm.trim()) {
    analytics.search(searchTerm, filtered.length, 'department_search');
  }
}, [searchTerm, departments]);

// EnhancedSearch.tsx - Enhanced search with multiple types
const performSearch = async (query: string, types: string[]) => {
  // ... search logic ...
  if (data) {
    const totalResults = (data.departments?.length || 0) + 
                       (data.vendors?.length || 0) + 
                       (data.programs?.length || 0) + 
                       (data.funds?.length || 0) + 
                       (data.keywords?.length || 0);
    analytics.search(query.trim(), totalResults, 'enhanced_search');
  }
};

// Search page form submissions
const handleSearch = (e: React.FormEvent) => {
  e.preventDefault();
  if (query.trim()) {
    analytics.search(query.trim(), 0, 'search_page_form');
    // ... form submission logic ...
  }
};

✅ Filter Components - IMPLEMENTED

Files Updated: src/app/workforce/page.tsx, src/app/search/page.tsx, src/components/EnhancedSearch.tsx

// Workforce page filters
onClick={() => {
  analytics.filterApplied('view_mode', 'parent-only', 'workforce_page');
  setViewMode('parent-only');
}}

onChange={(e) => {
  const year = e.target.value;
  if (year.match(/^\d{4}$/)) {
    analytics.filterApplied('fiscal_year', year, 'workforce_page');
    setSelectedFiscalYear(year as AnnualYear);
  }
}}

// Search page filters
const toggleType = (type: string) => {
  analytics.filterApplied('search_type', type, 'search_page');
  // ... filter logic ...
};

const handleExcludeCommonChange = (value: string) => {
  analytics.filterApplied('exclude_common', value, 'search_page');
  // ... filter logic ...
};

// Enhanced search type filters
const toggleType = (type: string) => {
  analytics.filterApplied('search_type', type, 'enhanced_search');
  // ... filter logic ...
};

✅ Workforce Card Components - IMPLEMENTED

Files Updated: src/app/workforce/page.tsx

// Department card click tracking
const handleCardClick = () => {
  // Track workforce card click with employee count
  analytics.workforceCardClick(department.name, _workforceData);
  onClick();
};

// Department selection tracking
const handleSelectDepartment = (department: DepartmentData) => {
  // Track department view
  analytics.departmentView(department.name, department._slug);
  setSelectedDepartmentName(department.name);
};

✅ External Link Components - IMPLEMENTED

Files Updated: src/app/workforce/page.tsx, src/app/search/page.tsx, src/app/spend/page.tsx

// Government sources tracking
<a 
  href="https://publicpay.ca.gov/Reports/State/State.aspx" 
  target="_blank" 
  rel="noopener noreferrer"
  className="text-blue-600 hover:underline"
  onClick={() => analytics.externalLinkClick('https://publicpay.ca.gov/Reports/State/State.aspx', 'workforce_sources')}
>
  California State Controller's Office - Government Compensation
</a>

// Vendor lookup tracking (spend page)
<a 
  href={`https://projects.propublica.org/nonprofits/search?q=${encodeURIComponent(record.vendor)}`}
  target="_blank"
  rel="noopener noreferrer"
  className="text-blue-300 hover:underline mr-2"
  onClick={() => analytics.externalLinkClick(`https://projects.propublica.org/nonprofits/search?q=${encodeURIComponent(record.vendor)}`, 'vendor_lookup_propublica')}
>
  ProPublica
</a>

✅ Page View Tracking - IMPLEMENTED

Files Updated: src/app/search/page.tsx, src/app/workforce/page.tsx

// Search page tracking
useEffect(() => {
  analytics.pageView('/search', 'Search California Government Data');
}, []);

// Workforce page tracking
useEffect(() => {
  analytics.pageView('/workforce', 'California State Government Workforce');
}, [transactionId, searchParams]);

✅ Error Tracking - IMPLEMENTED

Files Updated: src/components/EnhancedSearch.tsx

// Search error tracking
} catch (error) {
  console.error('Error performing search:', error);
  setIsLoading(false);
  setSearchResults(null);
  
  // Track search errors
  analytics.error('search_error', error instanceof Error ? error.message : 'Unknown search error');
}

✅ Result Click Tracking - IMPLEMENTED

Files Updated: src/components/EnhancedSearch.tsx

// Enhanced search result clicks
const handleResultClick = (item: SearchItem | KeywordItem) => {
  if (item.type === 'keyword') {
    gaTrackEvent('keyword_click', { keyword: item.term, sources_count: item.sources.length });
  } else {
    switch (item.type) {
      case 'department':
        analytics.departmentView(item.term, item.id);
        break;
      case 'vendor':
        analytics.vendorView(item.term);
        break;
      case 'program':
        analytics.programView(item.id, item.term);
        break;
      case 'fund':
        gaTrackEvent('fund_view', { fund_name: item.term, fund_id: item.id });
        break;
    }
  }
  // ... navigation logic ...
};

✅ Implementation Summary

Total Components Updated: 6 files

  • src/components/DepartmentSearch.tsx - Department search tracking
  • src/components/EnhancedSearch.tsx - Enhanced search with error handling
  • src/app/search/page.tsx - Search page with filters and external links
  • src/app/workforce/page.tsx - Workforce cards, filters, and external links
  • src/app/spend/page.tsx - Vendor lookup and external links

Analytics Events Implemented:

  • ✅ Search queries with result counts and search types
  • ✅ Filter applications (view mode, fiscal year, search types, limits)
  • ✅ Workforce card clicks with employee counts
  • ✅ Department navigation and views
  • ✅ External link clicks with context tracking
  • ✅ Page view tracking with titles
  • ✅ Error tracking for search failures
  • ✅ Result click tracking by type (department, vendor, program, fund, keyword)

Ready for Testing: All tracking calls are implemented and ready for GA4 configuration and validation.

Step 7: GA4 Configuration ✅ CORRECTED

🚨 Important Correction

Custom events are NOT created in the GA4 interface. Your custom events are already implemented in your code (Steps 1-6). GA4 will automatically start collecting them once deployed. The GA4 interface is used for:

  1. Creating custom dimensions/metrics to access event parameters in reports
  2. Modifying existing events that are already being collected
  3. Creating new events based on existing collected events
  4. Marking events as key events (conversions)

🎯 Step 7A: Deploy and Verify Events Are Being Sent

Your custom events are already coded and ready to go. First, verify they're working:

1. Deploy Your Code

  • Deploy your implementation with analytics enabled
  • Set NEXT_PUBLIC_GA_ENABLE_DEVELOPMENT=true for testing

2. Test Event Tracking

Navigate to: GA4 Property → Configure → DebugView

  • Perform actions on your site (search, filter, click workforce cards)
  • Verify these events appear in DebugView:
    • search with parameters: search_term, result_count, search_type
    • filter_applied with parameters: filter_type, filter_value, page_context
    • workforce_card_click with parameters: department_name, employee_count
    • department_view with parameters: department_name, department_slug
    • external_link_click with parameters: link_url, link_context
    • web_vitals with parameters: metric_name, metric_value, page_path

3. Wait 24-48 Hours

  • Events will start appearing in standard reports after processing
  • DebugView shows real-time data immediately

📊 Step 7B: Create Custom Dimensions (REQUIRED)

Navigate to: GA4 Property → Configure → Custom definitions → Custom dimensions → Create custom dimension

To access event parameters in your reports, create these custom dimensions:

Government Data Dimensions

  1. Department Name

    • Dimension name: Department
    • Scope: Event
    • Event parameter: department_name
    • Description: "California government department"
  2. Search Type

    • Dimension name: Search Type
    • Scope: Event
    • Event parameter: search_type
    • Description: "Type of search performed"
  3. Filter Type

    • Dimension name: Filter Type
    • Scope: Event
    • Event parameter: filter_type
    • Description: "Type of filter applied"
  4. Page Context

    • Dimension name: Page Context
    • Scope: Event
    • Event parameter: page_context
    • Description: "Page where interaction occurred"
  5. Link Context

    • Dimension name: Link Context
    • Scope: Event
    • Event parameter: link_context
    • Description: "Context of external link clicks"

Performance Dimensions

  1. Performance Metric

    • Dimension name: Performance Metric
    • Scope: Event
    • Event parameter: metric_name
    • Description: "Core Web Vitals metric name"
  2. Error Type

    • Dimension name: Error Type
    • Scope: Event
    • Event parameter: error_type
    • Description: "Type of error encountered"

📈 Step 7C: Create Custom Metrics (OPTIONAL)

Navigate to: GA4 Property → Configure → Custom definitions → Custom metrics → Create custom metric

For numerical event parameters:

  1. Result Count

    • Metric name: Search Result Count
    • Scope: Event
    • Event parameter: result_count
    • Unit of measurement: Standard
  2. Employee Count

    • Metric name: Employee Count
    • Scope: Event
    • Event parameter: employee_count
    • Unit of measurement: Standard
  3. Performance Value

    • Metric name: Performance Value
    • Scope: Event
    • Event parameter: metric_value
    • Unit of measurement: Milliseconds

🎯 Step 7D: Mark Key Events as Conversions

Navigate to: GA4 Property → Configure → Events

Wait for your events to appear in the Events list (24-48 hours), then:

  1. Find search event → Toggle "Mark as key event" ✅
  2. Find department_view event → Toggle "Mark as key event" ✅
  3. Find workforce_card_click event → Toggle "Mark as key event" ✅
  4. Find vendor_view event → Toggle "Mark as key event" ✅
  5. Find program_view event → Toggle "Mark as key event" ✅

These will now count as conversions in your reports.

⚙️ Step 7E: Enhanced Measurement (RECOMMENDED)

Navigate to: GA4 Property → Data streams → Web → website → Enhanced measurement

Enable these automatic measurements that complement your custom tracking:

  • Page views - Automatic page tracking
  • Scrolls - 90% page scroll depth
  • Outbound clicks - Clicks to external domains
  • Site search - URL-based search tracking
  • File downloads - PDF, doc, zip downloads
  • Form interactions - Form starts and submissions

🔧 Step 7F: Optional Event Modifications

Navigate to: GA4 Property → Data streams → Web → website → Modify events

Only if needed, you can modify existing events. For example:

Example: Create "Contact Form Submission" Event

  • Modify existing events: Create new event from page_view
  • Matching conditions: page_location contains thank-you or confirmation
  • New event name: contact_form_submission

Example: Filter High-Value Searches

  • Modify existing events: Create new event from search
  • Matching conditions: result_count greater than 10
  • New event name: successful_search

📱 Step 7G: Configure Data Retention

Navigate to: GA4 Property → Configure → Data settings → Data retention

  • Event data retention: 14 months (maximum free tier)
  • Reset user data on new activity: ON (recommended)

🚀 Implementation Checklist

✅ Code Implementation (Completed in Steps 1-6)

  • Custom events implemented in components
  • Analytics interface created
  • Web Vitals tracking added
  • Three-layer analytics setup complete

🔄 GA4 Configuration (Do Now)

  • Deploy code and verify events in DebugView
  • Create 7 custom dimensions for event parameters
  • Create 3 custom metrics for numerical parameters
  • Mark 5 events as key events (conversions)
  • Enable Enhanced Measurement features
  • Set data retention to 14 months

📊 Reports and Analysis (After 48 Hours)

  • Verify events appear in standard reports
  • Check custom dimensions populate with data
  • Confirm key events show in conversions report
  • Create custom explorations using new dimensions

💡 Key Insights

  1. Your events are already implemented - GA4 will automatically collect them once deployed
  2. Custom dimensions are essential - Without them, you can't access event parameters in reports
  3. Key events = conversions - Mark important events to measure success
  4. Enhanced Measurement is free - Provides additional automatic tracking
  5. Data takes 24-48 hours - Be patient for events to appear in standard reports

This corrected approach aligns with how GA4 actually works: events are coded (✅ done), then configured in the interface for reporting and analysis.

Step 8: Testing

Development Testing

  1. Set NEXT_PUBLIC_GA_ENABLE_DEVELOPMENT=true
  2. Open browser dev tools → Network tab
  3. Perform actions (search, filter, click cards)
  4. Verify GA events are being sent
  5. Check GA4 DebugView for real-time events
  6. ✅ NEW: Monitor Web Vitals in browser console

Production Validation

  1. Deploy with analytics enabled
  2. Test key user flows
  3. Verify events appear in GA4 within 24-48 hours
  4. Check custom dimensions are populated
  5. ✅ NEW: Monitor Core Web Vitals in Vercel Analytics dashboard

Implementation Priority

Phase 1 (Essential) ✅ COMPLETED

  1. Basic page view tracking
  2. Search query tracking
  3. Filter usage tracking
  4. ✅ NEW: Web Vitals monitoring

Phase 2 (Important) ✅ COMPLETED

  1. Workforce card click tracking
  2. Department navigation tracking
  3. External link tracking
  4. ✅ NEW: Newsletter signup tracking
  5. ✅ NEW: Social media sharing tracking

Phase 3 (Nice to have)

  1. Time on page tracking
  2. Scroll depth tracking
  3. Form abandonment tracking
  4. ✅ NEW: Performance-behavior correlation analysis

✅ NEW: Three-Layer Analytics Benefits

🎯 Google Analytics (GA4) - Marketing Focus

  • User behavior, demographics, marketing attribution
  • Newsletter signups, social shares, conversion tracking
  • Custom dimensions for government data analysis

⚡ Vercel Analytics - Performance Focus

  • Page performance, Core Web Vitals, technical metrics
  • Simple event tracking, UX optimization data

🔍 Web Vitals - User Experience Quality

  • LCP, INP, CLS, FCP, TTFB monitoring
  • Performance correlation with user behavior
  • Government data optimization insights

💰 Total Cost

  • Vercel Pro: $20/month (already using)
  • Google Analytics: Free
  • Web Vitals: Free (open-source)
  • Total Additional Cost: $0

This plan provides concrete steps for implementing comprehensive analytics tracking focused on actual user interactions with California government data that exist in your codebase, now enhanced with performance monitoring and marketing optimization.