Installation
1. Create a site
Sign in to the admin and create a site. You'll get a unique slug.
2. Paste one line above GTM
Put this in the <head> of the client's site, above any GTM orgtag snippet. Don't add async or defer:
<script src="https://your-domain.com/cb/loader.js?slug=CLIENT_SLUG"></script>
3. Block non-essential scripts
Swap type="text/javascript" for type="text/plain" and add a category attribute:
<script type="text/plain" data-consent-category="marketing"
src="https://example.com/pixel.js"></script>
<iframe data-consent-category="marketing"
data-src="https://www.youtube.com/embed/VIDEO_ID"></iframe>Categories: necessary · functional · analytics · marketing.
4. Google Consent Mode v2
The loader fires gtag('consent','default', …) synchronously before GTM boots, andgtag('consent','update', …) on every decision. All 7 signals are sent. No extra GTM configuration is needed on Google tags — their built-in Consent Settings handle gating automatically.
Per site, in the admin: wait_for_update, ads_data_redaction, url_passthrough, and granted-by-default for US opt-out states (a region-specific default). Stored consent is only reused while it matches the current banner version, and Global Privacy Control signals are honoured.
5. dataLayer events
// Every page, before GTM:
{ event: 'cookie_consent_default', consent_status: 'pending' | 'restored',
consent_banner_version, consent_gpc, consent_mode, timestamp }
// Every time consent is applied:
{
event: 'cookie_consent_update',
consent_action: 'accept_all' | 'reject_all' | 'save_preferences' | 'do_not_sell'
| 'restored' | 'implied' | 'reconsent_required',
consent_status: 'new' | 'restored' | 'pending',
consent_regulation: 'GDPR' | 'CCPA' | 'LGPD' | 'DEFAULT',
consent_banner_version: 3,
consent_id: '…', // same ID as in the proof-of-consent log
consent_gpc: false,
consent: { necessary, functional, analytics, marketing },
consent_mode: { ad_storage, ad_user_data, ad_personalization,
analytics_storage, functionality_storage,
personalization_storage, security_storage },
timestamp: 1712345678901
}
// Banner lifecycle:
{ event: 'cookie_consent_banner_shown', consent_regulation, consent_banner_version }
{ event: 'cookie_consent_preferences_opened', consent_regulation, consent_banner_version }6. JavaScript API
ConsentBanner.show()/show(true)— re-open banner / preferences.ConsentBanner.accept()/reject()— programmatic choice.ConsentBanner.getConsent()— current state.ConsentBanner.hasConsented()/getConsentId()— whether a choice exists, and its ID.ConsentBanner.getRegulation()— GDPR / CCPA / LGPD / DEFAULT.ConsentBanner.onConsentChange(cb)— subscribe; returns unsubscribe.
7. Let visitors change their choice
A floating button appears after a choice (configurable per site). Any link to #cookie-settings or element withdata-cb-open also opens the preferences.
8. Accessibility widget (optional)
Enable it per site under Design → Accessibility widget. No extra snippet is needed: the banner loads/cb/a11y.js only for sites that have it on. Visitors get bigger text, line height, text alignment, a readable font, contrast, grayscale, paused animations, hidden images, highlighted links, a reading mask, a page-structure list and a stronger focus outline. Their settings are kept in localStorage and do not depend on cookie consent.
- A link to
#accessibilityor an element withdata-cb-a11y-openopens the panel (useful if a visitor hid the button). CBAccessibility.open()/close()/reset()from your own scripts.
An overlay helps visitors but does not fix the underlying site; WCAG, ADA and IS 5568 conformance still needs accessible markup.