cookie-consent-gdpr 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +758 -0
- package/dist/cookie-consent-gdpr.cjs.js +2365 -0
- package/dist/cookie-consent-gdpr.esm.js +2363 -0
- package/dist/cookie-consent-gdpr.min.js +2 -0
- package/dist/cookie-consent-gdpr.umd.js +2371 -0
- package/dist/gdpr-cookie-consent.cjs.js +2365 -0
- package/dist/gdpr-cookie-consent.d.ts +182 -0
- package/dist/gdpr-cookie-consent.esm.js +2363 -0
- package/dist/gdpr-cookie-consent.min.js +2 -0
- package/dist/gdpr-cookie-consent.umd.js +2371 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
# GDPR Cookie Consent
|
|
2
|
+
|
|
3
|
+
A lightweight (**~9.7 KB gzipped**), fully GDPR & ePrivacy Directive compliant cookie consent banner. Zero dependencies. Framework-agnostic. Production-ready.
|
|
4
|
+
|
|
5
|
+
Provides granular cookie category control, a preferences modal with full cookie disclosure, automatic script blocking, consent record keeping, and optional server-side webhook — everything you need to comply with EU privacy regulations without paying for a SaaS tool.
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="media/banner.png" alt="Claude Code On The Go Banner">
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Quick Start](#quick-start)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Configuration](#configuration)
|
|
19
|
+
- [Full Configuration Reference](#full-configuration-reference)
|
|
20
|
+
- [Cookie Categories](#cookie-categories)
|
|
21
|
+
- [Cookie Details](#cookie-details)
|
|
22
|
+
- [Texts / i18n](#texts--i18n)
|
|
23
|
+
- [Theme](#theme)
|
|
24
|
+
- [Webhook (Consent Records)](#webhook-consent-records)
|
|
25
|
+
- [Cross-Domain / Subdomain Cookies](#cross-domain--subdomain-cookies)
|
|
26
|
+
- [Layout Modes](#layout-modes)
|
|
27
|
+
- [HTML Auto-Initialization](#html-auto-initialization)
|
|
28
|
+
- [Script & Element Blocking](#script--element-blocking)
|
|
29
|
+
- [JavaScript API](#javascript-api)
|
|
30
|
+
- [Events](#events)
|
|
31
|
+
- [Preferences Button](#preferences-button)
|
|
32
|
+
- [TypeScript Support](#typescript-support)
|
|
33
|
+
- [Accessibility](#accessibility)
|
|
34
|
+
- [GDPR Compliance Reference](#gdpr-compliance-reference)
|
|
35
|
+
- [Browser Support](#browser-support)
|
|
36
|
+
- [License](#license)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Three layout modes** — bottom/top bar, centered modal, corner popup
|
|
43
|
+
- **Granular consent** — per-category control (Necessary, Functional, Analytics, Marketing — or define your own)
|
|
44
|
+
- **Full cookie disclosure** — accordion UI showing every cookie's name, provider, purpose, expiry, and type
|
|
45
|
+
- **Automatic script blocking** — scripts, iframes, and images are blocked until the user consents to the relevant category
|
|
46
|
+
- **Consent records** — every consent decision is stored with a UUID, timestamp, categories, URL, and user agent
|
|
47
|
+
- **Webhook support** — optionally POST consent records to your server for GDPR Article 7(1) proof-of-consent
|
|
48
|
+
- **Config versioning** — when you change your cookie configuration, users are automatically re-prompted
|
|
49
|
+
- **Fully customizable** — every text string, color, font, and size is configurable
|
|
50
|
+
- **Accessible** — WCAG 2.1 AA compliant: focus trapping, keyboard navigation, ARIA attributes, reduced-motion support
|
|
51
|
+
- **Zero dependencies** — pure vanilla JavaScript, works with any framework or static site
|
|
52
|
+
- **Tiny footprint** — ~9.7 KB gzipped, ~38 KB minified
|
|
53
|
+
- **Multiple formats** — ESM, CJS, UMD, and standalone IIFE (script tag)
|
|
54
|
+
- **TypeScript declarations** included
|
|
55
|
+
- **Cross-subdomain support** — share consent across subdomains via cookie domain config
|
|
56
|
+
- **No pre-ticked boxes** — GDPR Recital 32 compliant out of the box
|
|
57
|
+
- **Equal reject/accept prominence** — reject button is always visible alongside accept
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Script Tag (Fastest)
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<script src="https://unpkg.com/cookie-consent-gdpr@latest/dist/cookie-consent-gdpr.min.js"></script>
|
|
67
|
+
<script>
|
|
68
|
+
CookieConsent.init({
|
|
69
|
+
categories: {
|
|
70
|
+
necessary: {
|
|
71
|
+
enabled: true,
|
|
72
|
+
readOnly: true,
|
|
73
|
+
title: 'Strictly Necessary',
|
|
74
|
+
description: 'Essential cookies that make the website work.',
|
|
75
|
+
cookies: [
|
|
76
|
+
{
|
|
77
|
+
name: 'session_id',
|
|
78
|
+
provider: 'yourdomain.com',
|
|
79
|
+
purpose: 'Maintains your session across page requests.',
|
|
80
|
+
expiry: 'Session',
|
|
81
|
+
type: 'HTTP Cookie',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
analytics: {
|
|
86
|
+
enabled: false,
|
|
87
|
+
readOnly: false,
|
|
88
|
+
title: 'Analytics',
|
|
89
|
+
description: 'Help us understand how visitors interact with our website.',
|
|
90
|
+
cookies: [
|
|
91
|
+
{
|
|
92
|
+
name: '_ga',
|
|
93
|
+
provider: 'Google Analytics',
|
|
94
|
+
purpose: 'Distinguishes unique users by assigning a randomly generated number.',
|
|
95
|
+
expiry: '2 years',
|
|
96
|
+
type: 'HTTP Cookie',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: '_ga_*',
|
|
100
|
+
provider: 'Google Analytics',
|
|
101
|
+
purpose: 'Used to persist session state.',
|
|
102
|
+
expiry: '2 years',
|
|
103
|
+
type: 'HTTP Cookie',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
marketing: {
|
|
108
|
+
enabled: false,
|
|
109
|
+
readOnly: false,
|
|
110
|
+
title: 'Marketing',
|
|
111
|
+
description: 'Used to deliver personalised advertisements.',
|
|
112
|
+
cookies: [
|
|
113
|
+
{
|
|
114
|
+
name: '_fbp',
|
|
115
|
+
provider: 'Facebook',
|
|
116
|
+
purpose: 'Tracks visits across websites for targeted advertising.',
|
|
117
|
+
expiry: '3 months',
|
|
118
|
+
type: 'HTTP Cookie',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
privacyPolicyUrl: '/privacy-policy',
|
|
124
|
+
});
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
That's it. The banner appears, users make their choice, scripts are blocked/unblocked accordingly, and consent is stored.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Installation
|
|
133
|
+
|
|
134
|
+
### NPM / Yarn
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm install cookie-consent-gdpr
|
|
138
|
+
# or
|
|
139
|
+
yarn add cookie-consent-gdpr
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
import CookieConsent from 'cookie-consent-gdpr';
|
|
144
|
+
|
|
145
|
+
CookieConsent.init({
|
|
146
|
+
// your config
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### CDN (Script Tag)
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<!-- unpkg -->
|
|
154
|
+
<script src="https://unpkg.com/cookie-consent-gdpr@latest/dist/cookie-consent-gdpr.min.js"></script>
|
|
155
|
+
|
|
156
|
+
<!-- jsDelivr -->
|
|
157
|
+
<script src="https://cdn.jsdelivr.net/npm/cookie-consent-gdpr@latest/dist/cookie-consent-gdpr.min.js"></script>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Self-Hosted
|
|
161
|
+
|
|
162
|
+
Download `dist/cookie-consent-gdpr.min.js` from this repository and include it:
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<script src="/path/to/cookie-consent-gdpr.min.js"></script>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
### Full Configuration Reference
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
CookieConsent.init({
|
|
176
|
+
// ─── Container ────────────────────────────────────────────────
|
|
177
|
+
// CSS selector or DOM element where the banner mounts.
|
|
178
|
+
// If null, a container is auto-created and appended to document.body.
|
|
179
|
+
container: null,
|
|
180
|
+
|
|
181
|
+
// ─── Layout ───────────────────────────────────────────────────
|
|
182
|
+
// 'bar' | 'modal' | 'popup'
|
|
183
|
+
layout: 'bar',
|
|
184
|
+
|
|
185
|
+
// 'bottom' | 'top' (for bar)
|
|
186
|
+
// 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' (for popup)
|
|
187
|
+
position: 'bottom',
|
|
188
|
+
|
|
189
|
+
// ─── Behavior ─────────────────────────────────────────────────
|
|
190
|
+
autoShow: true, // Show banner on page load if no consent exists
|
|
191
|
+
reconsentOnChange: true, // Re-prompt when cookie config changes
|
|
192
|
+
forceOverlay: false, // Block page interaction until consent is given
|
|
193
|
+
closeOnBackdrop: false, // Clicking backdrop closes banner (false = strict GDPR)
|
|
194
|
+
|
|
195
|
+
// ─── Preferences Button ───────────────────────────────────────
|
|
196
|
+
// Selector or element. Also auto-binds [data-cc-open-preferences].
|
|
197
|
+
preferencesButton: null,
|
|
198
|
+
|
|
199
|
+
// ─── Privacy Policy ───────────────────────────────────────────
|
|
200
|
+
privacyPolicyUrl: '',
|
|
201
|
+
|
|
202
|
+
// ─── Cookie Storage ───────────────────────────────────────────
|
|
203
|
+
cookie: {
|
|
204
|
+
name: 'cc_consent', // Consent cookie name
|
|
205
|
+
domain: '', // '' = current domain. '.example.com' = all subdomains
|
|
206
|
+
path: '/',
|
|
207
|
+
expiryDays: 365, // GDPR recommends ≤12 months
|
|
208
|
+
sameSite: 'Lax', // 'Lax' | 'Strict' | 'None'
|
|
209
|
+
secure: true, // Auto-detected from protocol by default
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// ─── Webhook ──────────────────────────────────────────────────
|
|
213
|
+
webhook: {
|
|
214
|
+
url: null, // POST endpoint for consent records. null = disabled.
|
|
215
|
+
headers: {}, // Additional headers (e.g. authorization)
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// ─── Categories ───────────────────────────────────────────────
|
|
219
|
+
categories: {
|
|
220
|
+
necessary: {
|
|
221
|
+
enabled: true,
|
|
222
|
+
readOnly: true,
|
|
223
|
+
title: 'Strictly Necessary',
|
|
224
|
+
description: '...',
|
|
225
|
+
cookies: [],
|
|
226
|
+
},
|
|
227
|
+
functional: {
|
|
228
|
+
enabled: false,
|
|
229
|
+
readOnly: false,
|
|
230
|
+
title: 'Functional',
|
|
231
|
+
description: '...',
|
|
232
|
+
cookies: [],
|
|
233
|
+
},
|
|
234
|
+
analytics: {
|
|
235
|
+
enabled: false,
|
|
236
|
+
readOnly: false,
|
|
237
|
+
title: 'Analytics',
|
|
238
|
+
description: '...',
|
|
239
|
+
cookies: [],
|
|
240
|
+
},
|
|
241
|
+
marketing: {
|
|
242
|
+
enabled: false,
|
|
243
|
+
readOnly: false,
|
|
244
|
+
title: 'Marketing',
|
|
245
|
+
description: '...',
|
|
246
|
+
cookies: [],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
// ─── Texts (i18n) ────────────────────────────────────────────
|
|
251
|
+
texts: { /* see Texts section below */ },
|
|
252
|
+
|
|
253
|
+
// ─── Theme ────────────────────────────────────────────────────
|
|
254
|
+
theme: { /* see Theme section below */ },
|
|
255
|
+
|
|
256
|
+
// ─── Callbacks ────────────────────────────────────────────────
|
|
257
|
+
onConsent: function (consent) {},
|
|
258
|
+
onRevoke: function (consent) {},
|
|
259
|
+
onAccept: function (category) {},
|
|
260
|
+
onReject: function (category) {},
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Cookie Categories
|
|
265
|
+
|
|
266
|
+
Define as many categories as you need. Each category requires:
|
|
267
|
+
|
|
268
|
+
| Property | Type | Description |
|
|
269
|
+
| ------------- | ---------- | -------------------------------------------------------- |
|
|
270
|
+
| `enabled` | `boolean` | Default state. **Must be `false`** for non-necessary categories (GDPR Recital 32). |
|
|
271
|
+
| `readOnly` | `boolean` | If `true`, the user cannot toggle this off. Use for strictly necessary cookies. |
|
|
272
|
+
| `title` | `string` | Display title shown in the preferences modal. |
|
|
273
|
+
| `description` | `string` | Explains what this category is for. |
|
|
274
|
+
| `cookies` | `Array` | List of cookie detail objects (see below). |
|
|
275
|
+
|
|
276
|
+
Standard categories: `necessary`, `functional`, `analytics`, `marketing`. You can add custom ones:
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
categories: {
|
|
280
|
+
necessary: { enabled: true, readOnly: true, title: 'Essential', ... },
|
|
281
|
+
social_media: {
|
|
282
|
+
enabled: false,
|
|
283
|
+
readOnly: false,
|
|
284
|
+
title: 'Social Media',
|
|
285
|
+
description: 'Cookies used by social media embeds and sharing widgets.',
|
|
286
|
+
cookies: [
|
|
287
|
+
{ name: '__stripe_mid', provider: 'Stripe', purpose: 'Fraud prevention', expiry: '1 year', type: 'HTTP Cookie' }
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Cookie Details
|
|
294
|
+
|
|
295
|
+
Each entry in a category's `cookies` array should provide the information required by GDPR Article 13:
|
|
296
|
+
|
|
297
|
+
| Property | Type | Description |
|
|
298
|
+
| ---------- | -------- | -------------------------------------------------- |
|
|
299
|
+
| `name` | `string` | Cookie name or wildcard pattern (e.g. `_ga_*`) |
|
|
300
|
+
| `provider` | `string` | Who sets this cookie (domain or company name) |
|
|
301
|
+
| `purpose` | `string` | Human-readable explanation of what it does |
|
|
302
|
+
| `expiry` | `string` | Duration (e.g. `1 year`, `Session`, `30 days`) |
|
|
303
|
+
| `type` | `string` | `HTTP Cookie`, `localStorage`, `sessionStorage`, `Pixel` |
|
|
304
|
+
|
|
305
|
+
All cookie details are displayed in the preferences modal accordion — this is required by the ePrivacy Directive.
|
|
306
|
+
|
|
307
|
+
### Texts / i18n
|
|
308
|
+
|
|
309
|
+
Override any text string to support your language:
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
texts: {
|
|
313
|
+
bannerTitle: 'We use cookies',
|
|
314
|
+
bannerDescription: 'We use cookies to improve your experience...',
|
|
315
|
+
acceptAll: 'Accept All',
|
|
316
|
+
rejectAll: 'Reject All',
|
|
317
|
+
settings: 'Cookie Settings',
|
|
318
|
+
preferencesTitle: 'Cookie Preferences',
|
|
319
|
+
preferencesDescription: 'Choose which cookies you want to allow...',
|
|
320
|
+
save: 'Save Preferences',
|
|
321
|
+
acceptAllPreferences: 'Accept All',
|
|
322
|
+
rejectAllPreferences: 'Reject All',
|
|
323
|
+
alwaysActive: 'Always Active',
|
|
324
|
+
cookieNameLabel: 'Name',
|
|
325
|
+
cookieProviderLabel: 'Provider',
|
|
326
|
+
cookiePurposeLabel: 'Purpose',
|
|
327
|
+
cookieExpiryLabel: 'Expiry',
|
|
328
|
+
cookieTypeLabel: 'Type',
|
|
329
|
+
noCookies: 'No cookies to display.',
|
|
330
|
+
privacyPolicyLabel: 'Privacy Policy',
|
|
331
|
+
poweredBy: '', // Optional branding text
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Example: German**
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
texts: {
|
|
339
|
+
bannerTitle: 'Wir verwenden Cookies',
|
|
340
|
+
bannerDescription: 'Wir verwenden Cookies und ähnliche Technologien...',
|
|
341
|
+
acceptAll: 'Alle akzeptieren',
|
|
342
|
+
rejectAll: 'Alle ablehnen',
|
|
343
|
+
settings: 'Cookie-Einstellungen',
|
|
344
|
+
preferencesTitle: 'Cookie-Einstellungen',
|
|
345
|
+
preferencesDescription: 'Hier können Sie Ihre Cookie-Präferenzen verwalten...',
|
|
346
|
+
save: 'Einstellungen speichern',
|
|
347
|
+
acceptAllPreferences: 'Alle akzeptieren',
|
|
348
|
+
rejectAllPreferences: 'Alle ablehnen',
|
|
349
|
+
alwaysActive: 'Immer aktiv',
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Theme
|
|
354
|
+
|
|
355
|
+
Customize every visual aspect:
|
|
356
|
+
|
|
357
|
+
```js
|
|
358
|
+
theme: {
|
|
359
|
+
primary: '#0e6b4e', // Primary button & accent color
|
|
360
|
+
primaryHover: '#0a5a40', // Primary button hover state
|
|
361
|
+
primaryText: '#ffffff', // Text on primary buttons
|
|
362
|
+
background: '#ffffff', // Banner/modal background
|
|
363
|
+
text: '#333333', // Primary text color
|
|
364
|
+
textSecondary: '#666666', // Secondary/description text
|
|
365
|
+
border: '#e0e0e0', // Borders and dividers
|
|
366
|
+
overlay: 'rgba(0,0,0,0.55)', // Backdrop overlay color
|
|
367
|
+
toggleOn: '#0e6b4e', // Toggle switch ON color
|
|
368
|
+
toggleOff: '#cccccc', // Toggle switch OFF color
|
|
369
|
+
toggleKnob: '#ffffff', // Toggle knob color
|
|
370
|
+
fontFamily: "'Inter', sans-serif",
|
|
371
|
+
fontSize: '14px',
|
|
372
|
+
borderRadius: '8px',
|
|
373
|
+
zIndex: 2147483645, // Ensures banner is above everything
|
|
374
|
+
maxWidth: '1140px', // Max width for bar layout
|
|
375
|
+
popupWidth: '400px', // Width for popup layout
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
You can also override theme values directly in CSS using custom properties:
|
|
380
|
+
|
|
381
|
+
```css
|
|
382
|
+
.cc-container {
|
|
383
|
+
--cc-primary: #ff6600;
|
|
384
|
+
--cc-bg: #1a1a2e;
|
|
385
|
+
--cc-text: #eaeaea;
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Webhook (Consent Records)
|
|
390
|
+
|
|
391
|
+
GDPR Article 7(1) requires you to be able to **demonstrate** that the user gave consent. While the consent cookie provides client-side proof, sending records to your server is recommended.
|
|
392
|
+
|
|
393
|
+
```js
|
|
394
|
+
webhook: {
|
|
395
|
+
url: 'https://api.yoursite.com/consent',
|
|
396
|
+
headers: {
|
|
397
|
+
'Authorization': 'Bearer your-api-key',
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
When consent is given or updated, a `POST` request is sent with this payload:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"consentId": "a1b2c3d4-e5f6-...",
|
|
407
|
+
"timestamp": "2026-02-23T14:30:00.000Z",
|
|
408
|
+
"categories": {
|
|
409
|
+
"necessary": true,
|
|
410
|
+
"functional": false,
|
|
411
|
+
"analytics": true,
|
|
412
|
+
"marketing": false
|
|
413
|
+
},
|
|
414
|
+
"url": "https://yoursite.com/page",
|
|
415
|
+
"userAgent": "Mozilla/5.0 ...",
|
|
416
|
+
"configHash": "k8m3x"
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Cross-Domain / Subdomain Cookies
|
|
421
|
+
|
|
422
|
+
To share consent across subdomains (e.g. `www.example.com` and `app.example.com`):
|
|
423
|
+
|
|
424
|
+
```js
|
|
425
|
+
cookie: {
|
|
426
|
+
domain: '.example.com', // Note the leading dot
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
This sets the consent cookie on the parent domain, making it accessible to all subdomains.
|
|
431
|
+
|
|
432
|
+
For true cross-origin consent (different TLDs), you would need a server-side approach using the webhook to synchronize consent records.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Layout Modes
|
|
437
|
+
|
|
438
|
+
### Bottom Bar (default)
|
|
439
|
+
|
|
440
|
+
```js
|
|
441
|
+
CookieConsent.init({
|
|
442
|
+
layout: 'bar',
|
|
443
|
+
position: 'bottom', // or 'top'
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
A full-width bar fixed to the bottom (or top) of the viewport. Most common pattern.
|
|
448
|
+
|
|
449
|
+
### Center Modal
|
|
450
|
+
|
|
451
|
+
```js
|
|
452
|
+
CookieConsent.init({
|
|
453
|
+
layout: 'modal',
|
|
454
|
+
forceOverlay: true, // recommended for modal
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
A centered dialog with backdrop. Good for high-consent-rate scenarios.
|
|
459
|
+
|
|
460
|
+
### Corner Popup
|
|
461
|
+
|
|
462
|
+
```js
|
|
463
|
+
CookieConsent.init({
|
|
464
|
+
layout: 'popup',
|
|
465
|
+
position: 'bottom-right', // or 'bottom-left', 'top-right', 'top-left'
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
A small card positioned in the corner. Less intrusive.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## HTML Auto-Initialization
|
|
474
|
+
|
|
475
|
+
For users with minimal JavaScript knowledge, the banner can auto-initialize using HTML attributes:
|
|
476
|
+
|
|
477
|
+
### Option 1: Script attribute
|
|
478
|
+
|
|
479
|
+
```html
|
|
480
|
+
<script
|
|
481
|
+
src="cookie-consent-gdpr.min.js"
|
|
482
|
+
data-cc-auto
|
|
483
|
+
data-cc-config='{"privacyPolicyUrl": "/privacy", "categories": { ... }}'
|
|
484
|
+
></script>
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Option 2: Container element
|
|
488
|
+
|
|
489
|
+
```html
|
|
490
|
+
<div id="cc-banner" data-cc-auto data-cc-config='{"layout": "popup"}'></div>
|
|
491
|
+
<script src="cookie-consent-gdpr.min.js"></script>
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
The `data-cc-config` attribute accepts a JSON string with any configuration options. If omitted, default settings are used.
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Script & Element Blocking
|
|
499
|
+
|
|
500
|
+
The most important GDPR requirement: **no non-essential cookies may be set before consent is given.** This library provides automatic blocking.
|
|
501
|
+
|
|
502
|
+
### Blocking Scripts
|
|
503
|
+
|
|
504
|
+
Change `type="text/javascript"` to `type="text/plain"` and add a `data-cookiecategory` attribute:
|
|
505
|
+
|
|
506
|
+
```html
|
|
507
|
+
<!-- This script will NOT execute until the user consents to "analytics" -->
|
|
508
|
+
<script
|
|
509
|
+
type="text/plain"
|
|
510
|
+
data-cookiecategory="analytics"
|
|
511
|
+
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"
|
|
512
|
+
></script>
|
|
513
|
+
|
|
514
|
+
<!-- Inline scripts work too -->
|
|
515
|
+
<script type="text/plain" data-cookiecategory="analytics">
|
|
516
|
+
window.dataLayer = window.dataLayer || [];
|
|
517
|
+
function gtag(){ dataLayer.push(arguments); }
|
|
518
|
+
gtag('js', new Date());
|
|
519
|
+
gtag('config', 'G-XXXXXX');
|
|
520
|
+
</script>
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Blocking Iframes
|
|
524
|
+
|
|
525
|
+
Use `data-src` instead of `src`:
|
|
526
|
+
|
|
527
|
+
```html
|
|
528
|
+
<!-- YouTube embed blocked until marketing consent -->
|
|
529
|
+
<iframe
|
|
530
|
+
data-cookiecategory="marketing"
|
|
531
|
+
data-src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
|
532
|
+
src="about:blank"
|
|
533
|
+
width="560"
|
|
534
|
+
height="315"
|
|
535
|
+
></iframe>
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Blocking Images (Tracking Pixels)
|
|
539
|
+
|
|
540
|
+
```html
|
|
541
|
+
<img
|
|
542
|
+
data-cookiecategory="analytics"
|
|
543
|
+
data-src="https://analytics.example.com/pixel.gif"
|
|
544
|
+
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
|
545
|
+
/>
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
When the user consents to a category, all matching elements are automatically activated: scripts are cloned with `type="text/javascript"`, and `data-src` is copied to `src`.
|
|
549
|
+
|
|
550
|
+
A **MutationObserver** also watches for dynamically added elements, so scripts injected after page load are handled too.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## JavaScript API
|
|
555
|
+
|
|
556
|
+
All methods return the `CookieConsent` object for chaining (except getters).
|
|
557
|
+
|
|
558
|
+
| Method | Description |
|
|
559
|
+
| ------------------------------- | -------------------------------------------------- |
|
|
560
|
+
| `CookieConsent.init(config)` | Initialize with configuration. |
|
|
561
|
+
| `CookieConsent.show()` | Show the consent banner. |
|
|
562
|
+
| `CookieConsent.hide()` | Hide the consent banner. |
|
|
563
|
+
| `CookieConsent.showPreferences()` | Open the preferences/settings modal. |
|
|
564
|
+
| `CookieConsent.hidePreferences()` | Close the preferences modal. |
|
|
565
|
+
| `CookieConsent.acceptAll()` | Accept all cookie categories. |
|
|
566
|
+
| `CookieConsent.rejectAll()` | Reject all non-essential categories. |
|
|
567
|
+
| `CookieConsent.acceptCategory(key)` | Accept a specific category by key. |
|
|
568
|
+
| `CookieConsent.getConsent()` | Get the full consent record (or `null`). |
|
|
569
|
+
| `CookieConsent.hasConsented()` | `true` if the user has made any consent decision. |
|
|
570
|
+
| `CookieConsent.hasCategory(key)` | `true` if a specific category is consented. |
|
|
571
|
+
| `CookieConsent.revokeConsent()` | Revoke consent, delete cookie, re-show banner. |
|
|
572
|
+
| `CookieConsent.on(event, fn)` | Register an event listener. |
|
|
573
|
+
| `CookieConsent.off(event, fn)` | Remove an event listener. |
|
|
574
|
+
| `CookieConsent.destroy()` | Fully remove UI and clean up all listeners. |
|
|
575
|
+
| `CookieConsent.getConfig()` | Get a read-only copy of the active configuration. |
|
|
576
|
+
|
|
577
|
+
### Example: Conditional Loading
|
|
578
|
+
|
|
579
|
+
```js
|
|
580
|
+
CookieConsent.init({
|
|
581
|
+
onConsent: function (consent) {
|
|
582
|
+
if (consent.categories.analytics) {
|
|
583
|
+
// Load Google Analytics dynamically
|
|
584
|
+
var script = document.createElement('script');
|
|
585
|
+
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX';
|
|
586
|
+
document.head.appendChild(script);
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
});
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Example: Check Consent Before Action
|
|
593
|
+
|
|
594
|
+
```js
|
|
595
|
+
if (CookieConsent.hasCategory('marketing')) {
|
|
596
|
+
// Safe to initialize Facebook Pixel
|
|
597
|
+
fbq('init', '1234567890');
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Events
|
|
604
|
+
|
|
605
|
+
Listen for consent lifecycle events:
|
|
606
|
+
|
|
607
|
+
| Event | Data | Description |
|
|
608
|
+
| --------------------- | ----------------------- | ---------------------------------------- |
|
|
609
|
+
| `consent:given` | `ConsentRecord` | Consent was given or updated. |
|
|
610
|
+
| `consent:revoked` | Previous `ConsentRecord`| Consent was revoked. |
|
|
611
|
+
| `category:accepted` | `string` (category key) | A specific category was accepted. |
|
|
612
|
+
| `category:rejected` | `string` (category key) | A specific category was rejected. |
|
|
613
|
+
| `banner:shown` | — | The banner became visible. |
|
|
614
|
+
| `banner:hidden` | — | The banner was hidden. |
|
|
615
|
+
| `preferences:shown` | — | The preferences modal was opened. |
|
|
616
|
+
| `preferences:hidden` | — | The preferences modal was closed. |
|
|
617
|
+
|
|
618
|
+
```js
|
|
619
|
+
CookieConsent.on('consent:given', function (consent) {
|
|
620
|
+
console.log('Consent given:', consent.categories);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
CookieConsent.on('category:accepted', function (category) {
|
|
624
|
+
console.log('Category accepted:', category);
|
|
625
|
+
});
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Preferences Button
|
|
631
|
+
|
|
632
|
+
Users must be able to change their preferences at any time (GDPR Article 7(3)). There are three ways to set up a preferences button:
|
|
633
|
+
|
|
634
|
+
### 1. HTML attribute (recommended for simplicity)
|
|
635
|
+
|
|
636
|
+
```html
|
|
637
|
+
<button data-cc-open-preferences>Cookie Settings</button>
|
|
638
|
+
<!-- or a link in your footer -->
|
|
639
|
+
<a href="#" data-cc-open-preferences>Manage Cookies</a>
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Any element with the `data-cc-open-preferences` attribute is automatically bound.
|
|
643
|
+
|
|
644
|
+
### 2. Config selector
|
|
645
|
+
|
|
646
|
+
```js
|
|
647
|
+
CookieConsent.init({
|
|
648
|
+
preferencesButton: '#my-cookie-button',
|
|
649
|
+
});
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### 3. JavaScript API
|
|
653
|
+
|
|
654
|
+
```js
|
|
655
|
+
document.getElementById('my-button').addEventListener('click', function () {
|
|
656
|
+
CookieConsent.showPreferences();
|
|
657
|
+
});
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## TypeScript Support
|
|
663
|
+
|
|
664
|
+
Type declarations are included in the package:
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
import CookieConsent, { CookieConsentConfig, ConsentRecord } from 'cookie-consent-gdpr';
|
|
668
|
+
|
|
669
|
+
const config: CookieConsentConfig = {
|
|
670
|
+
layout: 'bar',
|
|
671
|
+
categories: {
|
|
672
|
+
necessary: {
|
|
673
|
+
enabled: true,
|
|
674
|
+
readOnly: true,
|
|
675
|
+
title: 'Essential',
|
|
676
|
+
description: 'Required for the site to function.',
|
|
677
|
+
cookies: [],
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
CookieConsent.init(config);
|
|
683
|
+
|
|
684
|
+
CookieConsent.on('consent:given', (consent: ConsentRecord) => {
|
|
685
|
+
console.log(consent.id, consent.categories);
|
|
686
|
+
});
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
## Accessibility
|
|
692
|
+
|
|
693
|
+
This library follows WCAG 2.1 AA guidelines:
|
|
694
|
+
|
|
695
|
+
- **Focus trapping** — when the preferences modal is open, focus is trapped within it
|
|
696
|
+
- **Keyboard navigation** — all interactive elements are reachable via Tab, toggleable via Space/Enter
|
|
697
|
+
- **ARIA attributes** — `role="dialog"`, `aria-modal`, `aria-label`, `aria-expanded` are used throughout
|
|
698
|
+
- **Focus restoration** — when the modal closes, focus returns to the previously focused element
|
|
699
|
+
- **Reduced motion** — all animations are disabled when `prefers-reduced-motion: reduce` is set
|
|
700
|
+
- **Screen reader support** — proper semantic HTML, labels, and descriptions
|
|
701
|
+
- **No print output** — the banner is hidden in print stylesheets
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
## GDPR Compliance Reference
|
|
706
|
+
|
|
707
|
+
This library is designed to satisfy the following EU regulations:
|
|
708
|
+
|
|
709
|
+
### GDPR (General Data Protection Regulation)
|
|
710
|
+
|
|
711
|
+
| Article / Recital | Requirement | How This Library Complies |
|
|
712
|
+
| --- | --- | --- |
|
|
713
|
+
| **Art. 4(11)** — Definition of consent | Consent must be freely given, specific, informed, and an unambiguous indication of wishes. | Granular per-category consent. Clear descriptions. No pre-selected checkboxes. Active opt-in required. |
|
|
714
|
+
| **Art. 6(1)(a)** — Lawfulness of processing | Processing based on consent requires valid consent. | No non-essential cookies are set until explicit consent is given. Scripts are blocked via `type="text/plain"`. |
|
|
715
|
+
| **Art. 7(1)** — Demonstrating consent | The controller must be able to demonstrate that the user consented. | Every consent decision generates a record with UUID, timestamp, categories, URL, and user agent. Webhook support for server-side storage. |
|
|
716
|
+
| **Art. 7(2)** — Distinguishable consent | Consent request must be clearly distinguishable, in clear and plain language. | Standalone banner/modal with plain-language descriptions. Not bundled with other terms. |
|
|
717
|
+
| **Art. 7(3)** — Right to withdraw | It must be as easy to withdraw consent as to give it. | Preferences button (always accessible) lets users change or revoke consent at any time. `revokeConsent()` API available. |
|
|
718
|
+
| **Art. 7(4)** — No bundling | Consent must not be bundled with acceptance of terms or services. | Cookie consent is a standalone interaction, separate from any other site functionality. |
|
|
719
|
+
| **Art. 13** — Information to be provided | Users must be informed about purposes, recipients, and duration of processing. | Each cookie's name, provider, purpose, expiry, and type are displayed in the preferences modal. |
|
|
720
|
+
| **Recital 32** — Conditions for consent | Silence, pre-ticked boxes, or inactivity do not constitute consent. | All non-essential categories default to `enabled: false`. No pre-checked toggles. Users must take affirmative action. |
|
|
721
|
+
| **Recital 42** — Informed consent | The user must know the identity of the controller and the purposes. | Cookie provider and purpose are disclosed per-cookie in the preferences modal. |
|
|
722
|
+
|
|
723
|
+
### ePrivacy Directive (2002/58/EC, "Cookie Directive")
|
|
724
|
+
|
|
725
|
+
| Requirement | How This Library Complies |
|
|
726
|
+
| --- | --- |
|
|
727
|
+
| Prior consent before setting non-essential cookies | Scripts/iframes/pixels are blocked until explicit consent per category. |
|
|
728
|
+
| Strictly necessary cookies exempt | The `necessary` category is always active (`readOnly: true`) and clearly labeled "Always Active". |
|
|
729
|
+
| Clear and comprehensive information | Full cookie tables with name, provider, purpose, duration, and type. |
|
|
730
|
+
|
|
731
|
+
### Key Implementation Details
|
|
732
|
+
|
|
733
|
+
1. **No cookies before consent** — The library itself only sets one cookie (`cc_consent`) which stores the consent decision. No third-party scripts execute until the user opts in.
|
|
734
|
+
2. **The consent cookie is functional** — `cc_consent` is classified as a strictly necessary cookie because it records the user's privacy preferences, which is required for the website to respect their choices.
|
|
735
|
+
3. **Consent expiry** — Defaults to 365 days. GDPR best practice recommends re-obtaining consent at least every 12 months. Configurable via `cookie.expiryDays`.
|
|
736
|
+
4. **Config versioning** — If you add or change cookies in your configuration, the `configHash` changes and users are automatically re-prompted (satisfying the requirement that consent is specific to the declared purposes).
|
|
737
|
+
|
|
738
|
+
> **Disclaimer:** While this library implements the technical requirements of GDPR and the ePrivacy Directive, legal compliance also depends on your specific use case, data processing activities, and jurisdiction. Consult a qualified legal professional for advice specific to your situation.
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## Browser Support
|
|
743
|
+
|
|
744
|
+
- Chrome 51+
|
|
745
|
+
- Firefox 54+
|
|
746
|
+
- Safari 10+
|
|
747
|
+
- Edge 15+
|
|
748
|
+
- Opera 38+
|
|
749
|
+
- iOS Safari 10+
|
|
750
|
+
- Android Browser 5+
|
|
751
|
+
|
|
752
|
+
The library uses `MutationObserver`, `requestAnimationFrame`, `classList`, and `JSON.parse` — all widely supported.
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
## License
|
|
757
|
+
|
|
758
|
+
[MIT](LICENSE) — free for personal and commercial use.
|