feat: cookie banner compliance hardening — 5 legal requirements
1. Impressum link mandatory in banner (§5 TMG) 2. Pre-ticked prevention: only "required" categories pre-enabled (Planet49) 3. Cookie-Settings reopen link (§7(3) DSGVO — revocation as easy as consent) 4. Script-Blocking: data-cookie-category + type="text/plain" pattern Scripts only execute AFTER user consents to that category 5. Buttons already equal size (flex:1) — verified correct Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -245,13 +245,16 @@ function generateHTML(config: CookieBannerConfig, privacyPolicyUrl: string): str
|
||||
const categoriesHTML = config.categories
|
||||
.map((cat) => {
|
||||
const isRequired = cat.isRequired
|
||||
// COMPLIANCE: Only "required" categories may be pre-enabled (EuGH Planet49)
|
||||
// Non-required categories must NEVER be defaultEnabled
|
||||
const isEnabled = isRequired ? true : false
|
||||
return `
|
||||
<div class="cookie-banner-category" data-category="${cat.id}">
|
||||
<div class="cookie-banner-category-info">
|
||||
<div class="cookie-banner-category-name">${cat.name.de}</div>
|
||||
<div class="cookie-banner-category-desc">${cat.description.de}</div>
|
||||
</div>
|
||||
<div class="cookie-banner-toggle ${cat.defaultEnabled ? 'active' : ''} ${isRequired ? 'disabled' : ''}"
|
||||
<div class="cookie-banner-toggle ${isEnabled ? 'active' : ''} ${isRequired ? 'disabled' : ''}"
|
||||
data-category="${cat.id}"
|
||||
data-required="${isRequired}"></div>
|
||||
</div>
|
||||
@@ -286,10 +289,22 @@ function generateHTML(config: CookieBannerConfig, privacyPolicyUrl: string): str
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="${privacyPolicyUrl}" class="cookie-banner-link" target="_blank">
|
||||
${config.texts.privacyPolicyLink.de}
|
||||
</a>
|
||||
<div class="cookie-banner-links">
|
||||
<a href="${privacyPolicyUrl}" class="cookie-banner-link" target="_blank">
|
||||
${config.texts.privacyPolicyLink.de}
|
||||
</a>
|
||||
<a href="${config.impressumUrl || '/impressum'}" class="cookie-banner-link" target="_blank">
|
||||
Impressum
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cookie Settings Re-Open (§7(3) DSGVO — Widerruf so einfach wie Einwilligung) -->
|
||||
<a href="#" id="cookieBannerReopen" class="cookie-settings-footer-link"
|
||||
onclick="document.getElementById('cookieBanner').style.display='block';document.getElementById('cookieBannerOverlay').classList.add('active');return false;"
|
||||
style="position:fixed;bottom:8px;left:8px;z-index:9990;font-size:11px;color:#6b7280;text-decoration:none;background:rgba(255,255,255,0.9);padding:4px 8px;border-radius:4px;border:1px solid #e5e7eb;">
|
||||
Cookie-Einstellungen
|
||||
</a>
|
||||
`.trim()
|
||||
}
|
||||
|
||||
@@ -397,6 +412,31 @@ function generateJS(config: CookieBannerConfig): string {
|
||||
overlay?.classList.remove('active');
|
||||
}
|
||||
|
||||
// Script-Blocking: activate scripts with data-cookie-category ONLY after consent
|
||||
function activateConsentedScripts() {
|
||||
const consent = getConsent();
|
||||
if (!consent) return;
|
||||
|
||||
// Find all blocked scripts (type="text/plain" with data-cookie-category)
|
||||
document.querySelectorAll('script[data-cookie-category][type="text/plain"]').forEach(script => {
|
||||
const category = script.getAttribute('data-cookie-category');
|
||||
if (consent[category] === true) {
|
||||
// Replace type to activate the script
|
||||
const newScript = document.createElement('script');
|
||||
if (script.src) newScript.src = script.src;
|
||||
else newScript.textContent = script.textContent;
|
||||
newScript.type = 'text/javascript';
|
||||
script.parentNode.replaceChild(newScript, script);
|
||||
}
|
||||
});
|
||||
|
||||
// Also fire custom event for programmatic listeners
|
||||
window.dispatchEvent(new CustomEvent('cookieConsentActivated', { detail: consent }));
|
||||
}
|
||||
|
||||
// Run script activation after consent is saved
|
||||
window.addEventListener('cookieConsentUpdated', activateConsentedScripts);
|
||||
|
||||
window.CookieConsent = {
|
||||
getConsent,
|
||||
saveConsent,
|
||||
@@ -405,14 +445,32 @@ function generateJS(config: CookieBannerConfig): string {
|
||||
document.getElementById('cookieBanner')?.classList.add('active');
|
||||
document.getElementById('cookieBannerOverlay')?.classList.add('active');
|
||||
},
|
||||
hide: closeBanner
|
||||
hide: closeBanner,
|
||||
activateScripts: activateConsentedScripts,
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initBanner);
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initBanner();
|
||||
activateConsentedScripts();
|
||||
});
|
||||
} else {
|
||||
initBanner();
|
||||
activateConsentedScripts();
|
||||
}
|
||||
})();
|
||||
|
||||
/*
|
||||
* USAGE: Script-Blocking
|
||||
*
|
||||
* Instead of:
|
||||
* <script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"></script>
|
||||
*
|
||||
* Use:
|
||||
* <script type="text/plain" data-cookie-category="statistics"
|
||||
* src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"></script>
|
||||
*
|
||||
* The script will only execute AFTER the user consents to "statistics".
|
||||
*/
|
||||
`.trim()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user