Cookie Control from CIVIC

Optional Categories


If you are interested in getting started with IAB Europe's Transparency and Consent Framework, please read the article TCF v2.0 .
From July 1, 2023, Google Analytics 4 (GA4) will fully replace Universal Analytics. This guide is still relevant for context to learn how a lot of tracking scripts should be configured, though for GA4 specifically we recommend viewing our guide on how to use GA4 with Google Tag Manager and Google Consent Mode click here.

Optional Categories offers complete freedom to support any third party software that you wish to use, though this form of consent management does require a level of manual set up to ensure no unwanted tracking occurs.

This introductory article aims to outline the general pattern for how you can do this in Cookie Control, by using an analytics category and integration with Google Analytics as an example.

If you are already confident with such software, you may wish to skip to the Final Snippet.

Anchor point for Step 1: Site cleanup Step 1: Site cleanup

Generally third party software such as analytics, marketing and social sharing tools are included via a <script> element.

As a rule, we recommend that you do not load scripts of this sort outside of Cookie Control since they may start setting cookies or sending tracking activity from your website before user consent is established.

Therefore, the first step is to locate and remove these references from your website.

In the case of Google Analytics, their documentation outlines the following code snippet:

<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->

Anchor point for Step 2: Cookie Categories Step 2: Cookie Categories

The next step is to extend your configuration by adding the property optionalCookies and as many categories as you'd like to present to the user.

To create a category, add a JavaScript Object within optionalCookies containing the following properties:

name A unique identifier for the category, will be used to store user preference. This value must be a valid cookie name, so special characters such as ()<>@,;:"?={}\/ are prohibited.
label The descriptive title assigned to the category and displayed to the user as a heading.
description The full description assigned to the category and displayed to the user as a paragraph.

In the example scenario, this would be single category to make users aware analytics software may be used on the website:

const config = {
    apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    product: 'XXX',
    optionalCookies: [
        {
            name : 'analytics',
            label: 'Analytical Cookies',
            description: 'Analytical cookies help us to improve our website by collecting and reporting information on its usage.',
        }
    ]
}

Anchor point for Step 3: Associate User Action Step 3: Associate User Action

By adding the above JavaScript Object, users will be able to understand the purpose behind this optional cookie category and be able to either grant their consent, or revoke it via the user interface.

Though these user actions need to be associated with the intended behaviour, which requires extending the JavaScript Object with the following properties:

onAccept Callback function that will fire on user's opting into this cookie category.
onRevoke Callback function that will fire on user's opting out of this cookie category.

Typically, this would mean following the third party software's guidance for loading the library as part of the onAccept callback; and following the software's guidance for User Opt-out as part of the onRevoke callback.

In the example of Google Analytics and similar tools, we have already seen the code snippet for loading the library referenced in step 1; and User Opt-out guidance can be found here:

window['ga-disable-UA-XXXXX-Y'] = true;

The only change needed is to remove any <script> elements from the suggested code snippets, as the onAccept callback and Cookie Control configuration will already be evaluated as JavaScript.

const config = {
    // apiKey and product removed for brevity
    optionalCookies: [
        {
            // name, label and description removed for brevity
            onAccept : function(){
                // Add Google Analytics
                (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
                    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
                    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
                })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

                ga('create', 'UA-XXXXX-Y', 'auto');
                ga('send', 'pageview');
                // End Google Analytics
            },
            onRevoke: function(){
                // Disable Google Analytics
                window['ga-disable-UA-XXXXX-Y'] = true;
                //
            }
        }
    ]
}

Anchor point for Step 4: Protected Cookies Step 4: Protected Cookies

Cookie Control attempts to clean up and remove as many cookies as it can every time a user interacts with the tool in order to respect their current preferences.

To ensure third party software functions correctly, we recommend extending each optional category with the following property:

cookies The names of the cookies that you wish to protect after a user opts in.

This property expects a JavaScript Array of String values identifying the Cookie Names to protect, though if convenient you may use the * wildcard at the end of the name to define multiple cookies that share the same prefix.

Again, the specific values needed here will depend on the third party software you wish to use so will need to refer to the respective software's guidance on Cookie Usage.

In the example scenario, this would mean reading Google's Analytics Cookie Usage and inputting those relevant to our usage. For instance:

const config = {
    // apiKey and product removed for brevity
    optionalCookies: [
        {
            // name, label and description, onAccept and onRevoke removed for brevity
            cookies: ['_ga', '_gid', '_gat', '__utma', '__utmt', '__utmb', '__utmc', '__utmz', '__utmv'],
        }
    ]
}

