Consent banner
This NPM package provides code for implementing a lightweight and accessible cookie / data consent banner on
Mozilla websites. This banner is used primarily by the Websites Team on sites such as www.mozilla.org,
but the code is provided here as a package should other teams at Mozilla find it useful.
What does it do?
The package contains all the CSS/JS required for displaying a banner, the necessary functions for accepting
or rejecting cookies, as well as an event based system to help your website code to manage user consent
actions. The UI and copy has been reviewed by Mozilla's legal team, and has also undergone internal
accessibility review. The banner behavior defaults to opt-in for cookies, however it can also
be configured to be opt-out depending on your requirements.
When should this banner be used?
Please see the Mozilla confluence page for an FAQ on how and when a cookie banner should
be used on Mozilla websites. You can also ask questions in the #websites-cookie-banner
channel on Slack.
What does it not do?
This package does not take on responsibility for deciding if the banner should be displayed depending on
a website visitor's geographical location. That part is up to individual sites to decide what is required /
appropriate. Mozilla's legal team can help with guidance for individual sites to follow.
Does the banner set a cookie that can be used across Mozilla subdomains?
Mozilla's legal team have stated that it is OK to assume that consent applies across mozilla.org
subdomains,
as long as the technologies deployed on those subdomains function in the same way (and for the same purposes).
As such, the banner sets a consent cookie with a default domain of .mozilla.org
when either accepting or
rejecting cookies.
It is therefore recommended to talk to legal about how your website uses cookies and other analytics tools
first, before using this banner. We should not assume that consent applies across Mozilla websites that are
on entirely different domains.
How can visitors change their cookie settings after interacting with the banner?
There will be a cookie settings page hosted on https://www.mozilla.org/ that explains
how Mozilla uses cookies and other analytics technologies. The page also enables people to update their cookie
settings after interacting with the banner. It is a legal requirement to link to this page in the banner UI,
as well as via a "Cookies" link in the website footer so that people can get back to it after the banner has
been dismissed.
[!IMPORTANT]
Mozilla websites should only link to the hosted cookie settings page above if their website is also hosted
on a mozilla.org
subdomain, and are using the default consent cookie that can be read across Mozilla
subdomains. Mozilla websites on entirely different domains or using different consent cookies should implement
their own settings page.
Installation
Install via NPM:
npm install @mozmeao/consent-banner --save
It is also required to install @mozmeao/cookie-helper as a peer dependency.
npm install @mozmeao/cookie-helper --save
Usage
Banner JavaScript
Import the banner via import
, require
, or by using a global variable in your script tag (choose one of
the methods below):
import MozConsentBanner from '@mozmeao/consent-banner';
const MozConsentBanner = require('@mozmeao/consent-banner');
const MozConsentBanner = window.MozConsentBanner;
You must also load CookieHelper
(which can also be imported using any of the above methods), and pass that as an
option to the banner when initializing it:
import CookieHelper from '@mozmeao/cookie-helper';
To initialize the banner:
MozConsentBanner.init({
helper: CookieHelper,
});
There are also several other optional arguments you can use when initializing the banner. A full list
of options are shown below:
Option | Required | Type | Description | Default Value |
---|
cookieDomain | false | String | Sets a consent cookie for a specific host name | .mozilla.org |
cookieExpiryDays | false | Number | Consent cookie expiry by number of days. | 182 (6 months) |
cookieID | false | String | Consent cookie identifier. | moz-consent-pref |
helper | true | Object | Reference to @mozmeao/cookie-helper peer dependency. | null |
optOut | false | Boolean | Sets the banner to opt-out mode. | false |
Opening and closing the banner UI
The consent banner code aims to be UI independent, so it fires custom JavaScript events for when things should happen.
For example, when the banner should open a mozConsentOpen
event is fired. And when the banner should close it fires a
mozConsentClose
event. A website can add standard event listeners on the window
object for these events.
window.addEventListener('mozConsentOpen', openBanner, false);
window.addEventListener('mozConsentClose', closeBanner, false);
MozConsentBanner.init({
helper: CookieHelper,
});
[!IMPORTANT]
The mozConsentOpen
event listener should be bound before init()
is called, since the event can fire straight away.
The mozConsentOpen
event will be fired automatically when init()
is called and a consent cookie does not yet exist.
Similarly, the mozConsentClose
event will be fired when cookies are either accepted or rejected and the banner is no
longer needed to remain open.
Here are is a basic vanilla JS example for handling opening and closing the banner:
function openBanner() {
document
.getElementById('moz-consent-banner-button-accept')
.addEventListener('click', MozConsentBanner.onAcceptClick, false);
document
.getElementById('moz-consent-banner-button-reject')
.addEventListener('click', MozConsentBanner.onRejectClick, false);
document.getElementById('moz-consent-banner').classList.add('is-visible');
}
function closeBanner() {
document
.getElementById('moz-consent-banner-button-accept')
.removeEventListener('click', MozConsentBanner.onAcceptClick, false);
document
.getElementById('moz-consent-banner-button-reject')
.removeEventListener('click', MozConsentBanner.onRejectClick, false);
document.getElementById('moz-consent-banner').classList.remove('is-visible');
}
Consent cookie
When either accepting or rejecting cookies, a consent cookie with the following attribute key/value pairs is set.
Attribute | Value |
---|
id | Defaults to moz-consent-pref . |
value | A stringified JSON object representing a visitor's granular cookie consent settings e.g. { preference: true, analytics: false } . Each key represents the consent status for "preference cookies" and "analytics cookies" respectively. |
expiry | Defaults to 6 months from the date when the cookie is set. |
path | Set to / so the cookie is readable from all pages. |
domain | Defaults to the same domain where the cookie was created. If the website is a mozilla.org subdomain, then the cookie domain will be set to .mozilla.org so that it is readable across all mozilla.org subdomains. |
samesite | Set to lax . |
Listening for consent status in website code
When the accept or reject buttons are clicked, the banner's JavaScript will fire a custom mozConsentStatus
event. Your website's code can listen for this event and respond accordingly to determine if analytics scripts
should be loaded, or attribution code should be run.
function initAnalytics(e) {
const hasConsent = e.detail.analytics;
if (hasConsent) {
window.removeEventListener('mozConsentStatus', initAnalytics, false);
}
}
window.addEventListener('mozConsentStatus', initAnalytics, false);
[!IMPORTANT]
To ensure that your front-end code receives events correctly, it is important to make sure that
MozConsentBanner.init()
is called after mozConsentStatus
event listeners have been bound
in your website's code. The easiest way to do this is to make sure the banner JS is the last
script loaded in your web page, or to wait for the window load
event to fire.
If the banner has been interacted with previously and a consent cookie already exists when init()
is called,
a mozConsentStatus
event will fire instead of a mozConsentOpen
event.
Banner HTML
As mentioned earlier, the banner JavaScript code is written in a way that aims to be UI independent. This is because
Mozilla websites are often built using different technology stacks for the front-end. Rather than try and take on the
role of rendering HTML, it is instead left to the website implementing the banner.
Here's an example of the recommended banner HTML markup:
<aside class="moz-consent-banner" id="moz-consent-banner" role="region" aria-label="Cookie Banner" data-nosnippet="true">
<div class="moz-consent-banner-content">
<h2 class="moz-consent-banner-heading">Help us improve your Mozilla experience</h2>
<div class="moz-consent-banner-copy">
<p>
In addition to cookies necessary for this site to function, we’d like your permission to set some additional
Cookies to better understand your browsing needs and improve your experience. Rest assured - we value your privacy.
</p>
<div class="moz-consent-banner-controls">
<button type="button" id="moz-consent-banner-button-reject" class="moz-consent-banner-button moz-consent-banner-button-reject">
Reject All Additional Cookies
</button>
<button type="button" id="moz-consent-banner-button-accept" class="moz-consent-banner-button moz-consent-banner-button-accept">
Accept All Additional Cookies
</button>
<a href="https://www.mozilla.org/privacy/websites/cookie-settings/">
Cookie settings
</a>
</div>
</div>
</div>
</aside>
You can also view the demo file in the GitHub repository to see a working example of the banner HTML.
HTML accessibility guidelines
The default banner CSS styling has undergone internal accessibility review, however there are a couple of important
things to note in the banner HTML:
- The banner should be an
<aside>
element, with role="region"
and aria-label="Cookie Banner"
attributes, so that
the banner can easily be searched for and discovered by screen reader users. - The banner should be the first element that appears in your page's HTML, ideally right after the opening
<body>
.
This helps to ensure that the buttons appear early in keyboard focus order, so users don't have to tab through all of
the web page content before reaching the banner.
Banner CSS
You can import the banner Sass styles using:
@import '~@mozmeao/consent-banner/styles';
There's also a pre-compiled CSS file called styles.css
in the package root which you can alternatively load:
<link href="/path/to/styles.css" rel="stylesheet" type="text/css">
[!NOTE]
The banner CSS defines the Inter web font as a font-family
, falling back to san-serif
. This package
does not include the Inter font directly, so the it must be loaded separately if you wish to use it.
Browser support
The banner is supported in all modern evergreen web browsers. It should also work in legacy browsers as far back as
Internet Explorer 9.
Local development
- Download this repo:
git clone https://github.com/mozmeao/consent-banner.git
- Move into repo folder:
cd consent-banner
- Build the banner files:
npm install && npm start
- A demo page for local development should automatically open at http://localhost:8000/
Running tests
To perform a run of the front-end JS tests using Jasmine and Jasmine Browser Runner
in both Firefox and Chrome:
npm run test
Building the NPM package
We use a Webpack configuration for building the contents of the NPM package ready for publishing. To build the
package, run:
npm run build
This will lint files, run tests, and then build the NPM package content in the ./dist/
directory.
Publishing to NPM
These steps assume you have logged into the NPM CLI and are apart of the @mozmeao organization on NPM.
The package is published to NPM under the @mozmeao/consent-bannner
namespace/package name. To publish a
release to NPM, use the following steps:
- Before you start make sure the project's CHANGELOG.md is up to date.
- Update the package
version
number in package.json (use [Semantic Versioning][semver]
to determine what the new version number should be). - Run
npm install
to update the package-lock.json
file. - Submit a pull request with your against the
main
branch. Once the changes have been approved and merged to main: - Run
npm run build
to run the front-end tests and then build the final package. The package contents will be located in ./dist/
. - Tag a new release. You can do this either using Git tag, or directly on the GitHub website.
(Example:
git tag -a v1.1.0
). If you used Git tag, push your tags to the repo using git push --tags
- If the build is successful and all tests pass, publish to NPM using
npm publish ./dist/
.