It’s hard to browse the web without coming across the ubiquitous cookie notice these days. In fact, it’s become a source of annoyance for many people. Privacy regulations oblige countless website owners to ask their visitors for consent before using any cookies that aren’t necessary for the site to function. Generally, those are cookies used for tracking and targeting.
In this article, I’ll present a pattern for a simple cookie notice. I mostly aim to demonstrate the ease of the native dialog element and of the Cookie Store API; two recent or recently improved web features that greatly reduce our effort. In doing so, I’ll be playing devil’s advocate as I’d rather see all forms of tracking disappear, and with it the need for these notices. In other words, only use this if you must.
Permission for analytics?
Cookies used in website analytics are deemed non-essential. That means you need a consent notice that informs your visitors and gathers their explicit consent before setting such cookies on their device. For many websites, this is the only additional source of cookies, meaning we can be concise and specific in how we present the choice to the user. The term “cookie” acts as a placeholder for any similar technology here, so switching to Local Storage won’t relieve you from an explicit opt-in. In fact, let me discourage you one more time.
I believe there’s a strict hierarchy of options regarding tracking and analytics. Ranked from best to worst:
- Don’t track anything. Soon, humanity shall unite in everlasting peace and prosperity.
- Use traditional analytics like Google Analytics, but ask for consent before doing so.
- Disregard your users and silently track them anyway. Your version of hell—or a lawsuit—awaits.
We’re concerned with the third option here. Let’s get to it.
As announced, we’ll use the dialog element. This native dialog implementation suffered a bunch of problems for a long time, but recent changes to the HTML specification have caused it to mature to the point of generally being the preferred option over rolling out a custom version. Accessibility expert Scott O’Hara—who’s been monitoring and testing the dialog for years—gave the green light back in January.
The markup is elegant and looks like this:
The heading and paragraph text provide context to the dialog, especially when paired with the
aria-describedby roles. The true novelty is the
open attribute to the dialog element, like so:
Then we’ll add some styling.
We’ll style our cookie notice as a non-modal dialog that sits in the bottom right corner of the screen. The dialog being non-modal means you can still interact with the rest of the webpage.
The restrictions on
max-block-size ensure the notice doesn’t overflow the viewport on smaller screens and stays clear of the edges. By adding
overflow-y: auto, the dialog becomes scrollable in the vertical direction if needed. If we’ve scrolled all the way down,
overscroll-behavior-block prevents the rest of the page to start scrolling too. The remaining styles provide consistent spacing between the elements in our dialog. Adding some extra cosmetics, the notice now looks something like this:
The bottom right positioning is clearer at full scale, though. Make sure to remove the
open attribute before heading to the next step.
Opening and closing the dialog
Let’s get our cookie notice to work. On page load, we should check for any stored preferences. If the user previously consented to the use of tracking cookies, the analytics script can go ahead and run. If we don’t find any preferences, we’ll show the dialog so the user can make a choice. We’ll save that choice in a cookie.
This is where the native dialog really starts to shine. Opening the dialog is as easy as calling the
show() method—no fiddling with ARIA roles or classes. We then add an event listener for the
close event. Remember how the “Accept” and “Reject” buttons automatically close the dialog through the
method="dialog" attribute on the parent form. This will also trigger the
close event. The
returnValue property on our dialog is now set to the
value attribute of whichever button we clicked.
As a last step, we should add the undefined
Old and new cookies
Getting and setting cookies the old way is surprisingly cumbersome. It involves weird string manipulation on the
document.cookie property. Luckily, there’s the newer Cookie Store API with its simple
set() methods. Browser support isn’t that great yet, with notably Firefox and Safari missing, but we’ll use the old cookie methods as a fallback. Here’s the code for the missing functions:
Now you may wonder why we go through the trouble of supporting two ways of getting and setting cookies, when Local Storage provides the same functionality with perfect browser support. The answer lies in the expiration date: items in Local Storage never expire. According to the GDPR, consent must be renewed at least once a year, and some national data protection hardliners like the French CNIL even recommend renewal every six months. Setting a cookie that expires ensures we comply with these regulations.
Here’s the result as a standalone or as an embedded version. The messages in the console will tell you if the settings cookie was found or not, and if analytics should run or not.
Permission for even more things?
What if our website uses other non-essential cookies besides the ones used for analytics? Privacy laws stipulate that users should receive granular control over the types of data collecting they do or do not agree to. That means we have to adapt our cookie notice to include several categories. We’ll make use of checkboxes. Let’s have a look.
A bit more markup and styling
We’ve added a third cookie category to the mix: cookies set by YouTube embeds. Each category gets a checkbox that’s linked to a descriptive paragraph through the
We’ll add just a dash more CSS:
Our cookie notice grew a bit bigger and now looks like this:
I’ve added the “YouTube” category on purpose, to demonstrate yet another way to go. In my article on privacy-friendly video embeds, I showed how you can replace the YouTube video iframe with an overlay that prompts the user to consent to YouTube’s cookies before loading the video. I called this just-in-time consent, because we asked the user for consent only at the time it’s needed. For analytics, that means on page load of any page; but video embeds aren’t usually the first thing you land on when visiting a website. We could employ the overlay strategy for the videos, in which case we fall back to the very simple cookie notice we made before.
A bit more cookies
And here’s the final result, as a standalone page and as an embed:
If you must use a cookie notice, then it might as well be a lightweight and an accessible one. The native dialog element does a lot of the heavy lifting, and the Cookie Store API makes storing and retrieving cookies a breeze.