Cookie bannerStatusrelease
An implementation of the ICO/GDPR compliant Storm Cookie Banner, with the cookie consent form embedded within the banner.
Example
Dependencies and installation
Package | Installation |
---|---|
@stormid/cookie-banner |
|
@stormid/toggle |
|
Code
import cookieBanner from '@stormid/cookie-banner';
import toggle from '@stormid/toggle';
const config = {
name: '.Patterns.Consent', //name of the consent cookie
tid: '', //tid of GA account anonymously measuring interactions with the banner
policyURL: '', //url of cookie policy page
hideBannerOnFormPage: true,
types: {
performance: {
suggested: true, //set as pre-checked on consent form as a suggested response
title: 'Performance',
description: 'Performance cookies are used to measure the performance of our website and make improvements. Your personal data is not used. For example, we will measure the links you click, but we will not know that it was you that clicked them.',
labels: {
yes: 'You agree to us using cookies to improve the service',
no: 'You do not agree to us using cookies to improve the service'
},
fns: [ //array of functions that set performance cookies
state => state.utils.gtmSnippet('UA-1234-5678') // example, inject Google Tag Manager snippet using utility function
]
},
thirdParty: {
title: 'Third-party services and advertising',
description: 'Some functionality of our website relies on third-party services such as video providers and social media content. These services may set their own cookies on behalf of their advertisers. You can choose whether we allow these cookies to be set or not. If you decline, some functions of the website may not work as expected.',
labels: {
yes: 'You agree to allow third-party and advertising cookies',
no: 'You do not agree to third-party and advertising cookies'
},
fns: [ //array of functions that set third-party cookies
state => state.utils.renderIframe(), //example, render all iframes from placeholders using utility function
state => state.utils.gtmSnippet('UA-1234-5678') // example, inject Google Tag Manager snippet using utility function
]
}
},
bannerTemplate(model){
return `<section role="dialog" aria-live="polite" aria-label="Cookies" class="${model.classNames.banner}">
<div class="privacy-content">
<div class="wrap">
<div class="col xs-12 privacy-banner__inner">
<!--googleoff: all-->
<div class="privacy-banner__content">
<div class="privacy-banner__title">Cookies</div>
<p class="privacy-banner__summary">This web service uses cookies to ensure it functions correctly, and to help gather analytics data which is used to help us improve the service.</p>
<p class="privacy-banner__summary">Find out more from our <a class="privacy-banner__link" rel="noopener noreferrer nofollow" href="/privacy">privacy policy</a> and <a class="privacy-banner__link" rel="noopener noreferrer nofollow" href="${model.policyURL}">cookie policy</a>.</p>
</div>
<div class="privacy-banner__actions">
<button type="button" class="privacy-banner__btn ${model.classNames.acceptBtn}">Accept and close</button>
<button type="button" class="privacy-banner__link js-toggle__preferences ${model.classNames.optionsBtn}">Your options</button>
</div>
<div id="preferences" class="privacy-banner__panel js-toggle-banner" data-toggle="js-toggle__preferences">
<p><button type="button" class="privacy-banner__btn-text ${model.classNames.acceptBtn}">Accept and close </button> or edit your choices below and click 'Save my choices'.</p>
<div class="privacy-banner__form-container"></div>
</div>
<!--googleon: all-->
</div>
</div>
</div>
</section>`;
},
formTemplate(model){
return `<form id="preferences-form" class="row ${model.settings.classNames.form}" novalidate>
${Object.keys(model.settings.types).map(type => `<div class="privacy-banner__col col"><fieldset class="${model.settings.classNames.fieldset}">
<legend class="${model.settings.classNames.legend}">
<span class="${model.settings.classNames.title}">${model.settings.types[type].title}</span>
<span class="${model.settings.classNames.description}">${model.settings.types[type].description}</span>
</legend>
<div class="privacy-banner__row">
<div class="relative">
<label class="privacy-banner__label">
<input
class="${model.settings.classNames.field}"
type="radio"
name="privacy-${type.split(' ')[0].replace(' ', '-')}"
value="1"
${model.consent[type] === 1 ? ` checked` : ''}>
<span class="privacy-banner__label-text">Ok</span>
<span class="privacy-banner__label-description">${model.settings.types[type].labels.yes}</span>
</label>
</div>
</div>
<div class="privacy-banner__row">
<div class="relative">
<label class="privacy-banner__label">
<input
class="${model.settings.classNames.field}"
type="radio"
name="privacy-${type.split(' ')[0].replace(' ', '-')}"
value="0"
${model.consent[type] === 0 ? ` checked` : ''}>
<span class="privacy-banner__label-text">No</span>
<span class="privacy-banner__label-description">${model.settings.types[type].labels.no}</span>
</label>
</div>
</div>
</fieldset></div>`).join('')}
<div class="privacy-banner__set col">
<button class="${model.settings.classNames.submitBtn}"${Object.keys(model.consent).length !== Object.keys(model.settings.types).length ? ` disabled` : ''}>Save my choices</button>
<div class="privacy-banner__set-accept">Or <button type="button" class="privacy-banner__btn-text ${model.settings.classNames.acceptBtn}">Accept and close</button></div>
</div>
</form>`;
}
};
let bannerToggle;
document.addEventListener('banner.show', () => [ bannerToggle ] = toggle('.js-toggle-banner'));
document.addEventListener('banner.hide', () => bannerToggle.startToggle());
const banner = cookieBanner(config);
[].slice.call(document.querySelectorAll('.js-preferences-update')).forEach(btn => btn.addEventListener('click', e => {
banner.showBanner();
bannerToggle.startToggle();
}));
Acceptance criteria
The following is a list of example acceptance criteria to test against when using this pattern. These critera should test that the specific markup requirements are met, and that the cookie banner behaves visually and functionally as expected.
For validation in developer tools / web inspector
- The cookie banner should be an HTML element with a
role="dialog"
attribute. This element must contain everything that's visible within the banner when it opens - The cookie banner element should either have an
aria-label
attribute which titles the content, or anaria-labelledby
attribute that points to a visible<h2>
element with a matching ID - An HTML
<button>
element should be present to accept all cookies - An HTML
<button>
element should be present to expand the consent form and show the available cookie options - Within the preferences form an HTML
<button>
element should be present to accept all cookies - Within the preferences form an HTML
<button>
element should be present to save user preferences - Any form inputs within the cookie preferences form should have a matching
<label>
describing the choice - All cookie selection inputs within the cookie preferences form should be grouped in
<fiedset>
tags with appropiate<legend>
tags for each cookie type
For functional validation
- The cookie banner should only appear if the user has not previously set their cookie preferences
- On selecting 'accept all cookies' the banner should immediately close
- On opening the cookie preferences form, the visual and keyboard focus should move to an HTML
<button>
element which acts as a shortcut to accept all cookies and close the banner - Once the preferences form is open, a user should be able to move through the available cookie types and set their preferences
- The button for saving the user preferences should be disabled until a user has made a choice for all cookie types
- Once the user has saved their preferences via the banner, the banner should close and not re-appear until the user clears their cookies, or until the preference cookie expires (1 year from date set)
- No functionality or third-party service that sets cookies should run until the user has consented to that type of cookie
- When the banner has closed and a user has accepted a cookie type, any functionality that relies on that cookie type (eg. analytics) should immediately run
- Consent should be saved in the browser when the user returns to the site, unless the user has cleared their cookies, or until the preference cookie expires (1 year from date set)