TapCub is live — analytics, insights and live chat, free on one platform

Free tool Cookie banner generator with a live preview

Pick a layout, colours, wording and the cookie categories you actually use. You get one block of plain HTML, CSS and JavaScript with no library behind it, a working preview next to it, and an optional Google Consent Mode v2 snippet that is wired to the banner.

  • Live preview
  • No dependencies
  • Runs locally
Generator

Design the banner, then copy one block of code

The preview runs the exact code below. Try Reject, Customize and the “Cookie settings” button; the log under the fake page shows what the banner sends to Google Consent Mode.

A bar is the least intrusive. A centred dialog blocks the page until the visitor chooses, so it must offer Reject just as clearly.

Text colours are fixed for contrast; only the button colour follows your brand.

Used for both Accept and Reject. The text colour on it is picked automatically for the higher contrast.

Button labels, category names and the default message.

Plain text. Say what the optional cookies are for; the policy link is added after it.

A path such as /cookies or a full https:// address. Need one? Try the cookie policy generator.

Both are first-party and readable only by your site.

1–730. Changing the version below also asks everyone again.

Bump it when you add a new purpose or vendor.

Live preview

A sandboxed frame with no access to storage, so choices live in memory and reset whenever you change a setting. Nothing is written on tapcub.com.

What this banner does

  • DoneButton text contrast is 5.17:1, above the WCAG 4.5:1 minimum for normal text.
  • DoneReject all and Accept all share one style and sit side by side on the first layer.
  • DoneOptional categories start unticked. Saved choices are shown as they were saved.
  • Donerole="dialog" with a labelled heading and message; every control is a real button or checkbox, works by keyboard and shows a focus ring; Escape closes the settings view.
  • DoneA floating “Cookie settings” button appears after a choice, and any element with data-cc-open reopens the settings.
  • NoteThe choice is saved in localStorage under “cookie_consent” and the banner asks again after 180 days or when the version changes.
  • NoteVisitors whose browser sends Global Privacy Control get marketing off by default, with a visible note. California’s regulations (§7025) require businesses that sell or share personal information to treat that signal as an opt-out.
  • NoteConsent Mode: paste the head snippet above the Google tag. Defaults are denied; the banner sends an update after every choice and on every later page view.
  • NoteTo block a script until consent, change its type to text/plain and add data-cc-category="analytics" (or marketing / preferences).
  • NoteIn the preview, links do nothing and choices are kept in memory only.
Consent Mode snippet: top of <head>
<!-- Google Consent Mode v2: paste this at the very top of <head>, before the Google tag or the Tag Manager container. -->
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  // Defaults: everything optional is denied until the visitor chooses.
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'denied',
    'functionality_storage': 'denied',
    'personalization_storage': 'denied',
    'security_storage': 'granted',
    'wait_for_update': 500
  });
  // Returning visitors: re-apply the saved choice before any Google tag runs.
  (function () {
    var raw = null, r = null;
    try { raw = localStorage.getItem('cookie_consent'); } catch (e) {}
    if (!raw) try { var m = document.cookie.match(/(?:^|; )cookie_consent=([^;]*)/); if (m) raw = decodeURIComponent(m[1]); } catch (e) {}
    try { r = JSON.parse(raw); } catch (e) {}
    if (!r || !r.c || !(r.e > Date.now()) || r.v !== '1') return;
    var c = r.c;
    gtag('consent', 'update', {
      'ad_storage': c.marketing ? 'granted' : 'denied',
      'ad_user_data': c.marketing ? 'granted' : 'denied',
      'ad_personalization': c.marketing ? 'granted' : 'denied',
      'analytics_storage': c.analytics ? 'granted' : 'denied',
      'functionality_storage': c.preferences ? 'granted' : 'denied',
      'personalization_storage': c.preferences ? 'granted' : 'denied'
    });
  })();
</script>

Paste it before the Google tag or Tag Manager container. For region-specific defaults, url_passthrough or GTM, use the Consent Mode generator; it reads the same saved choice.

