@c15t/browser 3.0.0-alpha.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/AGENTS.md +64 -0
- package/CHANGELOG.md +27 -0
- package/README.md +128 -0
- package/dist/861.js +1079 -0
- package/dist/925.js +649 -0
- package/dist/c15t.css +213 -0
- package/dist/c15t.devtools.js +1 -0
- package/dist/c15t.headless.js +1 -0
- package/dist/c15t.iab.css +190 -0
- package/dist/c15t.iab.js +221 -0
- package/dist/c15t.js +30 -0
- package/dist/devtools.js +11 -0
- package/dist/headless.js +17 -0
- package/dist/iab.js +784 -0
- package/dist/index.js +17 -0
- package/dist-types/auto-init.d.ts +42 -0
- package/dist-types/client.d.ts +51 -0
- package/dist-types/deferred.d.ts +13 -0
- package/dist-types/devtools.d.ts +32 -0
- package/dist-types/entries/cdn-devtools.d.ts +13 -0
- package/dist-types/entries/cdn-headless.d.ts +8 -0
- package/dist-types/entries/cdn-iab.d.ts +1 -0
- package/dist-types/entries/cdn.d.ts +8 -0
- package/dist-types/gated-scripts.d.ts +35 -0
- package/dist-types/generated/iab-styles.d.ts +144 -0
- package/dist-types/generated/styles.d.ts +137 -0
- package/dist-types/global.d.ts +112 -0
- package/dist-types/headless.d.ts +41 -0
- package/dist-types/iab/mount.d.ts +8 -0
- package/dist-types/iab/preferences.d.ts +9 -0
- package/dist-types/iab/styles.d.ts +2 -0
- package/dist-types/iab/surface.d.ts +4 -0
- package/dist-types/iab/vendor-disclosures.d.ts +14 -0
- package/dist-types/iab.d.ts +20 -0
- package/dist-types/index.d.ts +43 -0
- package/dist-types/transports/manifest.d.ts +62 -0
- package/dist-types/transports/offline.d.ts +18 -0
- package/dist-types/types.d.ts +290 -0
- package/dist-types/ui/actions.d.ts +29 -0
- package/dist-types/ui/banner.d.ts +13 -0
- package/dist-types/ui/branding.d.ts +25 -0
- package/dist-types/ui/copy.d.ts +28 -0
- package/dist-types/ui/dialog.d.ts +14 -0
- package/dist-types/ui/dom.d.ts +48 -0
- package/dist-types/ui/mount.d.ts +27 -0
- package/dist-types/ui/surface.d.ts +43 -0
- package/dist-types/ui/trigger.d.ts +14 -0
- package/dist-types/ui/widget.d.ts +27 -0
- package/dist-types/version.d.ts +1 -0
- package/docs/README.md +64 -0
- package/docs/frameworks/javascript/script-loader.md +47 -0
- package/docs/frameworks/javascript/script-tag.md +375 -0
- package/docs/integrations/adobe-analytics.md +294 -0
- package/docs/integrations/ahrefs-analytics.md +291 -0
- package/docs/integrations/amplitude.md +292 -0
- package/docs/integrations/building-integrations.md +47 -0
- package/docs/integrations/clearbit.md +291 -0
- package/docs/integrations/cloudflare-web-analytics.md +292 -0
- package/docs/integrations/crisp.md +294 -0
- package/docs/integrations/databuddy.md +311 -0
- package/docs/integrations/fathom-analytics.md +293 -0
- package/docs/integrations/google-maps.md +384 -0
- package/docs/integrations/google-tag-manager.md +306 -0
- package/docs/integrations/google-tag.md +305 -0
- package/docs/integrations/heap.md +292 -0
- package/docs/integrations/hightouch.md +293 -0
- package/docs/integrations/hotjar.md +292 -0
- package/docs/integrations/intercom.md +285 -0
- package/docs/integrations/linkedin-insights.md +291 -0
- package/docs/integrations/logrocket.md +293 -0
- package/docs/integrations/matomo-analytics.md +316 -0
- package/docs/integrations/meta-pixel.md +285 -0
- package/docs/integrations/microsoft-clarity.md +292 -0
- package/docs/integrations/microsoft-uet.md +291 -0
- package/docs/integrations/mixpanel-analytics.md +295 -0
- package/docs/integrations/openai-pixel.md +441 -0
- package/docs/integrations/overview.md +111 -0
- package/docs/integrations/pirsch.md +295 -0
- package/docs/integrations/plausible-analytics.md +295 -0
- package/docs/integrations/posthog.md +397 -0
- package/docs/integrations/promptwatch.md +291 -0
- package/docs/integrations/reddit-pixel.md +294 -0
- package/docs/integrations/rudderstack.md +328 -0
- package/docs/integrations/rybbit-analytics.md +296 -0
- package/docs/integrations/segment.md +292 -0
- package/docs/integrations/snapchat-pixel.md +293 -0
- package/docs/integrations/tiktok-pixel.md +291 -0
- package/docs/integrations/umami-analytics.md +295 -0
- package/docs/integrations/vercel-analytics.md +293 -0
- package/docs/integrations/x-pixel.md +291 -0
- package/docs/integrations/youtube.md +405 -0
- package/package.json +117 -0
- package/readme.json +44 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Script tag
|
|
3
|
+
description: Add a consent banner to Framer, Webflow, WordPress, Squarespace, or
|
|
4
|
+
any HTML page with one script tag and no build step.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`@c15t/browser` is the c15t consent banner for sites you cannot bundle: page builders, CMS themes, static HTML. One `<script>` tag from a CDN installs the banner, the preference centre, and script gating. Nothing to compile, nothing to import.
|
|
9
|
+
|
|
10
|
+
It is built on the same `@c15t/core` runtime and `@c15t/ui` stylesheet as the React, Vue, and Svelte packages, and uses the same policy and presentation resolvers. Framework API parity is covered under [limitations](#limitations).
|
|
11
|
+
|
|
12
|
+
## Add the tag
|
|
13
|
+
|
|
14
|
+
Paste this before `</head>` (Framer: Site Settings → Custom Code; Webflow: Project Settings → Custom Code; WordPress: a header scripts plugin or your theme's `header.php`).
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script
|
|
18
|
+
src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js"
|
|
19
|
+
data-backend-url="https://your-instance.c15t.dev"
|
|
20
|
+
defer
|
|
21
|
+
></script>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Reload the page. You should see the banner in the bottom left corner. Accept or reject, and it stays hidden on the next visit.
|
|
25
|
+
|
|
26
|
+
Pin a version in the URL so a release cannot change your banner without you.
|
|
27
|
+
|
|
28
|
+
### Without a backend
|
|
29
|
+
|
|
30
|
+
Drop `data-backend-url` to use offline mode. It resolves the same recommended rules as React and stores records in the browser. Unknown locations use the strict opt-in fallback. Pass known location through `overrides` or the data attributes, or use a hosted backend to resolve location. A backend also stores audit records and supplies translations.
|
|
31
|
+
|
|
32
|
+
## Attributes
|
|
33
|
+
|
|
34
|
+
Every attribute is optional.
|
|
35
|
+
|
|
36
|
+
| Attribute | What it does |
|
|
37
|
+
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| `data-backend-url` | Backend origin. Selects hosted mode. |
|
|
39
|
+
| `data-mode` | `hosted`, `offline`, or `manifest`. Inferred when omitted. |
|
|
40
|
+
| `data-manifest-url` | URL to fetch a consent manifest. Include `data-backend-url` when this is not the backend's `/manifest` endpoint. |
|
|
41
|
+
| `data-categories` | Comma-separated categories to offer: `functionality`, `experience`, `measurement`, `marketing`. Omit it to display the entire policy scope. This filters the UI, not the policy; undisplayed categories may leave a choice prompt incomplete. |
|
|
42
|
+
| `data-policy-rules` | Comma-separated policy presets for offline mode: `europeOptIn`, `usPrivacyStatesOptOut`, `quebecOptIn`, `worldNone`. Resolved by the visitor's country. |
|
|
43
|
+
| `data-language` | Force a language instead of reading the browser's. |
|
|
44
|
+
| `data-country`, `data-region` | Known location, when your host injects it. |
|
|
45
|
+
| `data-color-scheme` | `light`, `dark`, or `system` (default). |
|
|
46
|
+
| `data-trigger` | Show a floating button that reopens the preference centre. |
|
|
47
|
+
| `data-hide-branding` | Hide the "Secured by c15t" tag. The IAB first-layer banner always keeps its branding visible. |
|
|
48
|
+
| `data-privacy-policy-url`, `data-cookie-policy-url`, `data-terms-url` | Show the configured legal links in the stock banner and preference centre. |
|
|
49
|
+
| `data-shadow="false"` | Render into the page instead of a shadow root, so your CSS applies. |
|
|
50
|
+
| `data-no-ui` | Run the runtime with no banner. Same as loading `c15t.headless.js`. |
|
|
51
|
+
| `data-manual` | Capture the tag's configuration without initialising. The page calls `c15t.init()` itself after the script loads. |
|
|
52
|
+
|
|
53
|
+
For anything the attributes cannot express, queue a `config` call on `window.c15t` before the tag. There is only one global: before the script loads it is an array of calls, after it loads it is the API, and the queue is replayed in order. `config` accepts every option of [`init()`](#the-window-api) and wins over the attributes.
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<script>
|
|
57
|
+
window.c15t = window.c15t || [];
|
|
58
|
+
c15t.push(['config', {
|
|
59
|
+
legalLinks: { privacyPolicy: { href: '/privacy' } },
|
|
60
|
+
scripts: [
|
|
61
|
+
{
|
|
62
|
+
id: 'analytics',
|
|
63
|
+
src: '/analytics.js',
|
|
64
|
+
category: 'measurement',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
ui: {
|
|
68
|
+
colorScheme: 'dark',
|
|
69
|
+
trigger: true,
|
|
70
|
+
banner: { legalLinks: ['privacyPolicy'] },
|
|
71
|
+
dialog: { legalLinks: ['privacyPolicy'] },
|
|
72
|
+
},
|
|
73
|
+
}]);
|
|
74
|
+
</script>
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js" defer></script>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
With `data-manual`, a later `c15t.init()` retains the original tag's attributes. Queued `config` calls override those attributes, and options passed directly to `init()` win over both. Changing or removing the tag after it loads does not change the captured configuration. After `dispose()`, a new `init()` reuses the captured attributes and queued defaults, including backend-injected settings. Pass new options to `init()` to override them.
|
|
79
|
+
|
|
80
|
+
Legal-link URL attributes select their links for both stock surfaces, including IAB. Override the selection with `ui.banner.legalLinks` or `ui.dialog.legalLinks`; `[]` or `null` hides links on that surface. Other banner or dialog options, such as `hideBranding`, preserve the selection.
|
|
81
|
+
|
|
82
|
+
## Gate your own scripts
|
|
83
|
+
|
|
84
|
+
List scripts under `scripts` for the core loader lifecycle, including cleanup callbacks. In HTML, keep a tag inert until consent by changing its `type`:
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<script type="text/plain" data-c15t-category="marketing" src="https://pixel.example/tag.js"></script>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
External scripts without `async` finish loading before the next permitted non-async script runs, including inline code that uses the library. Explicit `async` scripts load independently. The scanner preserves each script's `nonce` for pages that authorize scripts through Content Security Policy.
|
|
91
|
+
|
|
92
|
+
An activated inline or external script cannot be undone. Supply cleanup through the `scripts` API when a vendor supports it; otherwise reload after withdrawal. The HTML scanner activates only `type="text/plain"` scripts and also watches newly inserted tags.
|
|
93
|
+
|
|
94
|
+
See [script gating](./script-loader.md) for the full `scripts` option, and the [integrations](../../integrations/overview.md) for ready-made GTM, GA4, and pixel loaders.
|
|
95
|
+
|
|
96
|
+
## Buttons and links
|
|
97
|
+
|
|
98
|
+
Any element on the page can drive the banner. Add `data-c15t-action` and the client wires it:
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<button data-c15t-action="customize">Cookie settings</button>
|
|
102
|
+
<button data-c15t-action="accept">Accept all</button>
|
|
103
|
+
<button data-c15t-action="reject">Reject all</button>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`dismiss` acknowledges a notice without changing category choices. `customize` opens the preference centre, `banner` shows the banner again, and `close` hides whatever is open. A plain link to `#c15t-preferences` opens the preference centre too, which is the easiest hook in a page builder's footer.
|
|
107
|
+
|
|
108
|
+
## The window API
|
|
109
|
+
|
|
110
|
+
The tag installs `window.c15t`. Use it from your own code:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
c15t.on('consent', (snapshot) => {
|
|
114
|
+
if (c15t.has('measurement')) {
|
|
115
|
+
startAnalytics();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
c15t.ready().then(() => {
|
|
120
|
+
// the policy is resolved and the banner has rendered
|
|
121
|
+
c15t.hasConsented(); // an explicit category receipt exists; use has() for permission
|
|
122
|
+
});
|
|
123
|
+
c15t.openDialog();
|
|
124
|
+
c15t.acceptAll();
|
|
125
|
+
c15t.save({ measurement: true, marketing: false });
|
|
126
|
+
c15t.identify({ id: 'user_123' });
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Queue `config` and `on` before the tag loads. Call action methods after initialization:
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<script>
|
|
133
|
+
window.c15t = [['on', 'consent', (snapshot) => console.log(snapshot.effectivePermissions)]];
|
|
134
|
+
</script>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Queued event listeners attach before the client starts, so consent listeners receive startup events when the client restores saved choices. `on()` returns an unsubscribe function that also works before initialization. Disposal removes the current client's listeners; register new listeners after `dispose()` to observe the next client.
|
|
138
|
+
|
|
139
|
+
The same events fire on `document` as `c15t:ready`, `c15t:consent`, `c15t:ui`, and `c15t:error`, with the payload in `event.detail`.
|
|
140
|
+
|
|
141
|
+
## Headless
|
|
142
|
+
|
|
143
|
+
Load `c15t.headless.js` instead and no banner or CSS ships. The runtime still resolves the policy, gates scripts, and persists consent; your HTML renders the banner and calls the API. `c15t.on('ui', surface)` tells you when to show it: `surface` is `'banner'`, `'dialog'`, or `'none'`.
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<div id="banner" hidden>
|
|
147
|
+
<p>We use cookies.</p>
|
|
148
|
+
<button data-c15t-action="reject">Reject</button>
|
|
149
|
+
<button data-c15t-action="accept">Accept</button>
|
|
150
|
+
</div>
|
|
151
|
+
<script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.headless.js" data-backend-url="https://your-instance.c15t.dev" defer></script>
|
|
152
|
+
<script>
|
|
153
|
+
window.c15t = [['on', 'ui', (surface) => { document.getElementById('banner').hidden = surface !== 'banner'; }]];
|
|
154
|
+
</script>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Styling
|
|
158
|
+
|
|
159
|
+
The banner renders inside a shadow root with its own copy of the stylesheet, so a theme's global `button {}` rule cannot reach it and the banner's rules cannot leak out. Customise it through the theme tokens rather than selectors:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
window.c15t = window.c15t || [];
|
|
163
|
+
c15t.push(['config', {
|
|
164
|
+
ui: {
|
|
165
|
+
theme: {
|
|
166
|
+
colors: { primary: '#0a66ff' },
|
|
167
|
+
radius: { lg: '1rem' },
|
|
168
|
+
typography: { fontFamily: 'system-ui, sans-serif' },
|
|
169
|
+
},
|
|
170
|
+
css: '[data-testid="consent-banner-card"] { border: 3px solid #0a66ff; }',
|
|
171
|
+
},
|
|
172
|
+
}]);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`theme` takes the same object `@c15t/react` accepts. `css` is appended after the bundled stylesheet, still inside the shadow root. To take over completely, set `shadow: false` (or `data-shadow="false"`) and the DOM renders in the page with a stylesheet owned by the mounted host; `noStyle: true` then strips every class so you start from unstyled markup with the same `data-testid` attributes as the other c15t packages.
|
|
176
|
+
|
|
177
|
+
For an ordinary page stylesheet, configure `ui: { shadow: false }` before loading the tag, then add:
|
|
178
|
+
|
|
179
|
+
```css
|
|
180
|
+
[data-c15t-ui] [data-testid="consent-banner-card"] {
|
|
181
|
+
border: 3px solid #0a66ff;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
For layout, pass host presentation separately from the policy:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
c15t.push(['config', {
|
|
189
|
+
presentation: {
|
|
190
|
+
prompt: { variant: 'bar', position: 'bottom' },
|
|
191
|
+
preferences: { uiProfile: 'balanced' },
|
|
192
|
+
},
|
|
193
|
+
}]);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The prompt supports `floating`, `bar`, `widget`, and `wall`. The shared resolver applies policy constraints, including nonblocking notices and blocking choice walls. Colors, fonts, spacing, borders, and layout are frontend configuration. A backend would only be needed to store and distribute remotely managed settings.
|
|
197
|
+
|
|
198
|
+
The repository's `examples/script-tag/styled.html` shows both CSS modes. Run `bun turbo run build --filter=@c15t/browser` and `bun run --cwd examples/script-tag dev`, then open `/styled` or `/styled?shadow=false`. `/custom` shows a banner made from your own HTML on the headless build.
|
|
199
|
+
|
|
200
|
+
## Faster first paint with a manifest
|
|
201
|
+
|
|
202
|
+
Hosted mode asks the backend for `/init` on every fresh visit before the banner can render. The backend also serves `/manifest`: the geo-independent half of that decision, cacheable at the CDN. In manifest mode the browser resolves the policy itself, so a "one banner for everyone" policy renders with no request at all when the manifest is inlined.
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<script>
|
|
206
|
+
window.c15t = window.c15t || [];
|
|
207
|
+
c15t.push(['config', {
|
|
208
|
+
mode: 'manifest',
|
|
209
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
210
|
+
manifest: {/* GET https://your-instance.c15t.dev/manifest */},
|
|
211
|
+
}]);
|
|
212
|
+
</script>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Or fetch a cached manifest with `data-manifest-url`. When you host the manifest on a separate CDN, also specify the backend that receives consent saves:
|
|
216
|
+
|
|
217
|
+
```html
|
|
218
|
+
<script
|
|
219
|
+
src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js"
|
|
220
|
+
data-manifest-url="https://cdn.example.com/consent-policy.json"
|
|
221
|
+
data-backend-url="https://your-instance.c15t.dev"
|
|
222
|
+
defer
|
|
223
|
+
></script>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Only a URL ending in `/manifest` can supply the backend base URL automatically. Other manifest URLs and inline manifests require `backendURL`, or `data-backend-url` on the tag. An explicit `backendURL: ''` uses the page's origin. Missing backend configuration raises an error during initialization. Use offline mode for browser-only persistence.
|
|
227
|
+
|
|
228
|
+
The client fetches the manifest once, honouring its cache headers. When the policy depends on location and the browser does not know the country or region needed by a rule, the client falls back to the backend's `/init`. Consent saves go to that backend, even when the manifest comes from another CDN.
|
|
229
|
+
|
|
230
|
+
Only English ships in the bundle; a manifest carries its own translations for the languages the site serves.
|
|
231
|
+
|
|
232
|
+
### Serve it from your backend
|
|
233
|
+
|
|
234
|
+
A [self-hosted backend](https://c15t.com/docs/self-host/quickstart) does the inlining for you. It serves `GET /c15t.js` (and `/c15t.headless.js`): the same bundle behind a queued `config` call that already carries the manifest, the backend origin, and any defaults you configure. One tag, one request, and the banner renders as soon as the script parses.
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<script src="https://consent.example.com/c15t.js" defer></script>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Configure what gets baked in with the backend's `script` option:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
createApp(runtime, {
|
|
244
|
+
script: {
|
|
245
|
+
config: {
|
|
246
|
+
legalLinks: { privacyPolicy: { href: 'https://example.com/privacy' } },
|
|
247
|
+
ui: {
|
|
248
|
+
banner: { legalLinks: ['privacyPolicy'] },
|
|
249
|
+
dialog: { legalLinks: ['privacyPolicy'] },
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Anything the page queues with `c15t.push(['config', ...])` before the tag still wins over the baked-in defaults. The response uses the same cache policy as `/manifest`, with an ETag covering the configuration and bundle, so put a CDN in front of it. Add `?language=de` to slice the manifest's translations to one language.
|
|
257
|
+
|
|
258
|
+
## Dev tools
|
|
259
|
+
|
|
260
|
+
Add a second tag and the c15t DevTools panel attaches to the page's client. It shows consents, gated scripts, the resolved policy and every kernel event, and its Location tab changes country, region, language and GPC and re-runs init, so you can watch a geo-keyed policy switch without spoofing headers or a VPN.
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.devtools.js" data-open data-tab="location" defer></script>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Tag order does not matter; the panel waits for the client. It is also on `window.c15t.devtools`, so `c15t.devtools.actions.setOverrides({ country: 'US' })` followed by `c15t.devtools.actions.init()` does the same from the console. Leave the tag out of production pages; it shows internal state. Bundler users import `mountDevTools` from `@c15t/browser/devtools`.
|
|
267
|
+
|
|
268
|
+
## In a bundler
|
|
269
|
+
|
|
270
|
+
The same client is published as ES modules for projects that do have a build step but no framework:
|
|
271
|
+
|
|
272
|
+
```package-install
|
|
273
|
+
@c15t/browser
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { init } from '@c15t/browser';
|
|
278
|
+
|
|
279
|
+
const c15t = init({
|
|
280
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`@c15t/browser/headless` exports the client without the UI, and `@c15t/browser/styles.css` is the stylesheet for `shadow: false`.
|
|
285
|
+
|
|
286
|
+
## Optional IAB entry
|
|
287
|
+
|
|
288
|
+
Load `c15t.iab.js` instead of `c15t.js` when your policy requires IAB TCF. This separate build includes the CMP, TC-string codec, banner, and vendor/purpose preference centre. The normal and headless builds exclude the IAB implementation and stylesheet.
|
|
289
|
+
|
|
290
|
+
```html
|
|
291
|
+
<script>
|
|
292
|
+
window.c15t = [['config', {
|
|
293
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
294
|
+
iab: {
|
|
295
|
+
cmpId: 28, // Replace with your registered CMP ID, or let the backend supply it.
|
|
296
|
+
vendors: [1, 2, 755],
|
|
297
|
+
},
|
|
298
|
+
legalLinks: {
|
|
299
|
+
privacyPolicy: { href: '/privacy' },
|
|
300
|
+
cookiePolicy: { href: '/cookies' },
|
|
301
|
+
},
|
|
302
|
+
ui: {
|
|
303
|
+
trigger: true,
|
|
304
|
+
theme: { colors: { primary: '#0a66ff' } },
|
|
305
|
+
banner: { legalLinks: ['privacyPolicy', 'cookiePolicy'] },
|
|
306
|
+
dialog: { legalLinks: ['privacyPolicy'], hideBranding: true },
|
|
307
|
+
},
|
|
308
|
+
}]];
|
|
309
|
+
</script>
|
|
310
|
+
<script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.iab.js" defer></script>
|
|
311
|
+
<button type="button" data-c15t-action="customize">Privacy settings</button>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The self-hosted backend can serve the same entry at `/c15t.iab.js`. Set `script.config.iab` to supply CMP defaults and `script.iabPath` to change the route. Both script routes use the same manifest and backend settings, but only the IAB route serves the IAB bundle.
|
|
315
|
+
|
|
316
|
+
Do not load both `c15t.js` and `c15t.iab.js`. The IAB entry owns one client and one CMP. It shows the ordinary banner when the resolved policy is non-IAB. If an IAB policy cannot load its vendor list, the UI shows an error and keeps confirmation disabled.
|
|
317
|
+
|
|
318
|
+
Bundler applications import the optional entry:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import { init } from '@c15t/browser/iab';
|
|
322
|
+
|
|
323
|
+
const client = init({
|
|
324
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
325
|
+
iab: { cmpId: 28, vendors: [1, 2, 755] },
|
|
326
|
+
ui: { trigger: true },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await client.ready();
|
|
330
|
+
const cmp = client.runtime.iab;
|
|
331
|
+
if (cmp) {
|
|
332
|
+
await cmp.whenReady?.();
|
|
333
|
+
cmp.setPurposeConsent(1, true);
|
|
334
|
+
cmp.setVendorConsent(755, true);
|
|
335
|
+
const result = await client.saveIAB();
|
|
336
|
+
if (!result.ok) client.openDialog();
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`ready()` resolves the policy. `runtime.iab.whenReady()` waits for the vendor list and CMP setup and rejects if setup fails. The CMP installs `window.__tcfapi`; `dispose()` removes it and its subscriptions.
|
|
341
|
+
|
|
342
|
+
`acceptAll()` and `rejectAll()` confirm through the CMP in IAB mode. Use `saveIAB()` for individual changes. Category-only `save({ ... })` rejects an IAB policy because it cannot produce the required TC authority. The CMP also exposes `setPurposeLegitimateInterest`, `setVendorLegitimateInterest`, and `setSpecialFeatureOptIn`. These edit the IAB draft; call `saveIAB()` to confirm it.
|
|
343
|
+
|
|
344
|
+
### Purpose and partner choices
|
|
345
|
+
|
|
346
|
+
In the stock preference centre, changing a purpose's consent also changes consent for the partners that use that purpose on a consent basis. Its legitimate-interest control changes the purpose and its legitimate-interest partners separately. Turning that control off records an objection; it does not change consent-based choices.
|
|
347
|
+
|
|
348
|
+
Each stack has a group control for its purposes and consent-based partners. The control exposes a mixed state when only some purposes are selected. Special-feature controls update the feature opt-in and its associated consent partners without changing the same-numbered purpose. Partners shared by several purposes have one vendor-wide consent value.
|
|
349
|
+
|
|
350
|
+
The vendor tab provides individual consent and legitimate-interest controls, search, and incremental loading. Expand a vendor to see its declared purposes, features, data categories, privacy and legitimate-interest policy links, cookie lifetime and refresh information, and retention periods for individual purposes and special purposes. These disclosures depend on the vendor's data in the Global Vendor List, or GVL.
|
|
351
|
+
|
|
352
|
+
These grouped actions belong to the stock UI. The low-level `runtime.iab` setters each edit their named choice, so custom HTML must set associated vendor choices explicitly when offering the same grouped behavior.
|
|
353
|
+
|
|
354
|
+
### IAB configuration and styling
|
|
355
|
+
|
|
356
|
+
Configure link destinations in `legalLinks`, then select which links each layer shows with `ui.banner.legalLinks` and `ui.dialog.legalLinks`, as in the script example above. With JavaScript configuration alone, an omitted or empty selection shows no links. URL attributes select their links for both surfaces by default. The IAB banner always shows c15t branding, matching React. `ui.dialog.hideBranding` can hide it in the preference centre.
|
|
357
|
+
|
|
358
|
+
Use `i18n` for translated copy. The ordinary `ui.banner.title`, `description`, `acceptButtonText`, `rejectButtonText`, and `customizeButtonText` overrides also apply to the IAB banner. A description can include `{partnerCount}`; the purpose summary and legitimate-interest disclosure remain visible below it.
|
|
359
|
+
|
|
360
|
+
Use `presentation.prompt` and `presentation.preferences` to configure blocking and layout. The banner's `scrollLock` and `trapFocus` options also work; explicit presentation blocking takes precedence, and a wall remains blocking.
|
|
361
|
+
|
|
362
|
+
The IAB UI supports `ui.theme`, `ui.css`, and `ui.shadow` just like the ordinary banner. For external light-DOM styles, load both `@c15t/browser/styles.css` and `@c15t/browser/iab/styles.css`. Set `ui: false` to build custom HTML against the IAB client. This still downloads the IAB-capable entry. Styling and custom markup need no backend changes or React component APIs.
|
|
363
|
+
|
|
364
|
+
Override the adapter's loading error, save error, and pagination copy with `ui.iab.loadErrorText`, `ui.iab.saveErrorText`, and `ui.iab.moreVendorsText`. The remaining IAB copy comes from the resolved translations.
|
|
365
|
+
|
|
366
|
+
Try `examples/script-tag/iab.html` at `/iab` in the example server. It uses a local sample vendor list and offline storage, so you can exercise TC-string saves without a backend.
|
|
367
|
+
|
|
368
|
+
## Limitations
|
|
369
|
+
|
|
370
|
+
* IAB TCF requires the optional IAB entry. The browser adapter has not yet joined the shared framework visual parity runner.
|
|
371
|
+
* The bundle carries English only. Other languages come from the backend, manifest, or `i18n` overrides.
|
|
372
|
+
* The browser client exposes `getSnapshot()`, `subscribe()`, and imperative actions. It does not expose React's `useConsentDraft()` API or compound component composition. A custom preference center must own its draft, seed it from explicit choices and policy defaults, and discard stale drafts when the choice fingerprint changes.
|
|
373
|
+
* `acceptAll()`, `rejectAll()`, and `save()` resolve to a `SaveResult`. Check `result.ok`; a transport failure keeps the local receipt and queues a retry. The prebuilt preferences remain open on failure.
|
|
374
|
+
* `ready()` waits for successful initialization. Listen for `error` when a transport cannot initialize.
|
|
375
|
+
* `script.bundles` supplies filesystem paths. The backend routes currently depend on Node filesystem APIs; passing a path does not add support for runtimes without a filesystem.
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Adobe Analytics
|
|
3
|
+
description: Configure Adobe Analytics with c15t v3, understand measurement
|
|
4
|
+
permission and verify loading and revocation.
|
|
5
|
+
group: integrations
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Configure Adobe Analytics
|
|
9
|
+
|
|
10
|
+
Copy the full HTTPS embed URL for your Adobe Data Collection property environment. Replace the example URL; Adobe does not have a shared Analytics loader that only needs an account ID.
|
|
11
|
+
|
|
12
|
+
| Package manager | Command |
|
|
13
|
+
| :-------------- | :-------------------------- |
|
|
14
|
+
| npm | `npm install @c15t/scripts` |
|
|
15
|
+
| pnpm | `pnpm add @c15t/scripts` |
|
|
16
|
+
| yarn | `yarn add @c15t/scripts` |
|
|
17
|
+
| bun | `bun add @c15t/scripts` |
|
|
18
|
+
|
|
19
|
+
```ts title="src/consent-scripts.ts"
|
|
20
|
+
import { adobeAnalytics } from '@c15t/scripts/adobe-analytics';
|
|
21
|
+
|
|
22
|
+
export const scripts = [adobeAnalytics({
|
|
23
|
+
scriptUrl: 'https://assets.adobedtm.com/YOUR_ORG/YOUR_PROPERTY/launch-production.min.js',
|
|
24
|
+
})];
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Register the scripts
|
|
28
|
+
|
|
29
|
+
Complete your [framework quickstart](https://c15t.com/docs/frameworks) first. Keep its Inth
|
|
30
|
+
endpoint, policy, styles and consent UI. Remove the vendor's original script,
|
|
31
|
+
SDK initializer or tag-manager entry so c15t owns loading once.
|
|
32
|
+
|
|
33
|
+
The `scripts` export in `src/consent-scripts.ts` is a configuration, not an
|
|
34
|
+
initializer. Add it to your existing consent owner using the registration point
|
|
35
|
+
below. These are partial edits to that owner, not additional providers.
|
|
36
|
+
|
|
37
|
+
**Next.js**
|
|
38
|
+
|
|
39
|
+
Import the configuration into the client boundary from your router guide:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { ConsentRoot } from 'c15t/next';
|
|
43
|
+
import { scripts } from './consent-scripts';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Keep the server-resolved `state` and shared `consentConfig` from your
|
|
47
|
+
router guide. Its manifest, init and save URLs stay in effect. Add
|
|
48
|
+
`scripts` as a top-level prop on the existing root:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<ConsentRoot state={state} config={consentConfig} scripts={scripts}>
|
|
52
|
+
{children}
|
|
53
|
+
</ConsentRoot>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For a Pages Router or static-export setup using `ConsentProvider`, add
|
|
57
|
+
`scripts` to its existing `options` instead. Keep the router-specific setup
|
|
58
|
+
from [Next.js script loading](https://c15t.com/docs/frameworks/next/script-loader).
|
|
59
|
+
|
|
60
|
+
**TanStack Start**
|
|
61
|
+
|
|
62
|
+
In your existing root route component, import the scripts alongside
|
|
63
|
+
`ConsentRoot`. Keep the server loader from the [TanStack Start quickstart](https://c15t.com/docs/frameworks/tanstack-start/quickstart).
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { Outlet } from '@tanstack/react-router';
|
|
67
|
+
import { ConsentRoot } from 'c15t/tanstack-start';
|
|
68
|
+
import { scripts } from '../consent-scripts';
|
|
69
|
+
|
|
70
|
+
function Root() {
|
|
71
|
+
const state = Route.useLoaderData();
|
|
72
|
+
return (
|
|
73
|
+
<ConsentRoot state={state} backendURL={backendURL} initRoute={false} scripts={scripts}>
|
|
74
|
+
<Outlet />
|
|
75
|
+
{/* Keep your consent banner, dialog and preferences link here. */}
|
|
76
|
+
</ConsentRoot>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This edits the existing route. `Route` and `backendURL` come from its setup;
|
|
82
|
+
keep the document shell and head components if they are part of your root.
|
|
83
|
+
`initRoute={false}` keeps the quickstart's direct-backend initialization.
|
|
84
|
+
If your app mounts a consent server route, retain its existing `initRoute`
|
|
85
|
+
instead. Do not return script callbacks from a server function or route loader.
|
|
86
|
+
|
|
87
|
+
**React**
|
|
88
|
+
|
|
89
|
+
Import the scripts into your existing provider component:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { ConsentProvider } from 'c15t/react';
|
|
93
|
+
import { scripts } from './consent-scripts';
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Keep the existing options and add `scripts`:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<ConsentProvider options={{ ...consentOptions, scripts }}>
|
|
100
|
+
{children}
|
|
101
|
+
</ConsentProvider>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Here `consentOptions` is your existing configuration, including
|
|
105
|
+
`mode: hosted({ url: backendURL })`. Keep the banner, dialog and preferences
|
|
106
|
+
link inside the provider. See [React script loading](https://c15t.com/docs/frameworks/react/script-loader).
|
|
107
|
+
|
|
108
|
+
**Nuxt**
|
|
109
|
+
|
|
110
|
+
Attach one loader from the root `app.vue`, after the Nuxt module has
|
|
111
|
+
started its browser runtime. This keeps vendor callbacks in application code rather
|
|
112
|
+
than serialized `nuxt.config.ts` runtime configuration.
|
|
113
|
+
|
|
114
|
+
```vue title="app/app.vue"
|
|
115
|
+
<script setup lang="ts">
|
|
116
|
+
import { onUnmounted } from 'vue';
|
|
117
|
+
import { createScriptLoader } from 'c15t/modules/script-loader';
|
|
118
|
+
import { scripts } from '../src/consent-scripts';
|
|
119
|
+
|
|
120
|
+
const nuxtApp = useNuxtApp();
|
|
121
|
+
const kernel = useConsentKernel();
|
|
122
|
+
let loader: ReturnType<typeof createScriptLoader> | undefined;
|
|
123
|
+
|
|
124
|
+
const removeMountedHook = nuxtApp.hook('app:mounted', () => {
|
|
125
|
+
loader = createScriptLoader({ kernel, scripts });
|
|
126
|
+
});
|
|
127
|
+
onUnmounted(() => {
|
|
128
|
+
removeMountedHook();
|
|
129
|
+
loader?.dispose();
|
|
130
|
+
});
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<template>
|
|
134
|
+
<ConsentRoot />
|
|
135
|
+
<NuxtPage />
|
|
136
|
+
</template>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Merge the setup code into your root and retain its footer and preferences
|
|
140
|
+
link. `useConsentKernel` is auto-imported by the c15t Nuxt module. Adjust the
|
|
141
|
+
relative script import if your `app.vue` is at the project root. This loader
|
|
142
|
+
waits until the module has applied browser persistence and privacy signals,
|
|
143
|
+
then reads the current snapshot and observes future changes. Do not also register these scripts
|
|
144
|
+
in another loader. See the [Nuxt quickstart](https://c15t.com/docs/frameworks/nuxt/quickstart).
|
|
145
|
+
|
|
146
|
+
**Vue**
|
|
147
|
+
|
|
148
|
+
Use the kernel already provided by the Vue plugin. Merge this setup into
|
|
149
|
+
`App.vue`, whose lifetime covers the application:
|
|
150
|
+
|
|
151
|
+
```vue title="src/App.vue"
|
|
152
|
+
<script setup lang="ts">
|
|
153
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
154
|
+
import { createScriptLoader } from 'c15t/modules/script-loader';
|
|
155
|
+
import { useConsentKernel } from 'c15t/vue/vue-plugin';
|
|
156
|
+
import ConsentRoot from 'c15t/vue/consent-root';
|
|
157
|
+
import { scripts } from './consent-scripts';
|
|
158
|
+
|
|
159
|
+
const kernel = useConsentKernel();
|
|
160
|
+
let loader: ReturnType<typeof createScriptLoader> | undefined;
|
|
161
|
+
|
|
162
|
+
onMounted(() => {
|
|
163
|
+
loader = createScriptLoader({ kernel, scripts });
|
|
164
|
+
});
|
|
165
|
+
onUnmounted(() => loader?.dispose());
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
<template>
|
|
169
|
+
<ConsentRoot />
|
|
170
|
+
<main>Your application</main>
|
|
171
|
+
</template>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Keep your existing page content and preferences link. The plugin still owns
|
|
175
|
+
the kernel and persistence; this component owns only the vendor loader.
|
|
176
|
+
Do not register the same scripts in plugin configuration as well. See the
|
|
177
|
+
[Vue quickstart](https://c15t.com/docs/frameworks/vue/quickstart).
|
|
178
|
+
|
|
179
|
+
**Astro**
|
|
180
|
+
|
|
181
|
+
Point the existing Astro integration at a client module. Keep its `mode`,
|
|
182
|
+
`ui` and framework integration from the [Astro quickstart](https://c15t.com/docs/frameworks/astro/quickstart).
|
|
183
|
+
Import `fileURLToPath` in your Astro configuration:
|
|
184
|
+
|
|
185
|
+
```js title="astro.config.mjs"
|
|
186
|
+
import { fileURLToPath } from 'node:url';
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Add this option to the existing `c15t({ ... })` call. Resolve the path from
|
|
190
|
+
the configuration file because Astro injects the import into a virtual module:
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
clientEntrypoint: fileURLToPath(new URL('./src/c15t.client.ts', import.meta.url)),
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Export the scripts from that module:
|
|
197
|
+
|
|
198
|
+
```ts title="src/c15t.client.ts"
|
|
199
|
+
import type { C15tClientOptionsExtension } from '@c15t/astro';
|
|
200
|
+
import { scripts } from './consent-scripts';
|
|
201
|
+
|
|
202
|
+
export default { scripts } satisfies C15tClientOptionsExtension;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The integration passes this extension to its shared browser runtime. Vendor
|
|
206
|
+
helpers contain callbacks, so do not put them in the serialized `scripts`
|
|
207
|
+
option in `astro.config.mjs`. Keep one runtime across consent islands and
|
|
208
|
+
`ClientRouter` navigation.
|
|
209
|
+
|
|
210
|
+
**Svelte**
|
|
211
|
+
|
|
212
|
+
Import the scripts in the component that owns your existing provider and
|
|
213
|
+
pass them as a top-level prop:
|
|
214
|
+
|
|
215
|
+
```svelte title="src/App.svelte"
|
|
216
|
+
<script lang="ts">
|
|
217
|
+
import { ConsentManagerProvider, hosted } from '@c15t/svelte';
|
|
218
|
+
import { scripts } from './consent-scripts';
|
|
219
|
+
|
|
220
|
+
const backendURL = import.meta.env.VITE_C15T_BACKEND_URL;
|
|
221
|
+
if (!backendURL) throw new Error('Set VITE_C15T_BACKEND_URL');
|
|
222
|
+
const mode = hosted({ url: backendURL });
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<ConsentManagerProvider {mode} {scripts}>
|
|
226
|
+
<!-- Keep your application, consent UI and preferences link here. -->
|
|
227
|
+
</ConsentManagerProvider>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Retain the styles and consent UI from the [Svelte quickstart](https://c15t.com/docs/frameworks/svelte/quickstart).
|
|
231
|
+
The provider owns the loader and disposes it on unmount.
|
|
232
|
+
|
|
233
|
+
**SvelteKit**
|
|
234
|
+
|
|
235
|
+
Add the scripts to the existing root layout provider. Keep the server load
|
|
236
|
+
and its serializable prefetch data from the [SvelteKit quickstart](https://c15t.com/docs/frameworks/sveltekit/quickstart).
|
|
237
|
+
|
|
238
|
+
```svelte title="src/routes/+layout.svelte"
|
|
239
|
+
<script lang="ts">
|
|
240
|
+
import { ConsentManagerProvider, hosted } from '@c15t/svelte';
|
|
241
|
+
import { scripts } from '../consent-scripts';
|
|
242
|
+
|
|
243
|
+
let { children, data } = $props();
|
|
244
|
+
const mode = hosted({ url: data.backendURL });
|
|
245
|
+
</script>
|
|
246
|
+
|
|
247
|
+
<ConsentManagerProvider {mode} {scripts} prefetch={data.prefetch}>
|
|
248
|
+
{@render children()}
|
|
249
|
+
<!-- Keep your consent UI and preferences link here. -->
|
|
250
|
+
</ConsentManagerProvider>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Import vendor helpers in the layout component, not in `+layout.server.ts`.
|
|
254
|
+
For static hosting, keep your browser-only `mode` setup and omit request
|
|
255
|
+
prefetch; the `scripts` prop stays the same. If you pass an externally owned
|
|
256
|
+
`runtime` to the provider, register scripts when creating that runtime instead.
|
|
257
|
+
|
|
258
|
+
**JavaScript**
|
|
259
|
+
|
|
260
|
+
Attach the loader to your existing kernel before calling
|
|
261
|
+
`kernel.commands.init()`:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { createScriptLoader } from 'c15t/modules/script-loader';
|
|
265
|
+
import { scripts } from './consent-scripts';
|
|
266
|
+
|
|
267
|
+
const loader = createScriptLoader({ kernel, scripts });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Call `loader.dispose()` when that application instance is destroyed.
|
|
271
|
+
`kernel` is the hosted kernel from your quickstart. A provider-owned kernel
|
|
272
|
+
already has a loader; do not attach a second one. See
|
|
273
|
+
[JavaScript script loading](../frameworks/javascript/script-loader.md).
|
|
274
|
+
|
|
275
|
+
## Options
|
|
276
|
+
|
|
277
|
+
| Option | Behavior |
|
|
278
|
+
| -------------------- | ------------------------------------------------------------------------ |
|
|
279
|
+
| `scriptUrl` | Required full HTTPS property embed URL. Invalid or non-HTTPS URLs throw. |
|
|
280
|
+
| `async` | Defaults to `true`. |
|
|
281
|
+
| `seedAdobeDataLayer` | Defaults to `true`. Creates `window.adobeDataLayer` only if absent. |
|
|
282
|
+
|
|
283
|
+
## Consent behavior
|
|
284
|
+
|
|
285
|
+
Measurement permission gates the property loader. Rules and extensions inside the Adobe property may load other services. Review those destinations and their purposes too; this helper does not assign separate consent categories to individual Adobe rules.
|
|
286
|
+
|
|
287
|
+
## Verify the integration
|
|
288
|
+
|
|
289
|
+
Confirm the environment URL is correct and inspect requests from every enabled extension. Check that revoking permission stops application data-layer events; the helper does not call an Adobe shutdown API.
|
|
290
|
+
|
|
291
|
+
Use a fresh session with an opt-in policy to check initial denial. Then grant
|
|
292
|
+
`measurement`, revoke it, and reload. Inspect both network requests and future
|
|
293
|
+
application events. Removing a script cannot undo code or requests that already
|
|
294
|
+
ran. Follow the [consent verification guide](https://c15t.com/docs/guides/verify-consent).
|