Cookie Alert
In order to be legally on the safe side, the cookie alert must be used in most cases. This modal informs the user about cookies and also gives the user the possibility to set his personal cookie preferences. This cookie alert is designed as an overlay because it is legally required that the user first interacts with this element before he can use the site.
This component uses the button component and the checkbox of the form component. Therefore you have to include the CSS of these both components in order to get the cookie alert displayed correctly!
<dialog role="dialog" class="cookie-alert" lang="en" dir="lr" data-controller="cookie-alert"
aria-labelledby="087d5192" aria-describedby="7b84810b">
<div class="cookie-alert-modal" aria-modal="true">
<h2 class="cookie-alert-title" id="087d5192">Title</h2>
<p class="cookie-alert-description" id="7b84810b">At vero eos et accusam et justo duo
dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum
dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea
takimata sanctus est Lorem <a href="#">ipsum dolor sit amet.</a>
</p>
<div class="cookie-alert-controls">
<button type="button" class="cookie-alert-button" tabindex="1"
data-controller="cookie-alert/button/accept">Accept all</button>
<a href="#" class="cookie-alert-detail-link" tabindex="2"
data-controller="cookie-alert/detail-link" aria-controls="08b65029"
id="04211ef6">More details</a>
</div>
<div class="cookie-alert-configuration" data-controller="cookie-alert/configuration"
aria-controls="08b65029" aria-labelledby="04211ef6" aria-expanded="false" id="08b65029">
<div class="cookie-alert-configuration-settings">
<div class="cookie-alert-configuration-control">
<input type="checkbox" class="cookie-alert-configuration-input" id="necessary"
tabindex="2" checked disabled />
<label for="necessary" class="cookie-alert-checkbox-label">Necessary</label>
</div>
<div class="cookie-alert-configuration-control">
<input type="checkbox" class="cookie-alert-configuration-input" id="preferences"
tabindex="2" />
<label for="preferences" class="cookie-alert-checkbox-label">Preferences</label>
</div>
<div class="cookie-alert-configuration-control">
<input type="checkbox" class="cookie-alert-configuration-input" id="statistics"
tabindex="2" />
<label for="statistics" class="cookie-alert-checkbox-label">Statistics</label>
</div>
<div class="cookie-alert-configuration-control">
<input type="checkbox" class="cookie-alert-configuration-input" id="marketing"
tabindex="2" />
<label for="marketing" class="cookie-alert-checkbox-label">Marketing</label>
</div>
</div>
<button type="button" class="cookie-alert-button-secondary" tabindex="2"
data-controller="cookie-alert/button/configuration">Accept configuration</button>
</div>
</div>
</dialog>
if (typeof window.cake !== "object") {
window.cake = {};
}
window.cake.cookie = {
//Add Event Listeners
_eventListeners: [],
_forceFocus: true,
_addEventListener: function(element, listenerType, listenerFunction) {
window.cake.cookie._eventListeners.push({
element: element,
listenerFunction: listenerFunction,
listenerType: listenerType
});
element.addEventListener(listenerType, listenerFunction);
},
_removeAllEventListeners: function() {
window.cake.cookie._eventListeners.forEach(function(listenerConfig) {
listenerConfig.element.removeEventListener(listenerConfig.listenerType,
listenerConfig.listenerFunction);
});
},
// Add custom polyfills / utilities
_addPolyfills: function() {
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
},
//Set cookie settings and hide cookie
acceptCookies: function(optinPreferences, optinStatistics, optinMarketing) {
if (window.Cookiebot) {
window.Cookiebot.submitCustomConsent(optinPreferences, optinStatistics,
optinMarketing);
}
window.cake.cookie.hideCookieAlert();
},
//Show and initialize cookie alert
showCookieAlertWithoutForcedFocus: function() {
return window.cake.cookie.showCookieAlert(false);
},
showCookieAlert: function(forceFocus) {
window.cake.cookie._addPolyfills();
if (forceFocus === undefined) {
forceFocus = true;
}
window.cake.cookie._forceFocus = forceFocus;
// Parse cookie-alert element
var cookieAlert = document.querySelector("*[data-controller='cookie-alert']");
//If not cookie-alert is available, just skip
if (!cookieAlert || cookieAlert.classList.contains("opened")) {
return;
}
// Parse relevant elements
var cookieAlertModal = cookieAlert.childNodes[0];
var acceptAllButton = document.querySelector(
"*[data-controller='cookie-alert/button/accept']");
var acceptConfigButton = document.querySelector(
"*[data-controller='cookie-alert/button/configuration']");
var showDetailsLink = document.querySelector(
"*[data-controller='cookie-alert/detail-link']");
var configurationDiv = document.querySelector(
"*[data-controller='cookie-alert/configuration']");
var closeElements = document.querySelectorAll(
"*[data-controller='cookie-alert/button/close']");
//update overlay/alert size depending on viewport
setTimeout(function() {
window.cake.cookie._updateOverlaySize(cookieAlert, acceptAllButton,
true);
}, 100);
window.cake.cookie._addEventListener(acceptAllButton, "click", function() {
window.cake.cookie.acceptCookies(true, true, true);
});
window.cake.cookie._addEventListener(acceptConfigButton, "click", function() {
var preferenceCookies = document.getElementById("preferences")
.checked || false;
var statisticsCookies = document.getElementById("statistics").checked ||
false;
var marketingCookies = document.getElementById("marketing").checked ||
false;
window.cake.cookie.acceptCookies(preferenceCookies, statisticsCookies,
marketingCookies);
});
window.cake.cookie._addEventListener(showDetailsLink, "click", function() {
configurationDiv.classList.toggle("expanded");
showDetailsLink.classList.toggle("expanded");
if (configurationDiv.classList.contains("expanded")) {
// Make acceptAllButton disabled
acceptAllButton.disabled = true;
//Adjust details-text, if texts are available
if (window.CookieConsent && window.CookieConsent.dialog) {
showDetailsLink.innerHTML = window.CookieConsent.dialog
.hideDetailsText;
}
configurationDiv.setAttribute("aria-expanded", "true");
cookieAlertModal.scrollTop = cookieAlertModal.scrollHeight;
} else {
// Make acceptAllButton enabled
acceptAllButton.disabled = false;
//Adjust details-text, if texts are available
if (window.CookieConsent && window.CookieConsent.dialog) {
showDetailsLink.innerHTML = window.CookieConsent.dialog
.showDetailsText;
}
configurationDiv.setAttribute("aria-expanded", "false");
}
});
// Close elements event Listener
closeElements.forEach(function(closeElement) {
window.cake.cookie._addEventListener(closeElement, 'click', function(
e) {
e.preventDefault();
window.cake.cookie.acceptCookies(false, false, false);
});
});
//Display cookie alert
cookieAlert.showModal = cookieAlert.showModal || function() {};
cookieAlert.showModal(); // Native Browser-Method for the dialog-element
cookieAlert.setAttribute('open', 'open');
cookieAlert.classList.add("opened");
cookieAlert.style.display = "block";
},
//Deconstruct cookie alert
hideCookieAlert: function() {
var cookieAlert = document.querySelector("*[data-controller='cookie-alert']");
//Hide cookie alert
cookieAlert.close = cookieAlert.close || function() {};
cookieAlert.close(); // Native Browser-Method for the dialog-element
cookieAlert.removeAttribute('open');
cookieAlert.classList.remove("opened");
cookieAlert.style.display = "none";
//update overlay/alert size to previous values
window.cake.cookie._revertOverlaySize();
//Remove eventListeners
window.cake.cookie._removeAllEventListeners();
},
// bugfix - oldBrowser - Safari iOS viewport is initially bigger than the visible part (https://medium.com/@susiekim9/how-to-compensate-for-the-ios-viewport-unit-bug-46e78d54af0d)
_tmpStylings: [],
_updateOverlaySize: function(cookieAlert, acceptAllButton, setEventListener) {
if (setEventListener === true && window.cake.cookie._tmpStylings.length < 1) {
window.cake.cookie._tmpStylings.push({
el: document.body,
val: document.body.style.overflow,
attr: "overflow"
});
window.cake.cookie._tmpStylings.push({
el: document.body,
val: document.body.style.height,
attr: "height"
});
window.cake.cookie._tmpStylings.push({
el: document.documentElement,
val: document.documentElement.style.overflow,
attr: "overflow"
});
window.cake.cookie._tmpStylings.push({
el: document.documentElement,
val: document.documentElement.style.height,
attr: "height"
});
window.cake.cookie._addEventListener(window, "resize", function() {
//On resize or orientation switch, update the size of the alert
window.cake.cookie._updateOverlaySize(cookieAlert);
}.bind(cookieAlert));
//Keep focus inside the cookie-alert element, if option is set to true
if (window.cake.cookie._forceFocus) {
cookieAlert.querySelectorAll("button,a,input").forEach(function(element) {
window.cake.cookie._addEventListener(element, "focusout",
function() {
setTimeout(function() {
//Prevent focus from jumping out of cookie-alert elements
if (!cookieAlert.contains(document
.activeElement)) {
acceptAllButton.focus();
}
}, 20);
});
});
document.querySelectorAll('[tabindex="1"]').forEach(function(element) {
if (!cookieAlert.contains(element)) {
element.dataset.oldTabIndex = "1";
element.setAttribute('tabindex', 0);
}
});
document.querySelectorAll('[tabindex="2"]').forEach(function(element) {
element.dataset.oldTabIndex = "2";
element.setAttribute('tabindex', 0);
});
}
}
cookieAlert.style.height = window.innerHeight + "px";
document.body.style.overflow = "hidden";
document.body.style.height = window.innerHeight + "px";
document.documentElement.style.overflow = "hidden";
document.documentElement.style.height = window.innerHeight + "px";
},
_revertOverlaySize: function() {
window.cake.cookie._tmpStylings.forEach(function(tmpStyling) {
tmpStyling.el.style[tmpStyling.attr] = tmpStyling.val;
});
if (window.cake.cookie._forceFocus) {
document.querySelectorAll('[data-old-tab-index="1"]').forEach(function(
element) {
element.removeAttribute("data-old-tab-index");
element.setAttribute('tabindex', 1);
});
document.querySelectorAll('[data-old-tab-index="2"]').forEach(function(
element) {
element.removeAttribute("data-old-tab-index");
element.setAttribute('tabindex', 2);
});
}
}
};
The cookie alert uses the <dialog>
element. In order to get the correct styling applied to it, you have to use the .cookie-alert
class.
This dialog element is the transparent black background. Inside this <dialog>
element you should create a <div>
with the class .cookie-alert-modal
applied to it. This is the wrapper element for all the content elements of this component:
- title as a
<h2>
element with the class.h5
- description as a
<p>
tag and the class.cookie-alert-description
- more-details link
.cookie-alert-detail-link
- accept all cookies button
- the cookie configuration collapsible
<div>
.cookie-alert-configuration
- the checkboxes wrapper
<div>
.cookie-alert-configuration-settings
- the labels of each checkbox
.cookie-alert-checkbox-label
- the labels of each checkbox
- the accept configuration button
- the checkboxes wrapper
The detailed structure of this component can be seen in the above example. Best practice is to place the cookie alert component as the first element in the body of each page. Additionaly it is mandatory to place a link to your cookie-information page into the description text! Be aware, that on the cookie-information page there shouldn't be any cookie-alert. Because otherwise the visitor is not able to read the information before accepting anything. Thus you should not save or use any cookies on this page because the visitor has not accepted anything.
Accessibility#
Because this component is a legal requirement, special attention should be paid to good accessibility! One important thing is to not use a <div>
but a <dialog>
element to tell especially screenreaders that this element is an overlay which stays in front of other content. Also some aria-attributes should be added. The aria-labelledby
and the aria-describedby
attributes should reference to the appropriate element in the modal (title and description). Also the special open
attribute of the <dialog>
element should get set correctly. You can find a description of this html element on it's dedicated page on the MDN web docs. It's also a good practice to set the role element (role="dialog"
) for supporting browsers. The modal div (.cookie-alert-modal
) has the attribute aria-modal="true"
attatched to it.
For the collapsable cookie configuration at the bottom of the cookie dialog, you should add aria-controls="id"
. The configuration (.cookie-alert-configuration
) needs also some additional attributes for accessibility: aria-controls="id"
, aria-labelledby="id"
, aria-expanded="false"
. What these attributes are doing can be read in the two examples W3: dialog-modal and W3: accordion.
To also get the correct tab order for users only using their keyboard or other tools, the tabindex of the accept all button should be set to tabindex="1"
and all the other clickable elements of the cookie alert to tabindex="2"
. So the user first must tab through the cookie alert, before accessing the website itself. The accept all button has tabindex 1 because this element should have the inital focus.
JavaScript#
With our JavaScript we have focused on the usage in CookieBot because this is the most used tool by our users. Because of that we have written this component's JavaScript in the "old-fashioned" way with ES5 syntax
. This way you can simply copy paste our JavaScript into your CookieBot console. But more to this further below.
To make your HTML
work with our JavaScript, you have to apply the coorect data-controller
s. Every element, that causes some JavaScript code execution needs one of the following attributes:
- the
<dialog>
element should have the attributedata-controller="cookie-alert"
attatched to it - the more details link
.cookie-alert-detail-link
has the attributedata-controller="cookie-alert/detail-link"
- for the accept all button
data-controller="cookie-alert/button/accept"
- the configuration element
.cookie-alert-configuration
needs thedata-controller="cookie-alert/configuration"
attribtue attatched - the accept configuration button has the attribute
data-controller="cookie-alert/button/configuration"
- the close buttons or links must have the attribute
data-controller="cookie-alert/button/close"
Our JavaScript parses the elements with the above mentioned data-attributes
and adds three click event listener to the two buttons (accept all and accept configuration) and to the more details link. These click event listeners implement some functionality like setting up the appropriate accessibility attributes or disabling the primary CTA button when showing more details.
The JavaScript of this component exposes three methods that can be used:
Accept cookies#
window.cake.cookie.acceptCookies: function (optinPreferences, optinStatistics, optinMarketing) {…}
. This method simply saves the configuration set by the user. If the user for example clicks the accept all button, all three parameters are set to true
:
acceptAllButton.addEventListener ("click", function () {
window.cake.cookie.acceptCookies (true, true, true);
});
Show cookie alert#
window.cake.cookie.showCookieAlert: function () {…}
. With this method you can show the cookie alert and initialize all the event listeners needed by this component.
window.cake.cookie.showCookieAlert (forcedFocus = true);
This method has also an optional property forcedFocus
, which can force the customers browser to keep focus on the relevant elements of our cookie alert. This property is set to true
as default but if forcedFocus
is set to false
, it will not change anything in the default focus handling of the browser.
Hide cookie alert#
window.cake.cookie.hideCookieAlert: function () {…}
This method simply hides the cookie alert and removes all events added in the function above. But please be sure to save the cookie configuration before with the first method mentioned!
window.cake.cookie.hideCookieAlert ();
CookieBot integration#
We have developed our cookie alert especially for CookieBot, visit this dokumentaion if you are interestet in using our CookieBot Theme.
OneTrust integration#
We also have developed a CAKE Theme for OneTrust.
Change log#
Changed#
Doc
: "Cookie alert" | moved CookieBot theme and OneTrust theme toCustomization
Added#
Doc
: "Cookie alert" | added javaScript file content as "JS" tab
Changed#
SCSS
,JS
,HTML
: "Cookie alert" | Renamed cookie-alert-extended to cookie-alert in all classes, javascript-methods, data-controllers and files.JS
: "Cookie alert" | Disable primary CTA button on expanded details
Removed#
SCSS
,JS
,HTML
: "Cookie alert" | Removed deprecated cookie-alert in all classes, javascript-methods, data-controllers and files.
Deprecated#
- "Cookie alert" | The simple version with notice banner will be deprecated in future releases. Due to legal requirements the extended cookie alert with settings must be used.
Added#
SCSS
,JS
,HTML
: "Cookie alert" | Added extended overlay version of cookie alert with configuration menu
Fixed#
SCSS
: "Cookie alert" | Added!default
to$cookie-alert-color
,$cookie-alert-bg-color
,$cookie-alert-font-size
in_variables.scss
file.