Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions source/_static/cookie-settings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
Copyright 2026 European Union

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/* Matches the webportal's CookieSettingsModal (HeroUI Modal, size 4xl). */
.edh-cookie-settings {
--edh-cookie-settings-text: #1d3c7c;
--edh-cookie-settings-violet: #8739c4;
--edh-cookie-settings-switch: #7828c8;
--edh-cookie-settings-switch-off: #d4d4d8;
--edh-cookie-settings-danger: #f31260;
--edh-cookie-settings-focus: #006fee;

background: #fff;
border-radius: 14px;
box-shadow:
0 0 5px 0 rgb(0 0 0 / 2%),
0 2px 10px 0 rgb(0 0 0 / 6%),
0 0 1px 0 rgb(0 0 0 / 30%);
color: var(--edh-cookie-settings-text);
margin: auto;
max-height: calc(100% - 128px);
max-width: min(896px, calc(100% - 8px));
outline: none;
width: 100%;
}

.edh-cookie-settings[open] {
animation:
edh-cookie-settings-fade 300ms ease-out,
edh-cookie-settings-slide 400ms ease;
display: flex;
flex-direction: column;
}

.edh-cookie-settings::backdrop {
animation: edh-cookie-settings-fade 300ms ease-out;
background: rgb(0 0 0 / 50%);
}

/* HeroUI's modal enter animation */
@keyframes edh-cookie-settings-fade {
from {
opacity: 0;
}
}

@keyframes edh-cookie-settings-slide {
from {
translate: 0 80px;
}
}

@keyframes edh-cookie-settings-scale {
from {
scale: 1.03;
}
}

html:has(.edh-cookie-settings[open]) {
overflow: hidden;
}

.edh-cookie-settings__body {
display: flex;
flex: 1;
flex-direction: column;
gap: 32px;
overflow-y: auto;
padding: 24px 20px;
}

.edh-cookie-settings__intro {
line-height: 28px;
}

.edh-cookie-settings__category {
display: grid;
gap: 16px;
}

.edh-cookie-settings__category h3 {
color: inherit;
font-size: 20px;
font-weight: 600;
line-height: 28px;
margin-bottom: 12px;
}

.edh-cookie-settings__category p {
font-size: 14px;
line-height: 20px;
}

.edh-cookie-settings__switch {
align-items: center;
cursor: pointer;
display: inline-flex;
justify-self: start;
position: relative;
user-select: none;
}

.edh-cookie-settings__switch:has(input:disabled) {
opacity: 0.5;
pointer-events: none;
}

.edh-cookie-settings__switch input {
cursor: pointer;
height: 100%;
inset: 0;
opacity: 0;
position: absolute;
width: 100%;
}

.edh-cookie-settings__track {
align-items: center;
background: var(--edh-cookie-settings-switch-off);
border-radius: 9999px;
display: inline-flex;
height: 28px;
margin-inline-end: 8px;
padding: 0 4px;
transition: background 250ms;
width: 48px;
}

.edh-cookie-settings__track::before {
background: #fff;
border-radius: 9999px;
box-shadow:
0 0 5px 0 rgb(0 0 0 / 2%),
0 2px 10px 0 rgb(0 0 0 / 6%),
0 0 1px 0 rgb(0 0 0 / 30%);
content: "";
height: 20px;
transition: margin 250ms cubic-bezier(0.4, 0, 0.2, 1);
width: 20px;
}

.edh-cookie-settings__switch input:checked + .edh-cookie-settings__track {
background: var(--edh-cookie-settings-switch);
}

.edh-cookie-settings__switch input:checked + .edh-cookie-settings__track::before {
margin-inline-start: 20px;
}

.edh-cookie-settings__switch input:focus-visible + .edh-cookie-settings__track,
.edh-cookie-settings__button:focus-visible {
outline: 2px solid var(--edh-cookie-settings-focus);
outline-offset: 2px;
}

.edh-cookie-settings__footer {
border-top: 1px solid #e7ecf6;
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 16px 20px;
}

.edh-cookie-settings__button {
border: 2px solid currentcolor;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
height: 40px;
line-height: 20px;
min-width: 80px;
padding: 0 16px;
transition: transform 250ms, opacity 250ms;
white-space: nowrap;
}

.edh-cookie-settings__button:hover {
opacity: 0.8;
}

.edh-cookie-settings__button:active {
transform: scale(0.97);
}

.edh-cookie-settings__button--deny {
color: var(--edh-cookie-settings-danger);
}

.edh-cookie-settings__button--accept {
color: var(--edh-cookie-settings-violet);
}

.edh-cookie-settings__button--save {
background: var(--edh-cookie-settings-violet);
border: 0;
color: #fff;
font-weight: 600;
}

