1. Legal Foundations & Regulatory Standards
Building a compliant web application in the European Economic Area (EEA) and the United Kingdom requires adhering strictly to two overlapping privacy frameworks mapped in our Interactive GDPR Compliance Matrix:
- The ePrivacy Directive (2002/58/EC as amended by 2009/136/EC): Specifically Article 5(3), which mandates prior, informed user consent before storing or accessing information on a user’s terminal equipment (cookies,
localStorage, IndexedDB, canvas fingerprinting). - The General Data Protection Regulation (EU 2016/679 - GDPR): Specifically Article 4(11) defining valid consent, Article 7 governing conditions for consent, and European Data Protection Board (EDPB) Guidelines 05/2020. You can also generate turnkey disclosures using our Public Trust Center Compiler.
The CJEU Planet49 Landmark Ruling (Case C-673/17)
The Court of Justice of the European Union (CJEU) established key legal precedents for online tracking:
- No Pre-Ticked Checkboxes: Pre-selected option boxes do not constitute active, valid consent.
- Strict Opt-in Requirement: Non-essential tracking cookies (Analytics, Advertising, Personalization) must remain completely blocked until the user takes affirmative action.
- Equal Prominence: Rejecting consent must be as effortless as accepting consent. “Reject All” buttons must be positioned alongside “Accept All” with identical visual weight, font sizing, and button contrast. Dark patterns (such as hiding the reject option inside sub-menus or using low-contrast text) violate Article 7(3).
Google Consent Mode v2 Compliance
Beginning March 2024, Google mandated Consent Mode v2 for all applications using Google Ads, Google Analytics 4 (GA4), or Floodlight tags in the EEA/UK. Consent Mode v2 introduces two mandatory parameter signals alongside legacy flags:
ad_storage: Enables storage (such as cookies) related to advertising.analytics_storage: Enables storage (such as cookies) related to analytics (e.g. visit duration).ad_user_data: Controls whether user data can be sent to Google for online advertising purposes.ad_personalization: Controls whether personalized advertising (remarketing) can be enabled.
2. Consent Mode v2 Initialization Architecture
To prevent race conditions where tracking scripts execute before default consent states are declared, the initialization snippet must be placed as the very first script block in the HTML <head>, preceding Google Tag Manager (GTM), GA4, or any ad scripts.
<!-- HTML Head Initialization - Positioned ABOVE GTM/GA4 Scripts -->
<script>
// Initialize dataLayer array
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
// Set DEFAULT consent states to 'denied' BEFORE loading analytics or marketing tags
gtag('consent', 'default', {
'ad_storage': 'denied',
'analytics_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'functionality_storage': 'granted', // Essential cookies for core site functionality
'security_storage': 'granted', // Essential security/CSRF tokens
'wait_for_update': 500 // Wait up to 500ms for saved consent retrieval
});
// Enable advanced redaction for ad signals when ad_storage is denied
gtag('set', 'ads_data_redaction', true);
gtag('set', 'url_passthrough', true);
</script>
3. Production-Ready Vanilla JavaScript Consent Engine
Below is a complete, modular, framework-agnostic JavaScript implementation (CookieConsentManager) that handles local preference persistence, granular modal controls, dynamic script injection, and signal synchronization.
/**
* SovereignShield Cookie & Privacy Consent Manager v2
* Zero-telemetry, local-first client-side implementation.
*/
export class CookieConsentManager {
constructor(options = {}) {
this.STORAGE_KEY = options.storageKey || 'sovereign_consent_state_v2';
this.bannerId = 'sovereign-gdpr-banner';
this.modalId = 'sovereign-gdpr-modal';
this.callbacks = options.onConsentUpdate || null;
this.defaultState = {
essential: true,
analytics: false,
marketing: false,
functional: false,
timestamp: null,
version: '2.0.0'
};
this.init();
}
init() {
const savedConsent = this.getSavedConsent();
if (savedConsent) {
this.applyConsent(savedConsent, false);
} else {
this.renderBanner();
}
this.attachFooterListeners();
}
getSavedConsent() {
try {
const data = localStorage.getItem(this.STORAGE_KEY);
return data ? JSON.parse(data) : null;
} catch (err) {
console.warn('[ConsentManager] Unable to access localStorage:', err);
return null;
}
}
saveConsent(state) {
const consentPayload = {
...state,
essential: true,
timestamp: new Date().toISOString(),
version: '2.0.0'
};
try {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(consentPayload));
} catch (err) {
console.error('[ConsentManager] Failed to persist consent state:', err);
}
this.applyConsent(consentPayload, true);
this.closeBanner();
this.closeModal();
}
applyConsent(state, triggerEvents = true) {
// Synchronize with Google Consent Mode v2 API
if (typeof window.gtag === 'function') {
window.gtag('consent', 'update', {
'analytics_storage': state.analytics ? 'granted' : 'denied',
'ad_storage': state.marketing ? 'granted' : 'denied',
'ad_user_data': state.marketing ? 'granted' : 'denied',
'ad_personalization': state.marketing ? 'granted' : 'denied',
'functionality_storage': state.functional ? 'granted' : 'denied'
});
}
// Trigger dynamic script execution for granted categories
if (state.analytics) this.loadAnalyticsScripts();
if (state.marketing) this.loadMarketingScripts();
// Dispatch Custom DOM Event for internal components
window.dispatchEvent(new CustomEvent('sovereignConsentChanged', { detail: state }));
if (triggerEvents && typeof this.callbacks === 'function') {
this.callbacks(state);
}
}
loadAnalyticsScripts() {
if (window.analyticsScriptsLoaded) return;
window.analyticsScriptsLoaded = true;
// Dynamically inject GA4 or PostHog scripts only after consent is confirmed
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-YOURTRACKINGID';
document.head.appendChild(script);
}
loadMarketingScripts() {
if (window.marketingScriptsLoaded) return;
window.marketingScriptsLoaded = true;
// Inject Marketing Pixel Scripts
console.log('[ConsentManager] Marketing consent granted. Initializing ad pixels.');
}
renderBanner() {
if (document.getElementById(this.bannerId)) return;
const bannerHtml = `
<div id="${this.bannerId}" class="consent-banner-wrapper" role="region" aria-label="Cookie Consent Banner">
<div class="consent-banner-content">
<div class="consent-text">
<h3>Cookie & Privacy Preferences</h3>
<p>
We enforce strict GDPR Article 7 compliance. Non-essential cookies and analytics tools are disabled by default.
You can choose to accept all, reject non-essential cookies, or customize your granular preferences.
Read our <a href="/privacy/">Privacy Policy</a> and <a href="/terms/">Terms of Service</a> for details.
</p>
</div>
<div class="consent-actions">
<button id="gdpr-btn-accept-all" class="btn-primary">Accept All</button>
<button id="gdpr-btn-reject-all" class="btn-secondary">Reject Non-Essential</button>
<button id="gdpr-btn-customize" class="btn-outline">Customize Preferences</button>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', bannerHtml);
this.attachBannerEvents();
}
attachBannerEvents() {
const banner = document.getElementById(this.bannerId);
if (!banner) return;
banner.querySelector('#gdpr-btn-accept-all').addEventListener('click', () => {
this.saveConsent({ analytics: true, marketing: true, functional: true });
});
banner.querySelector('#gdpr-btn-reject-all').addEventListener('click', () => {
this.saveConsent({ analytics: false, marketing: false, functional: false });
});
banner.querySelector('#gdpr-btn-customize').addEventListener('click', () => {
this.renderModal();
});
}
renderModal() {
if (document.getElementById(this.modalId)) return;
const currentConsent = this.getSavedConsent() || this.defaultState;
const modalHtml = `
<div id="${this.modalId}" class="consent-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="consent-modal-card">
<div class="consent-modal-header">
<h2 id="modal-title">Privacy & Cookie Settings</h2>
<button id="modal-close-btn" class="close-x" aria-label="Close Modal">×</button>
</div>
<div class="consent-modal-body">
<!-- Category 1: Essential -->
<div class="consent-category">
<div class="category-header">
<div>
<strong>Strictly Necessary Cookies</strong>
<span class="badge-required">Always Active</span>
</div>
</div>
<p>Required for security authentication, CSRF protection, and session integrity. Cannot be disabled.</p>
</div>
<!-- Category 2: Analytics -->
<div class="consent-category">
<div class="category-header">
<label for="toggle-analytics">
<strong>Analytics & Performance Cookies</strong>
</label>
<input type="checkbox" id="toggle-analytics" ${currentConsent.analytics ? 'checked' : ''}>
</div>
<p>Helps us measure site traffic, page response times, and feature interaction patterns anonymously.</p>
</div>
<!-- Category 3: Marketing -->
<div class="consent-category">
<div class="category-header">
<label for="toggle-marketing">
<strong>Marketing & Targeting Cookies</strong>
</label>
<input type="checkbox" id="toggle-marketing" ${currentConsent.marketing ? 'checked' : ''}>
</div>
<p>Used to deliver personalized ad content, campaign attribution, and Google Consent Mode v2 ad signals.</p>
</div>
</div>
<div class="consent-modal-footer">
<button id="modal-save-preferences" class="btn-primary">Save Selected Preferences</button>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);
const modal = document.getElementById(this.modalId);
modal.querySelector('#modal-close-btn').addEventListener('click', () => this.closeModal());
modal.querySelector('#modal-save-preferences').addEventListener('click', () => {
const analytics = modal.querySelector('#toggle-analytics').checked;
const marketing = modal.querySelector('#toggle-marketing').checked;
this.saveConsent({ analytics, marketing, functional: false });
});
}
closeBanner() {
const banner = document.getElementById(this.bannerId);
if (banner) banner.remove();
}
closeModal() {
const modal = document.getElementById(this.modalId);
if (modal) modal.remove();
}
attachFooterListeners() {
// Re-open preferences panel when user clicks footer 'Cookie Preferences' link
document.querySelectorAll('.trigger-cookie-preferences').forEach(el => {
el.addEventListener('click', (e) => {
e.preventDefault();
this.renderModal();
});
});
}
}
4. CSS Design Rules for Equal Prominence & Accessibility
To pass manual compliance checks by data protection authorities and Google AdSense reviewers, the banner must strictly adhere to WCAG 2.1 AA accessibility guidelines and CSS visual balance rules.
/* SovereignShield Cookie Banner Design System */
.consent-banner-wrapper {
position: fixed;
bottom: 1.5rem;
left: 1.5rem;
right: 1.5rem;
max-width: 48rem;
margin: 0 auto;
background-color: #09090b;
border: 1px solid #27272a;
border-radius: 1.25rem;
padding: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.75);
z-index: 99999;
color: #f4f4f5;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.consent-banner-content h3 {
font-size: 1.125rem;
font-weight: 800;
color: #10b981;
margin-bottom: 0.5rem;
}
.consent-banner-content p {
font-size: 0.875rem;
color: #a1a1aa;
line-height: 1.6;
margin-bottom: 1.25rem;
}
.consent-banner-content a {
color: #34d399;
text-decoration: underline;
}
.consent-actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
/* Equal Prominence Buttons */
.consent-actions button {
flex: 1 1 140px;
padding: 0.75rem 1rem;
font-size: 0.875rem;
font-weight: 700;
border-radius: 0.75rem;
cursor: pointer;
transition: all 0.2s ease;
border: none;
text-align: center;
}
.btn-primary {
background-color: #10b981;
color: #000000;
}
.btn-primary:hover {
background-color: #34d399;
}
.btn-secondary {
background-color: #27272a;
color: #ffffff;
border: 1px solid #3f3f46 !important;
}
.btn-secondary:hover {
background-color: #3f3f46;
}
.btn-outline {
background-color: transparent;
color: #a1a1aa;
border: 1px solid #27272a !important;
}
.btn-outline:hover {
color: #ffffff;
border-color: #52525b !important;
}
5. Technical Safeguards & Verification Matrix
The matrix below maps frontend consent architecture features to specific GDPR and ePrivacy articles:
| Feature Target | GDPR / ePrivacy Reference | Technical Implementation | Verification Method |
|---|---|---|---|
| Default Blocked State | ePrivacy Art. 5(3) & GDPR Art. 7(1) | gtag('consent', 'default', { ... }) set to denied |
Audit Network Tab before interaction |
| Equal Prominence | EDPB Guidelines 05/2020 | Reject & Accept buttons share equal CSS sizing | Visual contrast audit (WCAG AA) |
| Granular Controls | GDPR Art. 6(1)(a) | Separate toggles for Analytics, Marketing, Functional | Check toggle states in Modal DOM |
| Local Persistence | GDPR Art. 25 (Privacy by Design) | Sandboxed localStorage persistence with ISO timestamps |
Inspect localStorage.getItem('sovereign_consent_state_v2') |
| Revocation Support | GDPR Art. 7(3) | Footer .trigger-cookie-preferences link re-opens modal |
Trigger click & verify state update |
6. Automated End-to-End Testing with Playwright
To prevent regressions in compliance behavior during continuous integration (CI/CD) deployments, use the automated Playwright test script below:
// tests/e2e/cookie-consent.spec.ts
import { test, expect } from '@playwright/test';
test.describe('GDPR Cookie Consent Engine Verification', () => {
test.beforeEach(async ({ page }) => {
// Clear storage before test
await page.goto('http://localhost:4321/');
await page.evaluate(() => localStorage.clear());
await page.reload();
});
test('should display banner on first visit with default consent denied', async ({ page }) => {
const banner = page.locator('#sovereign-gdpr-banner');
await expect(banner).toBeVisible();
// Verify Google Consent Mode default object in window
const defaultConsent = await page.evaluate(() => {
return (window as any).dataLayer?.find((entry: any) => entry[0] === 'consent' && entry[1] === 'default');
});
expect(defaultConsent[2].analytics_storage).toBe('denied');
expect(defaultConsent[2].ad_storage).toBe('denied');
});
test('should persist rejection when "Reject Non-Essential" is clicked', async ({ page }) => {
await page.click('#gdpr-btn-reject-all');
await expect(page.locator('#sovereign-gdpr-banner')).not.toBeVisible();
const storedData = await page.evaluate(() => {
return JSON.parse(localStorage.getItem('sovereign_consent_state_v2') || '{}');
});
expect(storedData.analytics).toBe(false);
expect(storedData.marketing).toBe(false);
expect(storedData.essential).toBe(true);
});
});
Conclusion & Action Plan
Deploying a zero-telemetry, GDPR-compliant cookie consent system requires more than rendering a banner overlay. Developers must guarantee that script tags remain completely suppressed prior to explicit user opt-in, synchronize consent signals with Google Consent Mode v2, and enable ongoing user revocation via permanent footer links.
To continue hardening your data privacy stack across related regulatory frameworks:
- Master backend cryptographic safeguards in our GDPR Data Encryption at Rest & in Transit Guide.
- Implement Article 17 database workflows with the GDPR Right to Be Forgotten Patterns Guide.
- Verify compliance posture with our Auditor Presentation Portal & Cryptographic Verifier.
- Track your team’s live regulatory coverage in the SovereignShield Interactive Compliance Matrix.