Privacy Choices is a JavaScript library that allows you to give the users of your website choices regarding the use of cookies and other web storage technologies on their device.
I am not a lawyer. You should independently verify - through your own research - that your use of this tool and your configuration of it henceforth is suitable to satisfy any legal requirements that you may be making use of it to satisfy.
If you are in any doubt, I strongly advise that you consult a legal professional before continuing.
If your website is impacted by EU privacy legislation, it is worth doing your research into why the legislation exists, how you are affected and what exactly is required of you prior to implementing any solution on your website. To that end, in the expandable section below is a short collection of what I learnt during the creation of this library about EU privacy legislation and how it impacts the use of web storage technologies.
Show/hide section
Under the Privacy and Electronic Communications Directive 2002/58/EC on Privacy and Electronic Communications (ePrivacy Directive), since 2003 anyone storing information on users' devices has been required to provide clear information about that storage (1).
In May 2011 the ePrivacy Directive was amended by Directive 2009/136/EC. A requirement to obtain consent for cookies and similar technologies was added, meaning it was no longer enough to simply let a user know that storage was being used (1).
In summary, as of May 2019 (2):
The ePrivacy directive – more specifically Article 5(3) – requires prior informed consent for storage or for access to information stored on a user's terminal equipment. In other words, you must ask users if they agree to most cookies and similar technologies (e.g. web beacons, Flash cookies, etc.) before the site starts to use them.
The General Data Protection Regulation (EU) 2016/679 (GDPR) was implemented in 2018 and specifies how personal data should be lawfully processed (including how it is collected, used, protected or interacted with) (3). While GDPR doesn't say anything about how cookies and other online identifiers should be used, it does specifically name them as types of personal data (4).
As a result, after the implementation of GDPR the use of web storage is still governed by the ePrivacy Directive (5). However, if a website uses storage that could identify or track a user then that website now has to comply with GDPR data-handling requirements as well (4).
If you didn't bother to read that then at least take note of the the short version:
- From 2003 - websites have to tell users from the EU about the storage they use
- From 2011 - websites also have to obtain consent from the user before using this storage
- From 2018 - websites using storage also need to comply with GDPR data-handling requirements
A friend of mine is involved with the Nightline Association, a charity that supports student listening services open at night and run by students for students at nearly 40 educational institutes across the UK and Ireland. When attempting to identify a solution to handle their use of web storage across their websites and applications it became clear that there wasn't anything that quite fit their needs.
Few of the many free or open-source plugins available online really felt like the right fit as they were either missing certain desired functionality or required a significant amount of manual customisation to get the look and feel that was desired.
Conversely, a number of the available paid-for solutions seemed like exactly what was wanted but for a charity it just wasn't feasible to pay a yearly subscription to get access to the one or two 'pro-edition' features that they needed.
As a result, I decided to put together Privacy Choices. It is my attempt at a privacy management library that, while it might not have all of the bells and whistles of the subscription-based solutions, has a clean look and feel, an intuitive settings panel, is highly customisable, and is free to use.
There are a number of live demo examples with various configurations here.
Head over to the releases page to get the latest version, compiled and ready to use.
-
A notification banner prompting users to make their choices
-
Equal-weighted options for accepting and denying full use of storage
-
Privacy settings always available to the user through an on-screen toggle:
-
A configurable list of storage categories that users can toggle on or off according to their preference in the settings sidebar
-
Support for when only strictly-necessary storage is in use, whereby the library acts as an information tool about privacy on your site
-
Option to display a link to your site's privacy policy
-
Configurable number of days after which a user's choices will expire
-
Stores user preferences in a cookie across all pages on the current domain
- Sub-domains will have their own preferences - they will not be shared with each other or their parent domain
-
JavaScript callbacks that execute when categories are toggled, allowing you to manage turning on and off your storage to respect your users' wishes
-
Lots of customisation
There are some things not covered by this library, which may or may not change in future releases. Do your research and reconsider whether to use Privacy Choices if your use case requires any of the below:
-
Handling of third-party cookies
-
Sharing users' preferences across a parent domain and its sub-domains
-
Multiple language support
-
The default configuration has text in English (UK)
-
Currently, all text values can be overidden (which you could do in another language)
-
-
Using users' geolocation to conditionally display the tool or not
- Change the wording to better inform users about how you use storage on your site
- Alternatively, override with text in your own language
- If these are changed, you should ensure that the replacement text is clear and unambiguous
- Match the tool to your site's design by changing text and background colours
- You should keep things user-friendly if you change things - don't use these settings to trick users
- After a configurable number of days users will be asked to provide their choices again
- Make sure your users stay in control by regularly reminding them to update their privacy settings
- The default reminder is every 90 days, and this can be changed to any value
- If changed, consider the context of your website when deciding how often to make users update settings
- Point users in the direction of your full privacy policy or other statement
- If you already have links to this that you wish to keep, the link can be disabled
- Add as many or few categories as needed
- Every category you use must be covered, although these categories are defined by you according to your use case
- In the case of only using necessary storage, define no categories to enable 'informational mode'
- These should entirely manage your storage usage if you use this library
- There is one for:
- The necessary category
- Every custom category, when turned on
- Every custom category, when turned off
Include privacy-choices.bundle.js
at the bottom of your <body>
:
<!DOCTYPE html>
<html>
<head>
<title>Installation</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Installation</h1>
<p>The rest of the page content.</p>
<!-- Include privacy-choices.bundle.js at the bottom of your <body> -->
<script type="text/javascript" src="path/to/privacy-choices.bundle.js"></script>
</body>
</html>
This should be done anywhere that you wish to show the tool (probably everywhere). For the tool to be effective the library must be loaded at all times as soon as the user lands on any page on your site.
Depending on your framework (or lack thereof) a theme or template would be a good way to include the script because you would only need to do it in one place. Alternatively, you will have to include it on every static page.
Privacy Choices should scale appropriately so that it has good usability on smaller devices (e.g. mobile). However, to take advantage of this, you should configure the viewport on your site to support such devices:
<head>
...
<meta name="viewport" content="width=device-width, initial-scale=1">
...
</head>
More information on the browser's viewport can be found in the MDN Web Docs.
Configuration is set as a JavaScript object window.privacyChoicesConfiguration
. It is merged with the default configuration that can be seen further down this README, with the configuration you set on the page taking priority. You only need to set what you want to override.
The configuration setting should also be included in your <body>
, but above the inclusion of the library itself:
<!DOCTYPE html>
<html>
<head>
<title>Configuration</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Configuration</h1>
<p>The rest of the page content </p>
<!-- Add configuration to the bottom of the <body> before the library itself -->
<script>
window.privacyChoicesConfiguration = { }
</script>
<script type="text/javascript" src="path/to/privacy-choices.bundle.js"></script>
</body>
</html>
While it would be recommended to configure some more things to suit you needs, the minimal configuration would only need to define the set of consent categories and the callbacks to manage the user's acceptance or non-acceptance of these. The live demo here shows this configuration.
window.privacyChoicesConfiguration = {
necessary: {
handle: function () {
/*
* This runs once, on page load.
*
* You should configure any strictly-necessary storage here.
*/
}
},
categories: [
{
storageKey: 'example1',
title: 'Category title',
description: 'Category description.',
handleAccept: function () {
/*
* This runs whenever the category is toggled ON, plus at page load if currently turned ON.
*
* You should set up, enable or configure any storage for this category here.
*/
},
handleReject: function () {
/*
* This runs whenever the category is toggled OFF, plus at page load if currently turned OFF.
*
* You should tear down, disable or remove any storage for this category here.
*/
}
},
{
storageKey: 'example2',
title: 'Category 2 title',
description: 'Category 2 description.',
handleAccept: function () {
/*
* This runs whenever the category is toggled ON, plus at page load if currently turned ON.
*
* You should set up, enable or configure any storage for this category here.
*/
},
handleReject: function () {
/*
* This runs whenever the category is toggled OFF, plus at page load if currently turned OFF.
*
* You should tear down, disable or remove any storage for this category here.
*/
}
},
]
}
An alternate configuration of Privacy Choices is available in the case of a website that only uses necessary storage. In this case, a website need only inform the user that they use storage for making the site work and nothing more. Simply don't define the categories
in the configuration and the library will run with a Dismiss
button instead of Accept
and Reject
buttons. The live demo here is an implementation of the below configuration, which may be suitable for such a use case.
window.privacyChoicesConfiguration = {
necessary: {
handle: function () {
/*
* This runs once, on page load.
*
* You should configure any strictly-necessary storage here.
*/
},
title: 'Strictly-necessary cookies',
description: 'These cookies enable core site functionality. This site cannot function properly without them, and they can only be disabled by changing your browser preferences.',
},
language: {
settings: {
openButton: 'Privacy Information',
closeButton: 'Close',
heading: 'Privacy Information',
description: 'This site uses cookies to enable core site functionality. No other storage is kept on your device.'
},
prompt: {
heading: 'Privacy information for this site',
description: 'This site uses cookies to enable core site functionality. No other storage is kept on your device.',
settingsButton: 'More information',
dismissButton: 'Dismiss'
}
},
}
The full set of configuration values are below. There are inline comments to describe them. The values assigned in the code snippet below are the default ones that any custom configuration defined on the page will overwrite:
window.privacyChoicesConfiguration = {
necessary: {
// OPTIONAL (string) the heading for the necessary storage category in the settings sidebar
title: 'Strictly necessary storage',
// OPTIONAL (string) the longer text for the necessary storage category in the settings sidebar
description: 'Necessary storage enables core site functionality. This site cannot function without it, so it can only be disabled by changing settings in your browser.',
// REQUIRED (function) the callback executed once on every page load where you should manage your necessary storage
handle: function () { }
},
policy: {
// OPTIONAL (boolean) whether the link to a privacy policy should be shown in the settings sidebar
display: true,
// OPTIONAL (string, URI) the location to link to for the privacy policy
uri: 'https://example.com/policy'
},
/* OPTIONAL (object array)
* The default value is an empty array, but an example category is included here for reference.
* Leave empty (i.e. don't define it in custom configuration) to run the library in informational mode.
*/
categories: [
{
// REQUIRED (string) unique identifier for this category in the user preferences cookie (should not contain spaces)
storageKey: 'analytics',
// REQUIRED (string) the title of the category as shown in the settings sidebar
title: 'Analytics storage',
// REQUIRED (string) the description of the category as shown in the settings sidebar
description: 'We use analytics to track visits to our sites. We also track which pages you view while you\'re hear to learn how we could improve the site.',
// REQUIRED (function) the callback executed whenever this category is toggled on (or when already on and the page is loaded) - you should manage enabling of this storage here
handleAccept: function () { },
// REQUIRED (function) the callback executed whenever this category is toggles off (or when already off and the page is loaded) - you should manage disabling and clearing of this storage here
handleReject: function () { }
}
],
language: {
// OPTIONAL (object) language options for the settings sidebar
settings: {
// OPTIONAL (string) text for the always-present button that opens the settings sidebar
openButton: 'Privacy',
// OPTIONAL (string) text for the settings sidebar close button
closeButton: 'Close',
// OPTIONAL (string) text for the setting-sidebar title
heading: 'Privacy information',
// OPTIONAL (string) text for the settings sidebar below the title
description: 'This site uses cookies and other web storage technologies. You can set your privacy choices below. Changes will take effect immediately.',
// OPTIONAL (string) text for the button on the settings sidebar that toggles all categories on
acceptAllButton: 'Accept all',
// OPTIONAL (string) text for the button on the settings sidebar that toggles all categories off
declineAllButton: 'Decline all'
},
// OPTIONAL (object) language options for the notification prompt banner
prompt: {
// OPTIONAL (string) text for the heading on the notification prompt
heading: 'Your privacy choices for this site',
// OPTIONAL (string) longer text on the notification prompt
description: 'This site uses cookies and other web storage technologies to enhance your experience beyond necessary core functionality.',
// OPTIONAL (string) text on the button that the user can accept everything using
acceptButton: 'Accept',
// OPTIONAL (string) text on the button that the user can decline everything using
declineButton: 'Decline',
// OPTIONAL (string) text on the button that the user can click to see more options
settingsButton: 'Manage choices',
// OPTIONAL (string) text on the button that dismisses the banner when in informational mode
dismissButton: 'Dismiss'
},
// OPTIONAL (object) language options for the privacy policy link, used if window.privacyChoicesConfiguration.policy.display is set to true
policy: {
// OPTIONAL (string) the text proceeding the privacy policy link, after which will follow a space character and then the link
text: 'For more information on our use of web storage, please refer to our',
// OPTIONAL (string) the text that is hyperlinked to the URI specified by window.privacyChoicesConfiguration.policy.uri
linkText: 'Privacy Policy'
}
},
storage: {
// OPTIONAL (string) the name of the cookie that is used to store the user preferences in the browser (should not contain spaces)
key: 'privacy-choices',
// OPTIONAL (integer) the number of days after which a user will be asked to make their choices again
expiryDays: 90
},
style: {
// OPTIONAL (string, hex colour) the background colour of the settings toggle button
toggleBackgroundColour: '#222222',
// OPTIONAL (string, hex colour) the text colour of the settings toggle button
toggleTextColour: '#ffffff',
// OPTIONAL (string, hex colour) the background colour of the buttons
buttonBackgroundColour: '#690060',
// OPTIONAL (string, hex colour) the text colour of the buttons
buttonTextColour: '#ffffff',
// OPTIONAL (string, hex colour) the background colour of the notification prompt banner
promptBackgroundColour: '#222222',
// OPTIONAL (string, hex colour) the text colour of the notification prompt banner
promptTextColour: '#ffffff',
// OPTIONAL (string, hex colour) the background colour of the settings sidebar
settingsBackgroundColour: '#222222',
// OPTIONAL (string, hex colour) the text colour of the settings sidebar
settingsTextColour: '#ffffff'
}
}
This section will be useful for anybody who ends up cloning this repository.
If you just want to use Privacy Choices, head over to the releases page to get the latest version that is compiled and ready to go.
Show/hide section
-
The library source CSS is in
./src/css
. -
The library source JavaScript is in
./src/js
. -
The live site HTML is in
./examples
.
npm install
To start the live-updating web server:
npm run start
This builds the bundle in memory and hosts a local copy of the demo site at http://localhost:9000
. This location will automatically be launched in your default browser.
Changes to the bundle JavaScript or CSS will be reflected instantly by the web server without needing a manual page reload, as will changes to the demo site HTML.
To build the bundle and live demo site files for release:
npm run build
The built production mode artefacts (library bundle and demo site files) will be in ./build
. These should be hosted on a web server
The demo site isn't designed for viewing from the local file system as Privacy Choices stores the user's preferences in a cookie and many popular browsers don't support the use of cookies for file://...
browsing.
-
The sources referenced in the history section for providing information on the legislation
-
https://github.com/b3none/gdprconsent for technical inspiration around some of the patterns used
-
https://www.civicuk.com/cookie-control for look and feel inspiration for the user experience
Licensed under the MIT License.