Banner code: before </body>
<!-- Cookie banner · generated with tapcub.com/tools/cookie-banner-generator · no dependencies. A template, not legal advice: review it before use. -->
<!-- Paste this block just before </body> on every page. -->
<!-- Optional scripts: write them as <script type="text/plain" data-cc-category="analytics" src="…"></script>; they run only after consent. -->
<!-- API: CookieConsent.get() · CookieConsent.open() · CookieConsent.reset() · CookieConsent.on(fn); any element with data-cc-open reopens the settings. -->
<style>
#cc-banner{--cc-bg:#ffffff;--cc-fg:#111827;--cc-muted:#4b5563;--cc-border:#d1d5db;--cc-panel:#f9fafb;--cc-accent:#2563eb;--cc-on-accent:#ffffff;--cc-link:#2563eb;
  position:fixed;z-index:2147483000;left:0;right:0;bottom:0;border-width:1px 0 0;box-sizing:border-box;padding:20px;background:var(--cc-bg);color:var(--cc-fg);border:1px solid var(--cc-border);
  box-shadow:0 8px 32px rgba(0,0,0,.18);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif}
#cc-banner[hidden],#cc-reopen[hidden],#cc-backdrop[hidden],#cc-banner [hidden]{display:none!important}
#cc-banner *{box-sizing:border-box}
#cc-banner:focus{outline:none}
#cc-banner .cc-inner{max-width:1080px;margin:0 auto;display:grid;gap:14px}
#cc-banner .cc-title{margin:0;font-size:17px;font-weight:700;line-height:1.3;color:var(--cc-fg)}
#cc-banner .cc-text{margin:0;color:var(--cc-muted)}
#cc-banner a{color:var(--cc-link);text-decoration:underline;text-underline-offset:2px}
#cc-banner .cc-cats{display:grid;gap:8px;margin:0;padding:0;border:0}
#cc-banner .cc-cat{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border:1px solid var(--cc-border);border-radius:8px;background:var(--cc-panel)}
#cc-banner .cc-cat input{width:20px;height:20px;margin:2px 0 0;flex:none;accent-color:var(--cc-accent)}
#cc-banner .cc-cat b{display:block;color:var(--cc-fg)}
#cc-banner .cc-cat span{display:block;font-size:13px;color:var(--cc-muted)}
#cc-banner .cc-note{margin:0;font-size:13px;color:var(--cc-muted)}
#cc-banner .cc-actions{display:flex;flex-wrap:wrap;gap:8px}
#cc-banner .cc-btn{flex:1 1 auto;min-height:44px;padding:10px 18px;border:2px solid var(--cc-accent);border-radius:8px;background:var(--cc-accent);color:var(--cc-on-accent);font:inherit;font-weight:600;cursor:pointer}
#cc-banner .cc-btn-alt{background:transparent;color:var(--cc-fg);border-color:var(--cc-border)}
#cc-banner .cc-btn:focus-visible,#cc-reopen:focus-visible,#cc-banner a:focus-visible,#cc-banner input:focus-visible{outline:3px solid var(--cc-link);outline-offset:2px}
#cc-backdrop{position:fixed;inset:0;z-index:2147482999;background:rgba(17,24,39,.55)}
#cc-reopen{position:fixed;z-index:2147482998;left:16px;bottom:16px;min-height:40px;padding:8px 14px;border:1px solid #d1d5db;border-radius:999px;background:#ffffff;color:#111827;font:600 13px/1.2 system-ui,sans-serif;cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.15)}
#cc-reopen:focus-visible{outline:3px solid #2563eb;outline-offset:2px}
@media (min-width:720px){#cc-banner.cc-bar .cc-inner{grid-template-columns:1fr auto;align-items:center}#cc-banner.cc-bar .cc-prefs,#cc-banner.cc-bar .cc-actions-wide{grid-column:1/-1}#cc-banner .cc-btn{flex:0 1 auto}}
</style>
<div id="cc-banner" class="cc-bar" role="dialog" aria-modal="false" aria-labelledby="cc-title" aria-describedby="cc-desc" tabindex="-1" lang="en" hidden>
  <div class="cc-inner">
    <div>
      <h2 class="cc-title" id="cc-title">We use cookies</h2>
      <p class="cc-text" id="cc-desc">We use strictly necessary cookies to run this site. With your consent we also use cookies for analytics and marketing. You can change your choice at any time in “Cookie settings”. <a href="/cookies">Cookie policy</a></p>
    </div>
    <div class="cc-prefs" id="cc-prefs" hidden>
      <fieldset class="cc-cats">
      <legend class="cc-note">Cookie categories</legend>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-necessary" checked disabled><label for="cc-cat-necessary"><b>Strictly necessary</b><span>Always on. Needed for the site to work, for example security and remembering this choice.</span></label></div>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-analytics" data-cc-cat="analytics"><label for="cc-cat-analytics"><b>Analytics</b><span>Helps us understand how the site is used, for example which pages are visited.</span></label></div>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-marketing" data-cc-cat="marketing"><label for="cc-cat-marketing"><b>Marketing</b><span>Used to measure ad campaigns and show more relevant ads.</span></label></div>
      </fieldset>
      <p class="cc-note" data-cc-gpc hidden>Opt-out preference signal honored: your browser sent Global Privacy Control, so marketing stays off unless you switch it on here.</p>
    </div>
    <div class="cc-actions">
      <button type="button" class="cc-btn" data-cc="reject">Reject all</button>
      <button type="button" class="cc-btn" data-cc="accept">Accept all</button>
      <button type="button" class="cc-btn cc-btn-alt" data-cc="customize" aria-expanded="false" aria-controls="cc-prefs">Customize</button>
      <button type="button" class="cc-btn cc-btn-alt" data-cc="save" hidden>Save choices</button>
    </div>
  </div>
</div>
<button type="button" id="cc-reopen" data-cc-open hidden>Cookie settings</button>
<script>
(function () {
  'use strict';
  // Settings: change them here, not further down.
  var KEY = "cookie_consent"; // localStorage key / cookie name
  var VERSION = "1"; // change it when your purposes change: everyone is asked again
  var DAYS = 180; // ask again after this many days
  var STORAGE = "local"; // 'local' = localStorage, 'cookie' = first-party cookie
  var CATEGORIES = ["analytics","marketing"];
  var CONSENT_MODE = true; // send gtag('consent', 'update', …)
  var TAPCUB = false; // tell the TapCub script (identification mode only)
  var HONOR_GPC = true; // Global Privacy Control keeps marketing off
  var MODAL = false;

  var root = document.getElementById('cc-banner');
  if (!root) return;
  var backdrop = document.getElementById('cc-backdrop');
  var reopenBtn = document.getElementById('cc-reopen');
  var prefs = document.getElementById('cc-prefs');
  var memory = null, lastFocus = null, firstRun = false;
  var gpc = HONOR_GPC && navigator.globalPrivacyControl === true && CATEGORIES.indexOf('marketing') > -1;
  // keep the banner first in the tab order
  document.body.insertBefore(root, document.body.firstChild);
  if (backdrop) document.body.insertBefore(backdrop, root);

  // ---- storage: falls back to memory when the browser blocks it
  function read() {
    var raw = null;
    try {
      if (STORAGE === 'local') raw = localStorage.getItem(KEY);
      else { var m = document.cookie.match(new RegExp('(?:^|; )' + KEY + '=([^;]*)')); if (m) raw = decodeURIComponent(m[1]); }
    } catch (e) {}
    if (!raw) return memory;
    try { return JSON.parse(raw); } catch (e) { return null; }
  }
  function write(rec) {
    memory = rec;
    var s = rec ? JSON.stringify(rec) : null;
    try {
      if (STORAGE === 'local') { if (s) localStorage.setItem(KEY, s); else localStorage.removeItem(KEY); }
      else document.cookie = KEY + '=' + (s ? encodeURIComponent(s) : '') + '; Max-Age=' + (s ? DAYS * 86400 : 0) + '; Path=/; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : '');
    } catch (e) {}
  }
  function valid(r) { return !!(r && r.c && r.v === VERSION && r.e > Date.now()); }

  // ---- what happens after a choice
  function apply(c, userAction) {
    if (CONSENT_MODE) {
      window.dataLayer = window.dataLayer || [];
      if (typeof window.gtag !== 'function') window.gtag = function () { window.dataLayer.push(arguments); };
      window.gtag('consent', 'update', {
        'ad_storage': c.marketing ? 'granted' : 'denied',
        'ad_user_data': c.marketing ? 'granted' : 'denied',
        'ad_personalization': c.marketing ? 'granted' : 'denied',
        'analytics_storage': c.analytics ? 'granted' : 'denied',
        'functionality_storage': c.preferences ? 'granted' : 'denied',
        'personalization_storage': c.preferences ? 'granted' : 'denied'
      });
      window.dataLayer.push({ event: 'cookie_consent_update' });
    }
    // run <script type="text/plain" data-cc-category="…"> blocks that are now allowed
    var blocked = document.querySelectorAll('script[type="text/plain"][data-cc-category]');
    for (var i = 0; i < blocked.length; i++) {
      var old = blocked[i];
      if (!c[old.getAttribute('data-cc-category')]) continue;
      var s = document.createElement('script');
      for (var k = 0; k < old.attributes.length; k++) {
        var a = old.attributes[k];
        if (a.name !== 'type' && a.name !== 'data-cc-category') s.setAttribute(a.name, a.value);
      }
      s.text = old.text;
      old.parentNode.replaceChild(s, old);
    }
    var ev;
    try { ev = new CustomEvent('cookieconsent', { detail: c }); } catch (e) { ev = document.createEvent('CustomEvent'); ev.initCustomEvent('cookieconsent', false, false, c); }
    document.dispatchEvent(ev);
  }

  function choice(all) {
    var c = {};
    for (var i = 0; i < CATEGORIES.length; i++) {
      var k = CATEGORIES[i];
      if (all === null) { var box = document.getElementById('cc-cat-' + k); c[k] = !!(box && box.checked); }
      else c[k] = all && !(gpc && k === 'marketing');
    }
    return c;
  }
  function save(c) {
    var now = Date.now();
    write({ v: VERSION, t: now, e: now + DAYS * 864e5, c: c });
    apply(c, true);
    close();
  }

  // ---- open / close and focus handling
  function focusables() { return root.querySelectorAll('button:not([hidden]):not([disabled]), a[href], input:not([disabled])'); }
  function showPrefs(on) {
    if (!prefs) return;
    prefs.hidden = !on;
    var cus = root.querySelector('[data-cc="customize"]');
    var sav = root.querySelector('[data-cc="save"]');
    if (cus) { cus.hidden = on; cus.setAttribute('aria-expanded', String(on)); }
    if (sav) sav.hidden = !on;
    if (on) { var first = prefs.querySelector('input:not([disabled])'); if (first) first.focus(); }
  }
  function open(withPrefs, byUser) {
    var r = read(), saved = valid(r) ? r.c : {};
    for (var i = 0; i < CATEGORIES.length; i++) {
      var box = document.getElementById('cc-cat-' + CATEGORIES[i]);
      if (box) box.checked = saved[CATEGORIES[i]] === true; // nothing is pre-ticked
    }
    var g = root.querySelector('[data-cc-gpc]'); if (g) g.hidden = !gpc;
    lastFocus = byUser ? document.activeElement : null;
    root.hidden = false;
    if (backdrop) backdrop.hidden = false;
    if (reopenBtn) reopenBtn.hidden = true;
    showPrefs(!!withPrefs);
    if (!MODAL) document.documentElement.style.scrollPaddingBottom = root.offsetHeight + 'px';
    if ((MODAL || byUser) && !withPrefs) root.focus();
  }
  function close() {
    root.hidden = true;
    firstRun = false;
    if (backdrop) backdrop.hidden = true;
    document.documentElement.style.scrollPaddingBottom = '';
    if (reopenBtn) reopenBtn.hidden = false;
    var to = lastFocus && document.contains(lastFocus) ? lastFocus : reopenBtn;
    if (to && to.focus) to.focus();
  }

  root.addEventListener('click', function (e) {
    var b = e.target.closest ? e.target.closest('[data-cc]') : null;
    if (!b) return;
    var act = b.getAttribute('data-cc');
    if (act === 'accept') save(choice(true));
    else if (act === 'reject') save(choice(false));
    else if (act === 'save') save(choice(null));
    else if (act === 'customize') showPrefs(true);
  });
  root.addEventListener('keydown', function (e) {
    if (e.key === 'Escape') {
      if (prefs && !prefs.hidden) { showPrefs(false); var cus = root.querySelector('[data-cc="customize"]'); if (cus) cus.focus(); }
      else if (!firstRun) close();
      return;
    }
    if (e.key !== 'Tab' || !MODAL) return;
    var f = focusables(); if (!f.length) return;
    var first = f[0], last = f[f.length - 1];
    if (e.shiftKey && (document.activeElement === first || document.activeElement === root)) { e.preventDefault(); last.focus(); }
    else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
  });
  document.addEventListener('click', function (e) {
    var t = e.target.closest ? e.target.closest('[data-cc-open]') : null;
    if (t) { e.preventDefault(); open(true, true); }
  });

  window.CookieConsent = {
    get: function () { var r = read(); return valid(r) ? r.c : null; },
    open: function () { open(true, true); },
    reset: function () { write(null); firstRun = true; open(false, false); },
    on: function (fn) { document.addEventListener('cookieconsent', function (e) { fn(e.detail); }); }
  };

  var rec = read();
  if (valid(rec)) { apply(rec.c, false); if (reopenBtn) reopenBtn.hidden = false; }
  else { if (rec) write(null); firstRun = true; open(false, false); }
})();
</script>

One block on every page. Settings are at the top of the script.

Runs entirely in your browser. Nothing you type here is uploaded or stored by TapCub.

Share these inputs: the values go after the # in the link, so they never reach a server.

Version v1.0Updated Update history

Read the result

What the generated code is made of

One block for the banner, plus an optional snippet for Google tags. Nothing loads from a third-party server.

Banner code

A <style> block scoped to #cc-banner, the banner markup and one script. The settings (storage key, version, days, categories) sit at the top of the script so you can change them without touching the logic.

The first layer

Heading, message, policy link, then Reject all, Accept all and Customize. Reject and Accept use the same colour, size and position weight. Customize opens a list of categories with the strictly necessary one locked on.

Consent Mode snippet

Goes at the very top of <head>. It sets every optional consent type to denied with wait_for_update: 500, then re-applies a saved choice so Google tags see it before they run.

The checks

Contrast of the button text, a missing policy link, a long re-ask period and what each option changes. They describe this code; whether your site needs a banner, and which categories, depends on what it actually loads.

The banner only records a choice. It cannot stop a script it does not control: tags that are hard-coded in your page still run unless you gate them with type="text/plain" or load them through Google Consent Mode or Tag Manager.

How to use it

Four steps

About ten minutes, most of it spent on step one.

  1. 1

    List what your site stores

    Open your site in a private window, then the browser’s developer tools (Application or Storage tab). Note every cookie and localStorage item and which tool set it. Only tick the categories you found.

  2. 2

    Design and word the banner

    Choose a layout and colour, check the contrast note, and write a message that says what each optional category is for. Link your cookie policy.

  3. 3

    Paste the code

    Banner block before </body> on every page; Consent Mode snippet first thing in <head>. Change optional scripts to type="text/plain" with data-cc-category.

  4. 4

    Test the three paths

    In a private window: Reject, then check the storage tab is still clean; Accept; then reopen and withdraw. With Consent Mode, Google Tag Assistant shows the default and update states.

The rules behind the defaults

Why the banner works the way it does

Each default maps to a published rule or guidance. Where the rules come from:

  • Storing or reading anything on a device needs consent unless it is strictly necessary for a service the user asked for: ePrivacy Directive Article 5(3). It covers localStorage as well as cookies
  • Consent must be a clear affirmative act; pre-ticked boxes do not count: CJEU judgment C-673/17 Planet49 (1 October 2019) and GDPR Recital 32
  • Withdrawing consent must be as easy as giving it: GDPR Article 7(3), hence the reopen button and data-cc-open
  • Refusing should be as easy as accepting: CNIL guidelines and recommendation 2020-091 / 2020-092 (September 2020); the EDPB cookie banner taskforce report (January 2023) lists missing reject buttons, pre-ticked boxes and deceptive button colours or contrast among the practices it examined
  • In California, a business that sells or shares personal information must treat a Global Privacy Control signal as a valid opt-out: Cal. Code Regs. tit. 11, §7025(b)
Banner behaviour and where it comes from
BehaviourIn the generated codeSource
Reject as easy as AcceptSame button style, same layer, side by sideCNIL 2020-092; EDPB taskforce report
No pre-ticked categoriesOptional checkboxes start untickedCJEU C-673/17 Planet49
Nothing optional before consentGated scripts stay text/plain; Consent Mode defaults are deniedePrivacy Art. 5(3)
Easy withdrawalFloating button, data-cc-open, CookieConsent.open()GDPR Art. 7(3)
Readable and operable4.5:1 button text, keyboard focus, labelled dialogWCAG 2.2 SC 1.4.3, 2.1.1, 2.4.7
Opt-out signalGPC keeps marketing off, with a noteCal. Code Regs. tit. 11, §7025
Worked example

Three sites, three banners

What to tick depends on what each site loads. All three use the same generator.

Example banner set-ups
SiteWhat it loadsSettingsResult
Blog with Google AnalyticsGA4 onlyAnalytics ticked; Consent Mode onReject / Accept / Customize; GA4 runs in denied mode until Accept
Shop with ad pixelsGA4, Google Ads, Meta PixelAnalytics + Marketing; Consent Mode on; pixel gated with data-cc-category="marketing"Pixel loads only after consent; Google tags get the update
SaaS site on TapCub onlyTapCub default mode + chatNo optional categoryA short notice with one OK button, or no banner at all after legal review: neither script stores anything before the visitor acts

The third row reflects TapCub’s own set-up: the analytics script writes no cookies or storage in its default mode, and the chat widget writes one session token only after a visitor starts a conversation (see our cookie page).

Common mistakes

Where cookie banners go wrong

Most problems are not in the banner itself but in what runs around it.

Tags fire before the choice

A banner on top of a page whose GA or pixel code already ran records consent too late. Gate those scripts or use Consent Mode defaults.

Reject hidden on a second layer

An “Accept” button next to a small “Settings” link is the pattern regulators examined first. Put Reject on the first layer with the same weight.

No way back

If the banner never reappears, visitors cannot withdraw consent. Keep the floating button or a footer link with data-cc-open.

Categories that do not match reality

Listing “Marketing” on a site with no ad tags, or calling an analytics cookie “necessary”, makes the banner misleading. Audit first.

FAQ

Cookie banner questions

Still have a question?

Chat with the team behind TapCub — we usually reply within a few hours on business days.

Do I need a cookie banner?

In the EU and UK you need consent before storing or reading anything on a visitor’s device that is not strictly necessary for a service they asked for (ePrivacy Article 5(3) and national laws). If your site only uses strictly necessary storage, such as a login session or a shopping cart, a banner is not required for those items. Analytics and ad cookies usually do need consent. This page is a template, not legal advice; ask a lawyer about your site.

Is a cookie banner enough to be GDPR compliant?

No. The banner records a choice; you also need scripts that respect it, a policy that explains each purpose, records of consent where you rely on it, and a way to withdraw. The generated code covers the banner part and can wire the choice to Google Consent Mode and gated scripts.

Does the Reject button have to look the same as Accept?

The CNIL’s 2020 recommendation proposes a “Reject all” button with the same look as “Accept all”, and the EDPB’s cookie banner taskforce examined missing reject buttons and deceptive colours or contrast. The safe design, used here, gives both buttons the same style on the first layer.

How long should a cookie consent choice be stored?

There is no single number in the GDPR. Pick a period you can justify in your cookie policy, ask again when it ends, and say how long the choice is kept. Set the days field, and bump the version whenever you add a purpose so everyone is asked again.

How do I block Google Analytics until consent?

Two ways. With Consent Mode on, paste the head snippet above your Google tag: GA4 then starts with analytics_storage denied and updates when the visitor accepts. Or gate the tag entirely: change <script> to <script type="text/plain" data-cc-category="analytics"> and the banner runs it only after consent.

Can visitors change their mind later?

Yes. After a choice, a small “Cookie settings” button stays in the corner, and any element with data-cc-open, or a call to CookieConsent.open(), reopens the categories with the saved choice shown.

Does it work with WordPress, Shopify or a single-page app?

Yes, it is plain HTML and JavaScript. On WordPress or Shopify, paste the banner block into the theme footer and the Consent Mode snippet into the theme head. In a single-page app, load it once in the page shell: the banner stays in the document across route changes. It is not meant for native mobile apps, which use their platform’s own consent screens.

Does the preview set cookies on tapcub.com?

No. The preview runs in a sandboxed frame that has no access to cookies or localStorage, so the generated code falls back to memory. Your choices disappear when you change a setting or leave the page.

Changelog

Update history

New features, fixes and data refreshes for this tool, newest first.

  1. · v1.0

    LaunchedFirst release.

Data this tool relies on

  • Google Consent Mode documentation — checked 2026-11-10, reviewed every 6 months (next review by 2027-05-10)

Missing something or found an error? Tell us — requests that come up often go into the next update. All tool updates

See your data clearly. Find your growth.

Every click, backed by data. Install one line of code and see your first numbers in a minute.

No credit card · Free plans for analytics and chat · Cookieless analytics