Anchor point for Step 5: Google Analytics Example Step 5: Google Analytics Example

The full code snippet and example configuration for Google Analytics can be found here:

Anchor point for Google Analytics 4 Consent mode Google Analytics 4 Consent mode

Google Analytics 4 Consent mode is a convenient way to adjust how Google tags behave based on the consent status of your users - making it easier for advertisers to manage cookies related to advertising and analytics and in turn comply with privacy legislation such as GDPR in the UK. GA4 has five keys: ad_storage, analytics_storage, functionality_storage, personalization_storage and security_storage, which may have the value of either "granted" or "denied". When the relevant consent is granted, the associated tags will utilise cookies and function normally as expected.

Currently, this dynamic behaviour only applies to Google tags deployed via gtag.js or Google Tag Manager and includes a limited set of products; Google Ads, Google Analytics and Floodlight.

If however you run Cookie Control without TCF enabled, then Consent mode does make configuration a lot easier for these products - simply because it does not require you to set up custom tags or events yourself. You only need to alter the value of ad_storage, analytics_storage, functionality_storage, personalization_storage and security_storage as part of your Cookie Control configuration - namely the relevant cookie category’s onAccept and onRevoke callback function.

For example, if you have two categories, named Analytics and Marketing, then the optionalCookies property of your configuration would be as follows:

optionalCookies: [
    {
        name: 'analytics',
        label: 'Google Analytics',
        description: 'Analytical cookies help us to improve our website by collecting and reporting information on its usage.',
        cookies: ['_ga', '_ga*', '_gid', '_gat', '__utma', '__utmt', '__utmb', '__utmc', '__utmz', '__utmv'],
        onAccept: function(){
            gtag('consent', 'update', {'analytics_storage': 'granted'});
        },
        onRevoke: function(){
            gtag('consent', 'update', {'analytics_storage': 'denied'});
        }
    },
    {
        name: 'marketing',
        label: 'Google Ad Platforms',
        description: 'We use marketing cookies to help us improve the relevancy of advertising campaigns you receive.',
        onAccept: function(){
            gtag('consent', 'update', {'ad_storage': 'granted'});
        },
        onRevoke: function(){
            gtag('consent', 'update', {'ad_storage': 'denied'});
        }
    }
],

The only detail to be mindful of is that the default value for ad_storage, analytics_storage, functionality_storage, personalization_storage and security_storage is "granted" and needs to be altered to "denied" to be GDPR compliant. For instance:

<script>
  // Include the following lines to define the gtag() function when
  // calling this code prior to your gtag.js or Tag Manager snippet
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}

  // Call the default command before gtag.js or Tag Manager runs to
  // adjust how the tags operate when they run. Modify the defaults
  // per your business requirements and prior consent granted/denied, e.g.:
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'analytics_storage': 'denied'
    'functionality_storage': 'denied'
    'personalization_storage': 'denied'
    'security_storage': 'denied'
  });
</script>
<!-- Load gtag.js or Tag Manager as normal, e.g.: -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID">
</script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'GA_MEASUREMENT_ID');
  gtag('config', 'AW-CONVERSION_ID');

  // Cookie Control, with optionalCookies described above
  CookieControl.load(...)
</script>
To view our GA4 & GTM Mode Template guide and download our template, click here.

Anchor point for Matomo Analytics Matomo Analytics

Matomo Analytics works in a similar manner as Google's Consent Mode. To make consent required, Matomo users will need to insert the following line at top of their existing Matomo Tracking code, on all their pages.

// require user tracking consent before processing data
_paq.push(['requireConsent']);

They can then configure a category for Matomo cookies with the onAccept and onRevoke properties providing or removing consent as per the visitors request.

Anchor point for Third Party Cookies Third Party Cookies

Cookie Control (and JavaScript generally) cannot delete third party cookies for browser security reasons, so if an optional category enables software that might set third party cookies as part of it's onAccept function, then it is important to add the property thirdPartyCookies as part of the Category definition.

This property expects a JavaScript Array listing the names of all third party vendors enabled by the Category, along with the relevant URL user's need to manually opt out of third party tracking.

thirdPartyCookies Expects a JavaScript Array with at least one JavaScript Object consisting of the properties name and url. Only applicable if the category will set third party cookies on acceptance.

This property is often required for categories that enable advertising, or functionality related to marketing and whose vendor does not offer any programmatic means to revoke consent.

For instance a category that enables AddThis functionality to share content via various social media channels might be:

