Designing a cookie consent modal certified by TCF IAB
In this comprehensive guide, the third installment of our series “Mastering CMP and IAB TCF: a developer’s guide”, we will focus on designing a cookie consent modal that meets the stringent standards set by the Interactive Advertising Bureau (IAB) Transparency and Consent Framework (TCF).
Building a user-friendly cookie consent popup
Creating a cookie consent popup that is both compliant and user-friendly is a balancing act. The design should be clear and unobtrusive, yet provide all necessary information and options for the user. Here are some tips:
- Clarity: use simple language to explain what cookies are, why they are used, and what the user’s choices are.
- Visibility: ensure that the popup is easily noticeable without being disruptive.
- Easy navigation: provide straightforward options for acceptance, rejection, and accessing more information or settings.
Storing and retrieving user consent
Once the user has made their choices, it’s essential to store this information in a way that is accessible and secure. You can store the user’s consent choices in cookies or local storage for easy retrieval.
Utilizing Klaro.js for UI/UX enhancements
Klaro.js stands out as a valuable open-source resource designed to elevate the user experience associated with cookie consent popups.
When integrated into a Consent Management Platform, Klaro.js works harmoniously with the IAB’s Transparency and Consent Framework (TCF), delivering a user experience that is not only compliant but also smooth and user-centric, particularly in the realm of managing consent preferences.
Setting up Klaro and TCF dependencies
First, ensure that you have imported the necessary libraries and set up the initial configurations:
import * as Klaro from './../klaro/dist/klaro.js'; import { CmpApi } from '@iabtechlabtcf/cmpapi'; import { TCModel, TCString, GVL } from '@iabtechlabtcf/core'; import * as cmpstub from '@iabtechlabtcf/stub'; const GVL_BASE_URL = 'https://myserver.com/tcf/'; const GVL_LATEST_FILENAME = 'vendor-list.json'; const CMP_ID = 999; const CMP_VERSION = 1500; cmpstub(); GVL.baseUrl = GVL_BASE_URL; GVL.latestFilename = GVL_LATEST_FILENAME;
This configuration script is a setup for integrating Klaro.js. Here’s a breakdown of the script and its components:
- Importing libraries and modules:
- Klaro: the Klaro.js library is imported to handle the user interface and interaction for cookie consent.
- CmpApi, TCModel, TCString, GVL: these are components from the IAB TCF library, used to create, manage, and encode the Transparency and Consent (TC) String, as well as to interact with the Global Vendor List (GVL).
- cmpstub: a stub library from IAB TCF, which ensures that the CMP (Consent Management Provider) API is available before the full CMP implementation loads.
- Configuration constants:
- GVL_BASE_URL: The base URL where the Global Vendor List (GVL) and other related IAB TCF files are hosted.
- GVL_LATEST_FILENAME: the filename of the latest version of the GVL. This file contains information about all the vendors and the purposes for which they process user data.
- CMP_ID: a unique identifier for the Consent Management Platform.
- CMP_VERSION: the version number of the CMP.
Initializing TCModel and CmpApi
Create and configure instances of TCModel and CmpApi, ensuring they are ready to handle consent data:
const tcModel = new TCModel(new GVL("LATEST")); tcModel.isServiceSpecific = true; const cmpApi = new CmpApi(CMP_ID, CMP_VERSION, true);
Setting up consent data transformation and the TCModel Update
Define a function to transform consent data from Klaro to the format expected by the TCModel, and update the TCModel and CmpApi accordingly:
function ctSetupTCModel(data) { tcModel.isServiceSpecific = true; tcModel.gvl.readyPromise.then(() => { // Set consent data based on the transformed Klaro data tcModel.vendorConsents.set(data.vendorConsents); tcModel.vendorLegitimateInterests.set(data.vendorLiConsents); tcModel.purposeConsents.set(data.purposeConsents); tcModel.specialFeatureOptins.set(data.specialFeatureOptins); // Set CMP identifiers tcModel.cmpId = CMP_ID; tcModel.cmpVersion = CMP_VERSION; // Encode TCModel to TCString and update CmpApi const encodedTCString = TCString.encode(tcModel); cmpApi.update(encodedTCString, true); }).catch(err => { console.error("An error occurred while loading the GVL:", err); }); }
Handling DOMContentLoaded and Klaro Events
Finally, set up event listeners to handle the loading of consents and updates from Klaro:
document.addEventListener('DOMContentLoaded', function() { if (!Klaro) return; const manager = Klaro.getManager(klaroConfig); const klaroData = { consents: manager.loadConsents() }; ctSetupTCModel(ctConstructMyData(klaroData)); manager.watch({ update(_, eventName, klaroData) { if (eventName === 'saveConsents') { const myData = ctConstructMyData(klaroData); ctSetupTCModel(myData); } } }); });
In this setup, ctConstructMyData is assumed to be a function that transforms the Klaro consent data into the format expected by ctSetupTCModel, aligning with the TCModel structure.
Crafting a Klaro configuration
Klaro provides a configurable and user-friendly interface for managing user consents. Attached is a practical example of a Klaro configuration, detailing each component and its role in the consent management process.
Here is a detailed example of a Klaro configuration object:
{ version: 1, elementID: "klaro", styling: { theme: ["light", "top", "narrow"] }, noAutoLoad: false, htmlTexts: true, embedded: false, groupByPurpose: true, storageMethod: "localStorage", cookieName: "ct-cmp-settings-505", cookieExpiresAfterDays: 365, default: false, mustConsent: true, acceptAll: true, hideDeclineAll: false, hideLearnMore: false, noticeAsModal: false, disablePoweredBy: true, lang: "en", translations: { zz: { privacyPolicyUrl: "/privacy-policy/" }, en: { ok: "Accept all", consentModal: { title: "<u>Customise Ad Choices</u>", description: "We and our partners (3) use cookies and other data to store, access and/or process your device information and personal data, like unique identifiers and browsing history. Advertising and content can be personalised..." }, purposes: { purposes: "Purposes", special_purposes: "Special Purposes", features: "Features", special_features: "Special Features", partners: "Partners", li_partners: "Partners (Legitimate Interest)" } } }, services: [ //... services configurations go here ] }
In the services array, you define the individual services (vendors, purposes, etc.) for which you are seeking consent. Each service object includes properties such as name, title, description, purposes, and required.
"services": [ { "name": "pu_3", "title": "Create profiles for personalised advertising (3 partners)", "description": "Information about your activity on this service (such as forms you submit, content you look at)...", "purposes": [ "purposes" ], "required": false }, { "name": "spfu_1", "title": "Use precise geolocation data", "description": "With your acceptance, your precise location (within a radius of less than 500 metres) may...", "purposes": [ "special_features" ], "required": false }, { "name": "ve_6", "title": "AdSpirit GmbH", "description": "<p>Privacy policy: <a href=\"https:\/\/help.adspirit.de\/privacy.php\" target=\"_blank\">https:\/\/help.adspirit.de\/privacy.php<\/a><\/p><p> Legitimate Interest claim: <a href=\"https:\/\/help.adspirit.de\/privacy.php\" target=\"_blank\">https:\/\/help.adspirit.de\/privacy.php<\/a><\/p><div class=\"toggle-section\"><p class=\"toggle-title\">Purposes (Consent) \u25bc<\/p>....<\/div>", "purposes": [ "partners" ], "required": false }, { "name": "veli_8", "title": "Emerse Sverige AB", "description": "<p>Privacy policy: ...</p>", "purposes": [ "li_partners" ], "required": false, "purposesLegitimate": true, "default": false }, { "name": "fe_1", "title": "Match and combine data from other data sources", "description": "Information about your activity on this service may be matched and combined with other...", "purposes": [ "features" ], "required": true }, { "name": "sppu_2", "title": "Deliver and present advertising and content", "description": "Certain information (like an IP address or device capabilities) is used to ensure...", "purposes": [ "special_purposes" ], "required": true } ]
Best practices for efficient loading
Optimizing the performance of your website is paramount, and the loading behavior of your cookie consent popup plays a significant role in this context. We will explain best practices for efficient loading, focusing on minimizing the number of partners/vendors displayed and implementing the defer attribute for script loading.
Minimizing the number of partners/vendors displayed
A cluttered and overwhelming list of partners or vendors can lead to user frustration and may negatively impact the user’s experience on your website. To optimize performance and enhance user experience:
- Curate your list: regularly review and curate the list of partners or vendors to ensure that only relevant and necessary entities are included. This not only simplifies the user’s decision-making process but also contributes to faster loading times.
Implementing the defer attribute for script loading
The defer attribute ensures that your script is executed only after the HTML document has been fully parsed, contributing to a non-blocking loading behavior. This is especially crucial for scripts that do not affect the initial rendering of the page.
In the context of WordPress, you can enqueue your script and conditionally add the defer attribute as follows:
wp_enqueue_script('ct-ultimate-gdpr-tcfcmp', ct_ultimate_gdpr_url('assets/tcf/dist/bundle.js'), array(), ct_ultimate_gdpr_get_plugin_version().'_' . $this->tcf_version, false); add_filter('script_loader_tag', function($tag, $handle) { if ('ct-ultimate-gdpr-tcfcmp' !== $handle) { return $tag; } return str_replace(' src', ' defer="defer" src', $tag); }, 10, 2);
The impact of TCF 2.2 on consent modal design
The introduction of TCF 2.2 has brought about significant changes in how consent modals should be designed and structured, ensuring a more transparent and user-centric approach.
1. Clear and concise wording
The wording used in the consent modal must be clear, straightforward, and devoid of any legal jargon. Users should be able to understand the purpose of data collection, the entities involved, and the implications of their choices without ambiguity.
- Simplicity: use simple language that is easily understandable by the average user.
- Transparency: clearly state the purposes of data collection and processing, ensuring that users have a comprehensive understanding.
- Use of stacks: integrate the concept of ‘stacks’ into your consent modal. Stacks are pre-defined collections of purposes, special purposes, features, and special features that can be presented as a single choice/text to the user.
2. Structured layout: first layer and second layer
A well-structured consent modal typically consists of two layers. The first layer provides a brief overview, while the second layer offers detailed information and granular control.
- First layer: present a concise summary of the data collection practices, with options to accept or customize settings. Make sure to display the total number of vendors involved.
- Second layer: Offer detailed information on each vendor, the purposes of data processing, and the ability to provide or withdraw consent on a granular level.
3. Vendor total count and easy withdrawal
Transparency about the total number of vendors and providing easy options to withdraw consent are crucial for user trust and compliance.
- Vendor count: clearly display the total number of vendors seeking consent, giving users a clear picture of the extent of data sharing.
- Withdrawal options: ensure that users can easily withdraw their consent, either in part or in full, at any time.
4. User control: changing preferences and opting out
Users should have the autonomy to change their preferences or opt out entirely, even after their initial choices.
- Change preferences: allow users to revisit and alter their consent choices at any time.
- Opt-out from all: include an option for users to easily withdraw consent for all vendors and purposes with a single action.
5. Accessibility and design considerations
The consent modal should be accessible to all users, including those with disabilities, and adhere to design best practices.
- Contrast ratio: ensure that the text and background have a sufficient contrast ratio, making the content readable for users with visual impairments.
- Responsive design: the modal should be optimized for various devices and screen sizes.
6. Links to legitimate interests and privacy policy
Each vendor involved in data processing and advertising activities has its own set of policies and practices regarding user data. It is imperative that users are not only made aware of these policies but also have straightforward access to them.
- Privacy policy: display a direct link to the privacy policy of each vendor
- Legitimate interests: Provide a separate link for users to explore and understand the legitimate interests claimed
vendor-list.json and TC string encoding/decoding in consent modals
The IAB TCF framework mandates the use of a standardized vendor list, typically provided in a vendor-list.json file, and the Transparency and Consent String (TC String) for encoding and decoding user consent choices.
Utilizing vendor-list.json in consent modals
The vendor-list.json file plays a crucial role in ensuring transparency and standardization across different vendors and consent management platforms (CMPs).
- Vendor information: the file contains a list of vendors, along with their IDs, names, and descriptions of the purposes for which they collect and process user data.
- Purposes and features: It also details the various purposes for data processing and features used by vendors, providing clear definitions and descriptions.
When a user visits a website, the consent modal interacts with the vendor-list.json file in the following ways:
- Dynamic content population: the consent modal populates its content dynamically, listing the vendors and the purposes for data processing as specified in the vendor-list.json file.
- User choices: users are provided with options to accept or reject specific vendors or purposes, and their choices are recorded.
TC String encoding and decoding
The TC String is a compact, URL-safe string that encodes the user’s consent choices. It serves as a standardized format for transmitting user consent across the advertising ecosystem.
- Encoding: when a user makes their consent choices on the consent modal, these choices are encoded into a TC String. The encoding process translates the user’s choices into a series of bits, each representing a specific choice or preference.
- Decoding: when the TC String needs to be read (for example, by a vendor or an ad tech platform), the decoding process translates the series of bits back into a readable format, revealing the user’s consent choices.
Key takeaways
- User-centric design: we emphasized the importance of building a consent modal with the user in mind, ensuring clear wording, a structured layout, and straightforward options for consent management.
- Compliance and transparency: the guide highlighted the need for compliance with TCF 2.2, ensuring transparency over the number of vendors, ease of consent withdrawal, and improvements in user-facing standard texts.
- Technical integration: we explored the technical aspects of supporting the CMP API, setting CMP identifiers, and converting user choices to the TC String format.
- Performance optimization: best practices for efficient loading were discussed, underscoring the importance of minimizing the number of vendors displayed and using the defer attribute for script loading to enhance website performance.
- Leveraging Klaro: the guide provided an example of configuring the Klaro consent modal, demonstrating how to fetch configuration data dynamically from vendor-list.json and ensure accurate reflection of user consent preferences.
Purchase the most popular GDPR WordPress plugin tailored to Google Consent Mode v2. Remember to use only certified solutions. You can also find our GDPR & CCPA WordPress plugin on Codecanyon