GDPR Cookie Banner With Category Consent API – Neiki’s Cookie Banner

GDPR Cookie Banner With Category Consent API – Neiki’s Cookie Banner
Neiki’s Cookie Banner is a vanilla JavaScript library that adds an accessible, GDPR-compliant cookie consent banner to static HTML pages, WordPress themes, Laravel apps, and client-side framework projects.

It also stores versioned consent records in localStorage, re-prompts users after a consent version change, and keeps third-party scripts blocked until the visitor accepts the matching category.

Features:

  • 3 banner layouts: bottom/top bar, corner box, and centered modal, each with configurable position options.
  • 3built-in themes: light, dark, and automatic OS-preference detection via prefers-color-scheme.
  • Granular per-category consent toggles with permanent locking for necessary cookies that cannot be disabled.
  • Versioned consent records with ISO timestamps stored in localStorage, with automatic in-memory fallback when storage is unavailable.
  • Script blocking and automatic unlocking.
  • 6 lifecycle callbacks: accept, reject, ready, change, scripts unlock, and consent revocation.
  • Google Consent Mode v2 integration with automatic default-denied push and per-category grant/deny signals.
  • Web Component support for markup-first configuration.
  • Built-in English and Czech translations plus a registration API for custom languages.
  • Full ARIA compliance: dialog roles, focus trap on modal layout, role="switch" toggles with live aria-checked, and prefers-reduced-motion support.
  • Every user-supplied string runs through HTML escaping before DOM injection.
  • Completely free under the MIT license for personal and commercial use.

How to use it:

1. Download and load the neiki-cookie-banner.min.js script in the document.

<script src="dist/neiki-cookie-banner.min.js"></script>

2. If you prefer to manage the stylesheet separately (for preloading or inline overrides), load both files:

<link rel="stylesheet" href="dist/neiki-cookie-banner.css">
<script src="dist/neiki-cookie-banner.js"></script>

3. Create a basic cookie consent banner. Call init() after the script loads and the banner appears on first visit, stores the user’s choice, and stays hidden on return visits.

NeikiCookieBanner.init({
  layout: 'bar', // bar | box | modal
  position: 'bottom', // bottom, top, bottom-left, bottom-right, center
  theme: 'auto', // follows OS preference
  privacyPolicyUrl: '/privacy-policy',
  consentVersion: '1.0' // bump this string to re-prompt all users
});

4. Wrap third-party script (e.g. Google Analytics and Meta Pixel) loading inside onScriptsUnlock so the scripts load only when the user approves the matching category.