@media (min-width: 640px) {
.edh-cookie-settings {
max-width: min(896px, calc(100% - 48px));
}

.edh-cookie-settings[open] {
animation:
edh-cookie-settings-fade 300ms ease-out,
edh-cookie-settings-scale 300ms ease-out;
}

.edh-cookie-settings__body,
.edh-cookie-settings__footer {
padding-left: 32px;
padding-right: 32px;
}

.edh-cookie-settings__category h3 {
font-size: 24px;
line-height: 32px;
}
}

@media (min-width: 768px) {
.edh-cookie-settings__category {
align-items: center;
grid-template-columns: minmax(0, 1fr) 152px;
}
}
112 changes: 102 additions & 10 deletions source/_static/matomo.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,97 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Mirrors the webportal's useTrackingConsent and useMatomo hooks.
(function () {
var TRACKING_CONSENT_STORAGE_KEY = 'edh.trackingConsent';

var containerUrl = document.currentScript.getAttribute('data-matomo-container-url');

function enableTracking() {
function getStoredTrackingConsent() {
try {
var storedConsent = window.localStorage.getItem(TRACKING_CONSENT_STORAGE_KEY);
return storedConsent === 'accepted' || storedConsent === 'denied' ? storedConsent : null;
} catch (error) {
return null;
}
}

function storeTrackingConsent(consent) {
try {
window.localStorage.setItem(TRACKING_CONSENT_STORAGE_KEY, consent);
} catch (error) {
// Consent still applies for the current page when storage is unavailable.
}
}

function applyMatomoTrackingConsent(consent) {
var queue = window._paq = window._paq || [];
queue.push(['setConsentGiven']);

if (consent === 'accepted') {
queue.push(['setConsentGiven']);
return;
}

queue.push(['forgetConsentGiven']);
queue.push(['deleteCookies']);
}

// Matomo Tag Manager creates the pageview tracker while the container loads,
// so reapply stored consent after load to reach the live tracker.
function reapplyStoredConsent() {
if (getStoredTrackingConsent() === 'accepted') {
applyMatomoTrackingConsent('accepted');
}
}

function loadMatomoContainer() {
var dataLayer = window._mtm = window._mtm || [];
dataLayer.push({'mtm.startTime': new Date().getTime(), event: 'mtm.Start'});

var script = document.createElement('script');
script.async = true;
script.src = containerUrl;
script.onload = reapplyStoredConsent;
document.head.appendChild(script);
}

function openCookieSettings() {
var dialog = document.querySelector('[data-cookie-settings]');
var trackingSwitch = dialog.querySelector('[data-cookie-settings-tracking]');
var trackingLabel = dialog.querySelector('[data-cookie-settings-tracking-label]');

function saveTrackingConsent(isTrackingCookiesEnabled) {
var consent = isTrackingCookiesEnabled ? 'accepted' : 'denied';

dialog.close();
applyMatomoTrackingConsent(consent);
storeTrackingConsent(consent);
}

trackingSwitch.addEventListener('change', function () {
trackingLabel.textContent = trackingSwitch.checked ? 'Enabled' : 'Disabled';
});
dialog.querySelector('[data-cookie-settings-deny]').addEventListener('click', function () {
saveTrackingConsent(false);
});
dialog.querySelector('[data-cookie-settings-accept]').addEventListener('click', function () {
saveTrackingConsent(true);
});
dialog.querySelector('[data-cookie-settings-save]').addEventListener('click', function () {
saveTrackingConsent(trackingSwitch.checked);
});
document.addEventListener('keydown', function (event) {
if (dialog.open && event.key === 'Escape' && !event.isComposing) {
event.preventDefault();
saveTrackingConsent(trackingSwitch.checked);
}
});
dialog.addEventListener('cancel', function (event) {
event.preventDefault();
});

dialog.showModal();
dialog.focus();
}

function enableMediaEvents() {
Expand Down Expand Up @@ -52,13 +137,20 @@
pluginInit.push(initMediaAnalytics);
}

enableTracking();
var dataLayer = window._mtm = window._mtm || [];
dataLayer.push({'mtm.startTime': new Date().getTime(), event: 'mtm.Start'});
var storedConsent = getStoredTrackingConsent();

if (storedConsent) {
applyMatomoTrackingConsent(storedConsent);
}

var script = document.createElement('script');
script.async = true;
script.src = containerUrl;
script.onload = enableTracking;
document.head.appendChild(script);
if (containerUrl) {
loadMatomoContainer();
}

if (!storedConsent) {
new PerformanceObserver(function (list, observer) {
observer.disconnect();
openCookieSettings();
}).observe({type: 'paint', buffered: true});
}
}());
Loading
Loading