const config = {
    // apiKey and product removed for brevity
    optionalCookies: [
        {
            name : 'socialmedia',
            label: 'Social Media Cookies',
            description: 'We use AddThis to facilitate the convenient sharing of our content with your friends across social media.',
            cookies: ['__atuvc', '__atuvs'],
            onAccept : function(){
                // Add addThis
                let script = document.createElement("script");
                script.src = "//s7.addthis.com/js/300/addthis_widget.js#pubid=XXXXX";
                document.body.appendChild(script);
                // End addThis
            },
            onRevoke: function(){
                // No programmatic opt out behaviour offered by vendor.
                // Requires user action
            },
            thirdPartyCookies: [
                {
                    name: "AddThis",
                    optOutLink: "https://www.addthis.com/privacy/opt-out"
                }
            ]
        }
    ]
}

Anchor point for Legitimate Interests Legitimate Interests

By default, all optional categories require an explicit affirmative user action before a record of consent is issued and the onAccept function is called.

Though it is possible to override this behaviour and enable certain optional categories prior to any user interaction if they may be justified on the grounds of legitimate interests - a more flexible lawful basis for processing data.

If you choose to rely on legitimate interest, then please be aware you are taking on extra responsibility for considering and protecting people’s rights and interests; and must include details of your legitimate interests in your privacy statement. This is unlikely to be the case for most analytics, advertising, or software offered by third party vendors. Read more from the ICO

To associate an optional category with this form of lawful basis, the Category definition needs to be extended with the property lawfulBasis.

lawfulBasis Can be either "consent" (default) or "legitimate interest".

If the latter, the UI will show the optional category as enabled, though no record of consent will exist. Users may still object and manually opt out.

For convenience, an optional category defined with this behaviour would be:

const config = {
    // apiKey and product removed for brevity
    optionalCookies: [
        {
            // name, description etc
            lawfulBasis: "legitimate interest"
        }
    ]
}

Anchor point for CCPA and Geolocation CCPA and Geolocation

By default, Cookie Control requires explicit, affirmative user action before any consent option is enabled. This stricter privacy requirement is due to GDPR and is enforced for those visiting your site from within the EU.

Though, for users accessing your site from outside the EU, it is possible to configure Cookie Control to run in a more flexible privacy model whereby consent options are enabled by default and users simply have the option to withdrawal their consent - so long as you make it convenient for users to find out and object to how you process their personal information.

This opt-out configuration satisfies the California Consumer Privacy Act (CCPA), so is identified within Cookie Control as ccpa mode.

Associating Cookie Control with CCPA behaviour

To enable support for CCPA, your configuration needs to be extended with the properties mode and ccpaConfig, which Cookie Control expects as a JavaScript Object containing the following properties:

descriptionThe text description that introduces your Personal Information Policy.
nameThe text label that best describes your Personal Information Policy and is included within the HTML link element.
urlThe URL where your Personal Information Policy may be publicly accessed. The HTML link element will try to open in a new tab, so it may point to a PDF document if you wish without closing the user's access to your site.
rejectButtonThe text used within the button for objecting to all available options.
updatedThe date that your Personal Information Policy was last issued, in the format of dd/mm/yyyy.

For convenience, you can extend your configuration with the following snippet - though please be aware that there are no default values for URL and updated and ccpaConfig will be considered invalid and dismissed if not supplied.

const config = {
    // apiKey and other settings...
    mode: 'ccpa',
    ccpaConfig : {
        description: 'This may include the website or our third-party tools '+
                     'processing personal data. In the case of your personal data, '+
                     'you may opt out of such activity by using the buttons below. '+
                     'To find out more about the categories of personal information '+
                     'collected and the purposes for which such information will be '+
                     'used, please refer to our',
        name: 'Personal Information Policy',
        url: 'https://www.civicuk.com/',
        updated: '25/05/2018',
        rejectButton: 'Do Not Sell or Share My Personal Information',
    }
}

With pro and multi-site licences, Cookie Control uses geolocation data to assess where each site visitor is accessing your site from. This means if your configuration includes the above, Cookie Control will consider this as the minimal level of privacy required and automatically upgrade to GDPR mode if the site visitor is determined to be accessing your site from within either the UK or European Union (EU).

Please note, if the user has a browser or extension that sends a Global Privacy Control (GPC) signal, Cookie Control will accept this as an intent not to be tracked and prevent automatic tracking from occurring. All categories will default to off even in CCPA mode, unless the user has overwritten this signal by explicitly opting in to a given category on your site.