NeikiCookieBanner.init({
  layout: 'bar',
  position: 'bottom',
  theme: 'light',
  privacyPolicyUrl: '/privacy',
  consentVersion: '2.0',
  // Fires immediately on accept and on return visits when consent already exists
  onScriptsUnlock: function (categories) {
    // Load GA4 only if the user consented to analytics cookies
    if (categories.analytics) {
      var gaScript = document.createElement('script');
      gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
      gaScript.async = true;
      document.head.appendChild(gaScript);
      window.dataLayer = window.dataLayer || [];
      function gtag() { dataLayer.push(arguments); }
      gtag('js', new Date());
      gtag('config', 'G-XXXXXXXXXX');
    }
    // Load Meta Pixel only if the user consented to marketing cookies
    if (categories.marketing) {
      !function(f,b,e,v,n,t,s) {
        // Standard Meta Pixel initialization snippet
        if (f.fbq) return;
        n=f.fbq=function(){ n.callMethod ? n.callMethod.apply(n,arguments) : n.queue.push(arguments) };
        if (!f._fbq) f._fbq=n;
        n.push=n; n.loaded=!0; n.version='2.0';
        n.queue=[];
        t=b.createElement(e); t.async=!0;
        t.src=v; s=b.getElementsByTagName(e)[0];
        s.parentNode.insertBefore(t,s)
      }(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
      fbq('init', '000000000000000');
      fbq('track', 'PageView');
    }
  },
  onReject: function () {
    // User rejected all non-necessary cookies; no third-party scripts load
    console.log('User rejected optional cookies.');
  }
});

5. The library supports a declarative approach to script blocking. Add type="text/plain" and data-category to any script tag. The library converts it to a real executable script when the matching category is approved.

<!-- This script stays inert (type="text/plain") until analytics consent is given -->
<script type="text/plain" data-category="analytics" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" async></script>

<!-- Inline script with the same pattern -->
<script type="text/plain" data-category="analytics">
  window.dataLayer = window.dataLayer || [];
  function gtag() { dataLayer.push(arguments); }
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
</script>
<script>
  NeikiCookieBanner.init({
    privacyPolicyUrl: '/legal/privacy',
    consentVersion: '1.0'
    // No callback needed; the library auto-unlocks scripts on consent
  });
</script>

6. Custom Categories and Modal Layout:

NeikiCookieBanner.init({

  layout: 'modal',            // Modal shows the preferences panel immediately
  theme: 'dark',
  privacyPolicyUrl: '/privacy',
  consentVersion: '3.0',
  lockScroll: true,           // Prevents scrolling while the banner is open

  categories: {
    necessary: {
      label: 'Required',
      description: 'Core site functions. These cannot be turned off.',
      locked: true            // User cannot toggle this off
    },
    analytics: {
      label: 'Analytics',
      description: 'Helps us understand page performance and traffic sources.',
      enabled: false          // Off by default; user must opt in
    },
    experiments: {
      label: 'A/B Tests',
      description: 'Tracks which feature variants you see during testing.',
      enabled: false
    }
  },

  onScriptsUnlock: function (categories) {
    if (categories.analytics) {
      // Load your analytics script here
    }
    if (categories.experiments) {
      // Initialize your A/B testing framework here
    }
  }
});

7. Prefer HTML configuration over JavaScript? Use the <neiki-cookie-banner> custom element. Place it anywhere in <body> before the closing tag.

<neiki-cookie-banner
  data-layout="modal"
  data-theme="auto"
  data-position="center"
  data-consent-version="1.0"
  data-privacy-policy-url="/privacy"
  data-lock-scroll="true"
  data-close-on-overlay-click="true">
</neiki-cookie-banner>

8. Re-open the Banner from a Footer Link. Add data-neiki-show-prefs to any anchor or button and the library re-opens the banner on click.

<a href="#" data-neiki-show-prefs>Manage Cookie Preferences</a>

9. If your site uses Google Tag Manager with Consent Mode, set googleConsentMode: true. The library pushes default-denied signals immediately on init() (before any GTM tags fire) and updates them when the user makes a choice.

<!-- GTM snippet must load before or alongside the cookie banner -->
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
NeikiCookieBanner.init({
  privacyPolicyUrl: '/privacy',
  consentVersion: '1.0',
  googleConsentMode: true,    // Pushes gtag('consent','default', ...) immediately
  // No manual gtag() calls needed; the library handles all consent signals
});

10. Override the default banner’s styles with CSS custom properties on the .neiki-cb root selector.

.neiki-cb {
  --neiki-cb-accent: #0f62fe;     /* Primary button background */
  --neiki-cb-radius: 8px;         /* Border radius for the container */
}

11. All configuration options.

  • layout ('bar' | 'box' | 'modal'): Sets the visual layout. Bar spans the full width, Box appears in a corner, Modal centers with a backdrop.
  • position (string): Positions the banner. Accepts top, bottom, bottom-left, bottom-right, and center.
  • theme ('light' | 'dark' | 'auto'): Sets the color theme. auto reads prefers-color-scheme from the OS.
  • title (string): Overrides the banner heading. Defaults to 'We use cookies'.
  • description (string): Overrides the banner body text. Defaults to built-in copy.
  • privacyPolicyUrl (string): Appends a privacy policy link to the description when set.
  • privacyPolicyText (string): Sets the link text for the privacy policy URL. Defaults to 'Privacy Policy'.
  • acceptAllText (string): Sets the primary button label. Defaults to 'Accept All'.
  • rejectAllText (string): Sets the reject button label. Pass an empty string to hide this button.
  • customizeText (string): Sets the customize/preferences button label. Defaults to 'Customize'.
  • savePreferencesText (string): Sets the label for the save button inside the preferences panel.
  • categories (object): Defines the consent categories. See the Custom Categories example above.
  • showAfterMs (number): Sets the delay in milliseconds before the banner appears on first visit. Defaults to 300.
  • closeOnOverlayClick (boolean): Closes the modal layout when the user clicks the backdrop. Defaults to false.
  • lockScroll (boolean): Adds overflow: hidden to the body while the banner is open. Defaults to false.
  • animationIn ('slide' | 'none'): Sets the entry animation. Defaults to 'slide'.
  • consentVersion (string): A version string stored alongside consent. Bump it to force a re-prompt for all users. Defaults to '1.0'.
  • googleConsentMode (boolean): Activates Google Consent Mode v2 integration. Defaults to false.
  • language (string): Sets the i18n language. Defaults to 'en'. Use 'cs' for Czech or register a custom translation with addTranslation().
  • zIndex (number): Sets the stacking order of the banner element. Defaults to 9999.
  • onAccept (function): Fires when the user accepts all cookies or saves custom preferences.
  • onReject (function): Fires when the user clicks Reject All.
  • onReady (function): Fires on page load when valid stored consent already exists. Receives the stored consent object.
  • onChange (function): Fires on every consent state change.
  • onScriptsUnlock (function): Fires with the approved categories object. Use this to load third-party scripts.
  • onRevoke (function): Fires when revoke() is called programmatically.

12. API methods:

// Initialize the banner and show it if no valid consent is stored
NeikiCookieBanner.init({ layout: 'bar', consentVersion: '1.0' });

// Re-open the banner (re-renders the panel and resets toggle states)
NeikiCookieBanner.show();

// Close the banner programmatically
NeikiCookieBanner.hide();

// Returns the stored consent object or null if no consent has been recorded
// Shape: { version: '1.0', timestamp: '2026-05-01T10:00:00.000Z', categories: { ... } }
const consent = NeikiCookieBanner.getConsent();

// Returns true if any consent record exists (regardless of what was accepted)
const hasConsented = NeikiCookieBanner.hasConsented();

// Returns true if the user approved a specific category
const analyticsAllowed = NeikiCookieBanner.isAllowed('analytics');

// Clears stored consent and re-shows the banner
NeikiCookieBanner.reset();

// Clears stored consent, revokes GCM signals, re-locks unlocked scripts, fires onRevoke,
// and optionally re-opens the banner (pass false to suppress re-open)
NeikiCookieBanner.revoke();         // Re-opens banner
NeikiCookieBanner.revoke(false);    // Revokes consent silently

// Manually unlock blocked scripts for a specific category
NeikiCookieBanner.unlockScripts('analytics');

// Register a custom translation table
NeikiCookieBanner.addTranslation('de', {
  title: 'Wir verwenden Cookies',
  acceptAllText: 'Alle akzeptieren',
  rejectAllText: 'Alle ablehnen',
  // ... rest of the translation keys
});

// Retrieve a registered translation table
const table = NeikiCookieBanner.getTranslation('de');

Alternatives:

FAQs:

Q: The banner re-appears on every page load. What’s wrong?
A: Check your init() call and verify the version string matches what’s in localStorage under the key neiki_cookie_consent.

Q: How do I re-prompt users after a privacy policy update?
A: Bump the consentVersion string in your init() call. The library compares the stored version against the configured one on every page load. A mismatch triggers a full re-prompt. Previous consent records are overwritten on the next user action.

Q: How do I add a translation for a language not included?
A: Call NeikiCookieBanner.addTranslation('fr', { title: '...', acceptAllText: '...', ... }) before calling init(). Then pass language: 'fr' in your init() config. The library falls back to English for any missing key in the custom table.

The post GDPR Cookie Banner With Category Consent API – Neiki’s Cookie Banner appeared first on CSS Script.


Discover more from RSS Feeds Cloud

Subscribe to get the latest posts sent to your email.

Discover more from RSS Feeds Cloud

Subscribe now to keep reading and get access to the full archive.

Continue reading