consentric 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 +225 -0
- package/dist/index.cjs +1019 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +157 -0
- package/dist/index.d.ts +157 -0
- package/dist/index.js +1011 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 salecki.digital
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# consentric
|
|
2
|
+
|
|
3
|
+
[](https://github.com/msalecki/consentric/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/consentric)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**A self-contained, drop-in GDPR cookie consent banner for React — with Google Consent Mode v2 built in.**
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
Most cookie banners are either a paid SaaS widget that injects a third-party script
|
|
12
|
+
into your page, or a barebones snippet you have to wire up yourself. `consentric` is
|
|
13
|
+
neither: it's a single React component you import, style with a few props, and forget
|
|
14
|
+
about. No external scripts, no account, no runtime dependencies, no Tailwind — just one
|
|
15
|
+
component that ships its own styles and talks to Google Consent Mode for you.
|
|
16
|
+
|
|
17
|
+
It uses a familiar tabbed layout (Consent / Details / About, four categories, an
|
|
18
|
+
accordion of per-cookie details) so it feels instantly recognisable to your visitors,
|
|
19
|
+
while every string, colour, category and cookie table stays under your control.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { CookieConsent } from 'consentric';
|
|
23
|
+
|
|
24
|
+
export default function App() {
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
{/* your app */}
|
|
28
|
+
<CookieConsent company="salecki.digital" privacyUrl="/privacy" />
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
That's the whole integration. On first visit it shows the banner; after a choice it
|
|
35
|
+
persists to a cookie and collapses to a small floating button so visitors can change
|
|
36
|
+
their mind anytime.
|
|
37
|
+
|
|
38
|
+
## Preview
|
|
39
|
+
|
|
40
|
+
The hero above is the **Consent** tab. The other two:
|
|
41
|
+
|
|
42
|
+
**Details** — every category's cookie table in an accordion.
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
**About** — a plain-language explainer plus your policy links.
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
Want to poke at it live? Spin up the playground — language picker plus light/dark themes:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm install
|
|
54
|
+
npm run example # opens http://localhost:5173
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- **Google Consent Mode v2** — pushes `default` (all denied) on mount and `update` on
|
|
60
|
+
the visitor's choice, mapping the four categories to the right Consent Mode signals.
|
|
61
|
+
- **Persistent & versioned** — the choice is stored in a cookie for one year and
|
|
62
|
+
restored on the next visit; bumping the schema version re-prompts everyone.
|
|
63
|
+
- **Familiar tabbed UX** — Consent / Details / About tabs, four categories, a per-cookie
|
|
64
|
+
accordion. Recognisable to visitors, nothing for you to design.
|
|
65
|
+
- **10 built-in languages** — English (default), German, French, Spanish, Italian,
|
|
66
|
+
Portuguese, Dutch, Polish, Czech and Slovak, picked with one `locale` prop. Every
|
|
67
|
+
category, cookie row and UI string is also overridable for any other language.
|
|
68
|
+
- **Accessible** — focus-trapped modal, focus restored on close, arrow-key tab
|
|
69
|
+
navigation, full ARIA wiring, and `prefers-reduced-motion` support.
|
|
70
|
+
- **No dark patterns** — "Allow all" and "Deny" get equal visual weight, the way EU
|
|
71
|
+
regulators expect a consent dialog to behave.
|
|
72
|
+
- **Works with Next.js** — ships as a Client Component (`"use client"`), so it drops
|
|
73
|
+
straight into the App Router.
|
|
74
|
+
- **Zero dependencies, zero Tailwind** — styles are scoped and injected; colours come
|
|
75
|
+
from props via CSS variables, so it drops into any React project and looks identical
|
|
76
|
+
regardless of the host's styling.
|
|
77
|
+
- **Small & self-contained** — one component with all 10 languages, ~15 KB gzipped;
|
|
78
|
+
ships ESM + CJS with types, `sideEffects: false`.
|
|
79
|
+
|
|
80
|
+
## Install
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npm install consentric
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`react >= 17` is a peer dependency.
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { CookieConsent } from 'consentric';
|
|
92
|
+
|
|
93
|
+
<CookieConsent
|
|
94
|
+
company="salecki.digital"
|
|
95
|
+
privacyUrl="/privacy"
|
|
96
|
+
termsUrl="/privacy#cookies"
|
|
97
|
+
colors={{ brand: '#FAE762', surface: '#050506', text: '#EDEDEF', onBrand: '#0a0a0c' }}
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
With no props it renders the default dark theme and English copy. Render it once, near
|
|
102
|
+
the root of your app.
|
|
103
|
+
|
|
104
|
+
## Props
|
|
105
|
+
|
|
106
|
+
| Prop | Default | What it does |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `manageDefault` | `true` | Push gtag consent `default` (all denied) on mount. Set `false` if you set it in `<head>` before GTM (strict timing — see below). |
|
|
109
|
+
| `cookieName` | `'site_consent'` | Consent cookie name. Must match the head/GTM snippet if you use one. |
|
|
110
|
+
| `company` | — | Operator / brand name shown in the header and About panel. |
|
|
111
|
+
| `logo` | — | Optional brand mark (an `<svg/>` or `<img/>`). |
|
|
112
|
+
| `privacyUrl` | `'/privacy'` | Privacy policy link. |
|
|
113
|
+
| `termsUrl` | `'/privacy#cookies'` | Cookie/terms link. |
|
|
114
|
+
| `locale` | `'en'` | Built-in language pack (`de`, `fr`, `es`, `it`, `pt`, `nl`, `pl`, `cs`, `sk`). English is the default and fallback; `pt-BR` resolves to `pt`. |
|
|
115
|
+
| `defaultOpen` | `false` | Open the dialog on mount even if a choice is stored (for previews/Storybook). |
|
|
116
|
+
| `defaultTab` | `'consent'` | Tab to show first: `consent` / `details` / `about`. |
|
|
117
|
+
| `colors` | dark theme | `{ brand, brandDeep, surface, surfaceAlt, text, textMuted, backdrop, onBrand }`. |
|
|
118
|
+
| `categories` | built-in EN | Per-category content overrides — see below. |
|
|
119
|
+
| `labels` | built-in EN | UI string overrides for localisation — see below. |
|
|
120
|
+
| `onChange` | — | `(choices) => void`, fired after the user makes or changes a choice. |
|
|
121
|
+
| `autoClearCookies` | `true` | When a category is denied or revoked, delete the cookies declared in its table (names support a trailing `*` wildcard). The consent cookie is never touched. |
|
|
122
|
+
|
|
123
|
+
## Customising categories & cookie tables
|
|
124
|
+
|
|
125
|
+
The four category keys are fixed (they map to Consent Mode signals), but their name,
|
|
126
|
+
descriptions and cookie list are yours. The count badge is derived from the cookie list,
|
|
127
|
+
so it always matches. Anything you omit falls back to the default.
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
<CookieConsent
|
|
131
|
+
categories={{
|
|
132
|
+
statistics: {
|
|
133
|
+
about: 'We use Plausible, a privacy-friendly analytics tool.',
|
|
134
|
+
cookies: [
|
|
135
|
+
{ name: 'plausible_ignore', provider: 'Plausible', purpose: 'Excludes your own visits.', meta: 'localStorage' },
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
// necessary / preferences / marketing keep their defaults
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Clearing cookies on withdrawal
|
|
144
|
+
|
|
145
|
+
When a visitor denies or later revokes a category, `consentric` deletes the cookies you
|
|
146
|
+
declared in that category's table — so the data actually leaves the browser, not just the
|
|
147
|
+
Consent Mode signal. Matching is by cookie name and supports a trailing `*` wildcard
|
|
148
|
+
(`_ga_*` clears `_ga_ABC123`), and the component tries the common path/domain combinations
|
|
149
|
+
so cookies set on the registrable domain (e.g. `.example.com`) are caught too.
|
|
150
|
+
|
|
151
|
+
Only cookies you list are ever removed, and the consent cookie itself is always kept. It's
|
|
152
|
+
on by default — pass `autoClearCookies={false}` to manage cleanup yourself.
|
|
153
|
+
|
|
154
|
+
## Localisation
|
|
155
|
+
|
|
156
|
+
`consentric` ships with **ten built-in languages** — English (the default and the
|
|
157
|
+
fallback for any missing string) plus nine more. Pick one with the `locale` prop and the
|
|
158
|
+
banner, categories and cookie tables are all translated:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<CookieConsent company="salecki.digital" locale="pl" />
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
| Code | Language | Code | Language |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| `en` | English (default) | `pt` | Portuguese |
|
|
167
|
+
| `de` | German | `nl` | Dutch |
|
|
168
|
+
| `fr` | French | `pl` | Polish |
|
|
169
|
+
| `es` | Spanish | `cs` | Czech |
|
|
170
|
+
| `it` | Italian | `sk` | Slovak |
|
|
171
|
+
|
|
172
|
+
Region subtags are accepted — `locale="pt-BR"` resolves to `pt`. The full list is also
|
|
173
|
+
exported as `SUPPORTED_LOCALES`.
|
|
174
|
+
|
|
175
|
+
### Custom strings & other languages
|
|
176
|
+
|
|
177
|
+
For a language without a built-in pack — or to reword one — pass your own strings via
|
|
178
|
+
`labels` (and `categories`). They win over the locale pack, which in turn falls back to
|
|
179
|
+
English, so you can override just a key or two. The `operatedBy` and `readMore` templates
|
|
180
|
+
support `{company}`, `{privacy}` and `{cookie}` placeholders so word order works in any
|
|
181
|
+
language.
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<CookieConsent
|
|
185
|
+
locale="de"
|
|
186
|
+
labels={{
|
|
187
|
+
// start from the German pack, just reword these two
|
|
188
|
+
heading: 'Wir verwenden Cookies',
|
|
189
|
+
deny: 'Nur notwendige',
|
|
190
|
+
}}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Light theme
|
|
195
|
+
|
|
196
|
+
Defaults assume a dark `surface`. For a light theme, also set `onBrand` (the text/icon
|
|
197
|
+
colour on top of the primary button) so it keeps enough contrast.
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
<CookieConsent
|
|
201
|
+
colors={{
|
|
202
|
+
brand: '#2563eb',
|
|
203
|
+
brandDeep: '#1d4ed8',
|
|
204
|
+
surface: '#ffffff',
|
|
205
|
+
surfaceAlt: 'rgba(0,0,0,0.04)',
|
|
206
|
+
text: '#0f172a',
|
|
207
|
+
textMuted: '#64748b',
|
|
208
|
+
backdrop: 'rgba(15,23,42,0.4)',
|
|
209
|
+
onBrand: '#ffffff',
|
|
210
|
+
}}
|
|
211
|
+
/>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Strict Consent Mode timing (recommended)
|
|
215
|
+
|
|
216
|
+
For tags to be gated **before** GTM loads, set the default in `<head>` (before the
|
|
217
|
+
GTM snippet) and pass `manageDefault={false}`. The same snippet can go into a GTM
|
|
218
|
+
Custom HTML tag on the **Consent Initialization - All Pages** trigger. The cookie
|
|
219
|
+
name in that snippet must match `cookieName`. Full snippet is in the header of
|
|
220
|
+
[src/CookieConsent.tsx](src/CookieConsent.tsx). If you skip both, leave `manageDefault`
|
|
221
|
+
on — the component pushes the default on mount (fine for simple setups).
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT © [salecki.digital](https://salecki.digital)
|