Docs

Three steps. The first one matters most.

There is no plugin to install. You add one tag, tell it about anything it would not already recognise, and that is the whole job.

  1. Step 1: Put the tag first in <head>

    html
    <script src="https://solidcookie.com/c/YOURSITEKEY.js"></script>

    Being first matters. Auto-blocking works by watching the page as the browser builds it. A tracker the browser reached before our tag has already run, and a banner that cannot block what it claims to control is worse than no banner — because it looks like it works.

    On this website the framework renders the tag at the very top of <body> rather than in <head>. That works here because nothing precedes it; on your site, head is the place.

    WordPress, no plugin

    Either add this to the child theme’s functions.php:

    functions.php
    add_action('wp_head', function () {
      echo '<script src="https://solidcookie.com/c/YOURSITEKEY.js"></script>' . "\n";
    }, 0); // priority 0 — before anything else in head

    or, better, drop this file in wp-content/mu-plugins/solidcookie.php. An mu-plugin survives a theme change and cannot be switched off by accident.

    wp-content/mu-plugins/solidcookie.php
    <?php
    /* Plugin Name: SolidCookie */
    add_action('wp_head', function () {
      echo '<script src="https://solidcookie.com/c/YOURSITEKEY.js"></script>' . "\n";
    }, 0);
  2. Step 2: Tag anything that should wait

    Common trackers are recognised and held automatically — Google Tag Manager and Analytics, Hotjar, Clarity, the Meta pixel, LinkedIn, TikTok, Pinterest, HubSpot, and the YouTube, Vimeo and Google Maps embeds. That covers most small sites.

    The reliable mechanism, and the one to use for anything else, is to tag the script yourself:

    html
    <script type="text/plain" data-consent="analytics"
            src="https://example.com/tracker.js"></script>
    
    <script type="text/plain" data-consent="marketing">
      // inline code is held the same way
      fbq('init', '1234567890');
    </script>

    A script with a non-executable type is never fetched or run by the browser, with or without our JavaScript. We restore it once the visitor allows that category. The categories are:

    data-consentCategoryWhat goes in it
    necessaryStrictly necessaryalways onAlways on — the site cannot work without them. They cover page navigation, security, and remembering the cookie choice you make here.
    preferencesPreferencesRemember choices such as your language or region, so the site is presented the way you prefer.
    analyticsAnalyticsAnonymous visit counts, so the site owner can see which pages are read and where people run into trouble. They do not identify you.
    marketingMarketingAds and remarketing tags, which follow you across websites so advertising can be tailored to you and measured.

    Embeds and iframes

    An iframe sets cookies the moment it loads, and on a brochure site the map on the contact page is often the only thing that needs consent. Known embed hosts are held automatically by parking the src; to hold any other iframe, put its address in data-gdprs-src instead:

    html
    <iframe data-consent="marketing"
            data-gdprs-src="https://www.youtube.com/embed/VIDEOID"
            width="560" height="315"></iframe>

    reCAPTCHA is never blocked. It lives on the same host as Google Maps, and holding it would break the contact form on every site that uses it — an outage, not a leak.

  3. Step 3: Give visitors a way back

    Anyone must be able to change their answer later. Put a link or button with data-gdprs-show in your footer and it reopens the banner showing their current choices. If you provide none, one is added for you — to your footer, or the end of the page if there is no footer — but it looks better when it is yours. The Cookie consent link at the bottom of this page is one.

    html
    <a href="#" data-gdprs-show>Cookie consent</a>

    To show the cookie table on your own page, add an empty <div data-gdprs-declaration></div> and it is filled from the live declaration. Or link to the hosted pages, which exist from the moment a site is set up:

    hosted policy pages
    https://solidcookie.com/p/YOURSITEKEY/cookie-policy
    https://solidcookie.com/p/YOURSITEKEY/privacy-policy

The JavaScript API

js
// Open the banner, straight to the category panel
window.gdprs.show(true)

// What this visitor has agreed to, or null before they answer
window.gdprs.getConsent()
// → { id, ver, hash, action, choices: { analytics: false, … }, ts }

// Run something once consent is decided (fires on every change too)
window.gdprs.on('consent', (c) => {
  if (c.choices.analytics) startAnalytics()
})

// Withdraw everything. Reloads the page — see below.
window.gdprs.withdraw()

Everything hangs off window.gdprs, which exists once the banner has loaded its configuration. A gdprs:consent event is also dispatched on window with the same record as its detail.

Withdrawing reloads the page. Granting a category is easy — a held script is released and runs. Revoking one is not: a tracker that has already executed has already set its cookies, and no amount of DOM manipulation un-runs it. The only honest way to return the page to the visitor’s new choice is to load it again.

The banner renders inside a shadow root with all styles reset, so your CSS cannot reach it and it cannot reach your CSS. It uses no inline event handlers and no innerHTML; its own stylesheet lives inside the shadow root, so a Content-Security-Policy needs to allow that one style element.

If you build sites for other people

Agencies and site builders provision sites through the partner API with a key, and the tag is injected at serve time. Sites provisioned this way belong to the partner’s account. aisites is the first integration.

POST/api/v1/partner/sitesprovision a site
GET/api/v1/partner/sites/<key>status and domains
PATCH/api/v1/partner/sites/<key>update name / domain list
DELETE/api/v1/partner/sites/<key>soft-delete (records kept)
PUT/api/v1/partner/sites/<key>/policies/<kind>generate and publish a policy
POST/api/v1/partner/sites/<key>/scansqueue a scan
GET/api/v1/partner/sites/<key>/scanslatest scan and findings

Getting a site key

There is no dashboard yet. Sites are set up by hand, or through the partner API. Ask, and you will get a key and the tag above with it.