<div class="o-cookie-banner" role="dialog" aria-labelledby="cookie-banner-text" aria-live="polite">
    <div class="o-cookie-banner__content">
        <p id="cookie-banner-text" class="o-cookie-banner__text">
            Wir verwenden Tracking-Skripte, um unsere Website zu verbessern und dir relevante Inhalte anzuzeigen.
            <a href="#" class="o-cookie-banner__privacy-link" target="_blank" rel="noopener">
                Datenschutzerklärung
            </a>
        </p>
        <div class="o-cookie-banner__actions">
            <button type="button" class="a-button o-cookie-banner__button o-cookie-banner__button--accept" data-action="accept">
                Akzeptieren
            </button>
            <button type="button" class="a-button a-button--secondary o-cookie-banner__button o-cookie-banner__button--decline" data-action="decline">
                Ablehnen
            </button>
        </div>
    </div>
    <button type="button" class="o-cookie-banner__close" aria-label="Close banner" hidden>
        <svg role="img" aria-hidden="true">
            <title>Close banner</title>
            <use xlink:href="#close"></use>
        </svg>
    </button>
</div>
<div class="o-cookie-banner" role="dialog" aria-labelledby="cookie-banner-text" aria-live="polite">
	<div class="o-cookie-banner__content">
		<p id="cookie-banner-text" class="o-cookie-banner__text">
			{{{ text }}}
			{{#if privacy_link}}
				<a href="{{ privacy_link }}" class="o-cookie-banner__privacy-link" target="_blank" rel="noopener">
					{{ privacy_text }}
				</a>
			{{/if}}
		</p>
		<div class="o-cookie-banner__actions">
			<button type="button" class="a-button o-cookie-banner__button o-cookie-banner__button--accept" data-action="accept">
				{{ accept_text }}
			</button>
			<button type="button" class="a-button a-button--secondary o-cookie-banner__button o-cookie-banner__button--decline" data-action="decline">
				{{ decline_text }}
			</button>
		</div>
	</div>
	<button type="button" class="o-cookie-banner__close" aria-label="Close banner" hidden>
		<svg role="img" aria-hidden="true">
			<title>Close banner</title>
			<use xlink:href="#close"></use>
		</svg>
	</button>
</div>
  • Content:
    @use "../../../scss/variables" as v;
    @use "../../../scss/mixins" as m;
    
    .o-cookie-banner {
    	position: fixed;
    	bottom: 0;
    	left: 0;
    	right: 0;
    	z-index: 9999;
    	background: v.$color-grey-1;
    	box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.20);
    	padding: 20px;
    	display: none;
    	animation: slideUp 0.3s ease-out;
    
    	@include m.media(v.$DESKTOP) {
    		left: auto;
    		right: 8%;
    		bottom: 0;
    		width: 800px;
    		max-width: calc(100vw - 40px);
    		border: 12px solid v.$color-grey-2;
    		border-top: 12px solid v.$color-grey-2;
    		border-bottom: none;
    		border-radius: 4px 4px 0 0;
    		box-shadow: 0 -2px 30px rgba(0, 0, 0, 0.30);
    	}
    
    	&--visible {
    		display: flex;
    		flex-direction: column;
    	}
    
    	&__content {
    		flex: 1;
    		display: flex;
    		flex-direction: column;
    		gap: 15px;
    
    		@include m.media(v.$DESKTOP) {
    			gap: 20px;
    		}
    	}
    
    	&__text {
    		margin: 0;
    		font-size: 14px;
    		line-height: 1.5;
    		color: v.$color-text;
    
    		@include m.media(v.$DESKTOP) {
    			font-size: 15px;
    		}
    	}
    
    	&__privacy-link {
    		color: v.$color-primary;
    		text-decoration: underline;
    		transition: color v.$transition-fast v.$transition-ease;
    
    		@include m.hover {
    			color: v.$color-primary-dark;
    		}
    	}
    
    	&__actions {
    		display: flex;
    		gap: 10px;
    		flex-wrap: wrap;
    
    		@include m.media(v.$DESKTOP) {
    			gap: 15px;
    		}
    	}
    
    	&__button {
    		flex: 1;
    		min-width: 120px;
    		font-size: 14px;
    		padding: 10px 20px;
    
    		@include m.media(v.$DESKTOP) {
    			flex: 0 1 auto;
    		}
    
    		&--accept {
    			background: v.$color-primary;
    			color: v.$color-white;
    			border: none;
    
    			@include m.hover {
    				background: v.$color-primary-dark;
    			}
    		}
    
    		&--decline {
    			background: v.$color-white;
    			color: v.$color-text;
    			border: 1px solid v.$color-grey-2;
    
    			@include m.hover {
    				background: v.$color-grey-1;
    			}
    		}
    	}
    
    	&__close {
    		position: absolute;
    		top: 10px;
    		right: 10px;
    		background: transparent;
    		border: none;
    		padding: 5px;
    		cursor: pointer;
    		opacity: 0.6;
    		transition: opacity v.$transition-fast v.$transition-ease;
    		display: none;
    
    		@include m.hover {
    			opacity: 1;
    		}
    
    		&[hidden] {
    			display: none;
    		}
    
    		&:not([hidden]) {
    			display: block;
    		}
    
    		svg {
    			width: 20px;
    			height: 20px;
    			stroke: v.$color-text;
    		}
    	}
    }
    
    @keyframes slideUp {
    	from {
    		transform: translateY(100%);
    		opacity: 0;
    	}
    	to {
    		transform: translateY(0);
    		opacity: 1;
    	}
    }
    
  • URL: /components/raw/o-cookie-banner/_o-cookie-banner.scss
  • Filesystem Path: styleguide/src/components/organisms/o-cookie-banner/_o-cookie-banner.scss
  • Size: 2.3 KB
  • Content:
    import BaseView from 'base-view';
    
    const STORAGE_KEY = 'trackingConsent';
    const CONSENT_EVENT = 'trackingConsentChanged';
    
    /**
     * Cookie Banner Component
     * Manages user consent for tracking scripts
     */
    export default class OCookieBanner extends BaseView {
    
    	initialize() {
    		this.acceptButton = this.getScopedElement('[data-action="accept"]');
    		this.declineButton = this.getScopedElement('[data-action="decline"]');
    		this.closeButton = this.getScopedElement('.o-cookie-banner__close');
    
    		// Check if consent already exists
    		const consent = this.getConsent();
    
    		if (consent === null) {
    			// No consent stored, show banner
    			this.show();
    		} else {
    			// Consent exists, load scripts if accepted
    			if (consent.accepted) {
    				// Try to load scripts immediately
    				this.loadTrackingScripts();
    
    				// Also try after DOM is fully loaded (in case window.trackingScripts is set later)
    				if (document.readyState === 'loading') {
    					document.addEventListener('DOMContentLoaded', () => {
    						this.loadTrackingScripts();
    					});
    				}
    			}
    		}
    
    		// Bind events
    		this.on('click', '[data-action="accept"]', this.handleAccept.bind(this));
    		this.on('click', '[data-action="decline"]', this.handleDecline.bind(this));
    		this.on('click', '.o-cookie-banner__close', this.handleClose.bind(this));
    	}
    
    	/**
    	 * Show the banner
    	 */
    	show() {
    		this.addClass('o-cookie-banner--visible');
    		this.element.setAttribute('aria-hidden', 'false');
    	}
    
    	/**
    	 * Hide the banner
    	 */
    	hide() {
    		this.removeClass('o-cookie-banner--visible');
    		this.element.setAttribute('aria-hidden', 'true');
    	}
    
    	/**
    	 * Handle accept button click
    	 */
    	handleAccept(e) {
    		e.preventDefault();
    		this.saveConsent(true);
    		this.loadTrackingScripts();
    		this.hide();
    	}
    
    	/**
    	 * Handle decline button click
    	 */
    	handleDecline(e) {
    		e.preventDefault();
    		this.saveConsent(false);
    		this.hide();
    	}
    
    	/**
    	 * Handle close button click
    	 */
    	handleClose(e) {
    		e.preventDefault();
    		this.hide();
    	}
    
    	/**
    	 * Show close button after consent decision
    	 */
    	showCloseButton() {
    		if (this.closeButton) {
    			this.closeButton.removeAttribute('hidden');
    		}
    	}
    
    	/**
    	 * Save consent to localStorage
    	 * @param {boolean} accepted
    	 */
    	saveConsent(accepted) {
    		const consent = {
    			accepted: accepted,
    			timestamp: Date.now()
    		};
    
    		try {
    			localStorage.setItem(STORAGE_KEY, JSON.stringify(consent));
    			this.trigger(CONSENT_EVENT, { detail: consent });
    		} catch (e) {
    			// Silent fail - consent not saved
    		}
    	}
    
    	/**
    	 * Get consent from localStorage
    	 * @returns {Object|null}
    	 */
    	getConsent() {
    		try {
    			const stored = localStorage.getItem(STORAGE_KEY);
    			return stored ? JSON.parse(stored) : null;
    		} catch (e) {
    			// Silent fail - return null
    			return null;
    		}
    	}
    
    	/**
    	 * Load tracking scripts if consent was given
    	 */
    	loadTrackingScripts() {
    		// Check if tracking scripts are defined
    		if (typeof window.trackingScripts === 'undefined') {
    			return;
    		}
    
    		const scripts = window.trackingScripts;
    
    		// Load Facebook Pixel
    		if (scripts.facebookPixel && scripts.facebookPixel.id) {
    			this.loadFacebookPixel(scripts.facebookPixel.id);
    		}
    
    		// Load custom scripts
    		Object.keys(scripts).forEach(key => {
    			if (key !== 'facebookPixel' && scripts[key].type === 'external') {
    				this.loadExternalScript(scripts[key].url, key);
    			}
    		});
    	}
    
    	/**
    	 * Load Facebook Pixel
    	 * @param {string} pixelId
    	 */
    	loadFacebookPixel(pixelId) {
    		// Check if already loaded or initialized
    		if (window.fbq || window._fbPixelInitialized) {
    			return;
    		}
    
    		// Mark as initialized to prevent duplicate loading
    		window._fbPixelInitialized = true;
    
    		// Facebook Pixel Code
    		!function(f,b,e,v,n,t,s)
    		{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    		n.callMethod.apply(n,arguments):n.queue.push(arguments)};
    		if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
    		n.queue=[];t=b.createElement(e);t.async=!0;
    		t.src=v;s=b.getElementsByTagName(e)[0];
    		s.parentNode.insertBefore(t,s)}(window, document,'script',
    		'https://connect.facebook.net/en_US/fbevents.js');
    
    		window.fbq('init', pixelId);
    		window.fbq('track', 'PageView');
    	}
    
    	/**
    	 * Load external script
    	 * @param {string} url
    	 * @param {string} key
    	 */
    	loadExternalScript(url, key) {
    		// Check if already loaded
    		const existingScript = document.querySelector(`script[data-tracking-script="${key}"]`);
    		if (existingScript) {
    			return;
    		}
    
    		const script = document.createElement('script');
    		script.src = url;
    		script.async = true;
    		script.setAttribute('data-tracking-script', key);
    
    		script.onload = () => {
    			// Script loaded successfully
    		};
    
    		script.onerror = () => {
    			// Script failed to load
    		};
    
    		document.head.appendChild(script);
    	}
    
    	/**
    	 * Public API: Check if user has consented
    	 * @returns {boolean}
    	 */
    	static hasConsent() {
    		try {
    			const stored = localStorage.getItem(STORAGE_KEY);
    			if (!stored) return false;
    			const consent = JSON.parse(stored);
    			return consent.accepted === true;
    		} catch (e) {
    			return false;
    		}
    	}
    
    	/**
    	 * Public API: Reset consent (for testing)
    	 */
    	static resetConsent() {
    		try {
    			localStorage.removeItem(STORAGE_KEY);
    			// Consent reset - reload page to see banner
    		} catch (e) {
    			// Failed to reset consent
    		}
    	}
    }
    
    // Expose public API
    window.CookieBanner = {
    	hasConsent: OCookieBanner.hasConsent,
    	resetConsent: OCookieBanner.resetConsent
    };
    
  • URL: /components/raw/o-cookie-banner/o-cookie-banner.js
  • Filesystem Path: styleguide/src/components/organisms/o-cookie-banner/o-cookie-banner.js
  • Size: 5.4 KB
{
  "text": "Wir verwenden Tracking-Skripte, um unsere Website zu verbessern und dir relevante Inhalte anzuzeigen.",
  "privacy_link": "#",
  "privacy_text": "Datenschutzerklärung",
  "accept_text": "Akzeptieren",
  "decline_text": "Ablehnen"
}
  • Handle: @o-cookie-banner
  • Preview:
  • Filesystem Path: styleguide/src/components/organisms/o-cookie-banner/o-cookie-banner.hbs

No notes defined.